{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-23-16-00",
   "metadata": {},
   "source": [
    "# 23-16 · Cofnij i konflikt na przywróceniu\n",
    "\n",
    "Praktyka do sekcji [„Cofnij ostatnią operację” `safesort`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "setup-23-16",
   "metadata": {},
   "source": [
    "## Reproducible local environment\n",
    "\n",
    "```bash\n",
    "git clone https://github.com/Cartesian-School/safesort.git\n",
    "cd safesort\n",
    "python3.14 -m venv .venv\n",
    "source .venv/bin/activate\n",
    "# Windows PowerShell: .venv\\Scripts\\Activate.ps1\n",
    "python -m pip install -U pip\n",
    "python -m pip install -e \".[dev]\"\n",
    "python -m pip install jupyter ipykernel\n",
    "python -m ipykernel install --user --name safesort-py314 --display-name \"SafeSort Python 3.14\"\n",
    "jupyter lab\n",
    "```\n",
    "\n",
    "Select the **SafeSort Python 3.14** kernel. The diagnostic cell below must\n",
    "point into this `.venv` and the cloned `src/safesort` tree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "diagnostic-23-16",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import safesort\n",
    "\n",
    "print(sys.executable)\n",
    "print(safesort.__file__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-03",
   "metadata": {},
   "source": [
    "## Cel\n",
    "\n",
    "Stosuj plan, anuluj go tym `safesort.manifest.undo()` i upewnić się, że w przypadku konfliktu (na początkowym miejscu już coś jest) anulowanie odmawia nadpisania."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-04",
   "metadata": {},
   "source": [
    "## Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-16-05",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tempfile\n",
    "from pathlib import Path\n",
    "\n",
    "from safesort.config import Config\n",
    "from safesort.scanner import scan\n",
    "from safesort.planner import build_plan\n",
    "from safesort.executor import apply_plan\n",
    "from safesort.manifest import write_manifest, read_manifest, find_latest_manifest, undo\n",
    "\n",
    "tmpdir = tempfile.TemporaryDirectory()\n",
    "koren = Path(tmpdir.name)\n",
    "\n",
    "(koren / \"otchet.pdf\").write_text(\"отчёт\", encoding=\"utf-8\")\n",
    "(koren / \"zametka.txt\").write_text(\"заметка\", encoding=\"utf-8\")\n",
    "\n",
    "nastrojki = Config()\n",
    "fajly = scan(koren, nastrojki)\n",
    "plan = build_plan(fajly, koren, nastrojki)\n",
    "rezultaty = apply_plan(plan)\n",
    "_manifest_obj, put_k_manifestu = write_manifest(koren, rezultaty)\n",
    "\n",
    "print(\"Перемещено файлов:\", sum(1 for r in rezultaty if r.completed))\n",
    "print(\"Манифест записан в:\", put_k_manifestu)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-06",
   "metadata": {},
   "source": [
    "## Sprawdzenie wyniku — pliki faktycznie zostały przeniesione"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-16-07",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert not (koren / \"otchet.pdf\").exists()\n",
    "assert not (koren / \"zametka.txt\").exists()\n",
    "assert (koren / \"Sorted\" / \"documents\" / \"otchet.pdf\").exists()\n",
    "assert put_k_manifestu.exists()\n",
    "print(\"Верно: оба файла оказались в Sorted/documents/, манифест записан на диск.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-08",
   "metadata": {},
   "source": [
    "## Eksperyment – undo przywraca pliki"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-16-09",
   "metadata": {},
   "outputs": [],
   "source": [
    "najdennyj_manifest = find_latest_manifest(koren)\n",
    "manifest_dlya_otmeny = read_manifest(najdennyj_manifest)\n",
    "\n",
    "rezultat_otmeny = undo(manifest_dlya_otmeny)\n",
    "\n",
    "assert (koren / \"otchet.pdf\").exists()\n",
    "assert (koren / \"zametka.txt\").exists()\n",
    "assert rezultat_otmeny.conflicts == ()\n",
    "print(\"Верно: оба файла вернулись на исходное место, конфликтов не было.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-10",
   "metadata": {},
   "source": [
    "## Starter\n",
    "\n",
    "Wypełnij zaznaczone miejsce. Niezmieniony starter nie przechodzi tests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "task-23-16",
   "metadata": {
    "tags": [
     "exercise",
     "starter"
    ]
   },
   "outputs": [],
   "source": [
    "def proverit_konflikt_undo(root: Path):\n",
    "    # TODO: apply one file, write a replacement at the source, then call undo.\n",
    "    raise NotImplementedError\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-12",
   "metadata": {},
   "source": [
    "## Task\n",
    "\n",
    "Napisz skrypt, w którym nowy plik pojawi się na oryginalnej ścieżce po apply i undo zgłasza jeden konflikt i nie nadpisuje go."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-13",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "Run After task cell: jest podstawowy przykład i przynajmniej jeden skrajny przypadek."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tests-23-16",
   "metadata": {
    "tags": [
     "exercise-tests"
    ]
   },
   "outputs": [],
   "source": [
    "with tempfile.TemporaryDirectory() as tmp:\n",
    "    test_root = Path(tmp)\n",
    "    konflikt, content = proverit_konflikt_undo(test_root)\n",
    "assert konflikt is True\n",
    "assert content == \"новый файл\"\n",
    "print(\"Tests passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-15",
   "metadata": {},
   "source": [
    "## Hint\n",
    "\n",
    "Oszczędź manifest później `apply_plan`, następnie utwórz nowy source do `undo`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-16-16",
   "metadata": {},
   "source": [
    "## Solution\n",
    "\n",
    "Pokaż rozwiązanie po własnej próbie</summary>\n",
    "\n",
    "```python\n",
    "def proverit_konflikt_undo(root: Path):\n",
    "    source = root / \"otchet.pdf\"\n",
    "    source.write_text(\"оригинал\", encoding=\"utf-8\")\n",
    "    config = Config()\n",
    "    plan = build_plan(scan(root, config), root, config)\n",
    "    moves = apply_plan(plan)\n",
    "    manifest, _ = write_manifest(root, moves)\n",
    "\n",
    "    source.write_text(\"новый файл\", encoding=\"utf-8\")\n",
    "    result = undo(manifest)\n",
    "    return len(result.conflicts) == 1, source.read_text(encoding=\"utf-8\")\n",
    "```\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
