"""Пересборка манифеста по требованию.

Слепая приёмка показала дыру: испорченный вручную файл переживал `chekan status`,
`chekan req list` и `chekan next` — read-only команды состояние не пишут, а значит и
проекцию не чинят. Испорченный файл мог уехать в коммит незамеченным.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from test_core import repo, run  # noqa: E402,F401 — общие фикстуры

from chekan import manifest  # noqa: E402
from chekan.store import Run  # noqa: E402

ROOT = Path(__file__).resolve().parent.parent


def ceh(root: Path, *args: str) -> subprocess.CompletedProcess:
    return subprocess.run(
        [sys.executable, "-m", "chekan", *args],
        cwd=root, capture_output=True, text=True,
        env={"PYTHONPATH": str(ROOT), "PATH": "/usr/bin:/bin", "HOME": str(root)},
    )


def test_rebuild_repairs_a_hand_broken_file(run: Run):  # noqa: F811
    run.add_requirement("R01", "цитата", note="причина")
    path = run.dir / "manifest.md"
    path.write_text("человек всё переписал\n", encoding="utf-8")

    proc = ceh(run.root, "manifest")
    assert proc.returncode == 0, proc.stderr
    assert "R01" in path.read_text(encoding="utf-8")


def test_rebuild_says_when_the_file_was_stale(run: Run):  # noqa: F811
    run.add_requirement("R01", "цитата", note="причина")
    (run.dir / "manifest.md").write_text("мусор\n", encoding="utf-8")
    assert "отстав" in ceh(run.root, "manifest").stdout


def test_rebuild_is_quiet_when_nothing_drifted(run: Run):  # noqa: F811
    run.add_requirement("R01", "цитата", note="причина")
    assert "совпадал" in ceh(run.root, "manifest").stdout


def test_rebuild_recreates_a_deleted_file(run: Run):  # noqa: F811
    run.add_requirement("R01", "цитата", note="причина")
    path = run.dir / "manifest.md"
    path.unlink()
    ceh(run.root, "manifest")
    assert path.is_file() and "R01" in path.read_text(encoding="utf-8")


def test_readonly_commands_leave_the_file_alone(run: Run):  # noqa: F811
    """Ровно та дыра, ради которой команда и заведена: read-only не чинит."""
    run.add_requirement("R01", "цитата", note="причина")
    path = run.dir / "manifest.md"
    path.write_text("испорчено\n", encoding="utf-8")

    for cmd in ("status", "next"):
        ceh(run.root, cmd)
    assert path.read_text(encoding="utf-8") == "испорчено\n"

    ceh(run.root, "manifest")
    assert "R01" in path.read_text(encoding="utf-8")


def test_rebuild_matches_the_renderer(run: Run):  # noqa: F811
    """Команда обязана писать ровно то же, что чистая функция, — она проекция."""
    run.add_requirement("R01", "цитата", note="причина")
    ceh(run.root, "manifest")
    on_disk = (run.dir / "manifest.md").read_text(encoding="utf-8")
    assert on_disk == manifest.render(Run(run.root, run.slug).state)


def test_basis_reaches_the_file(run: Run):  # noqa: F811
    """Заметка — это «почему требование существует».

    Вторая слепая приёмка нашла, что она оставалась только в state.json —
    ровно там, откуда заказ просил её вытащить.
    """
    run.add_requirement("R01", "цитата из брифа", note="потому что иначе разъедется")
    ceh(run.root, "manifest")
    text = (run.dir / "manifest.md").read_text(encoding="utf-8")
    assert "потому что иначе разъедется" in text


def test_report_heading_is_one_line(run: Run):  # noqa: F811
    """Заголовок отчёта не должен быть простынёй — как в панели и манифесте."""
    from chekan.report import build

    st = run.state
    st["title"] = ("Очень длинный бриф, который занимает несколько строк и содержит "
                   "точку в середине. А после неё продолжается ещё столько же текста.")
    run.save(st)
    head = build(run).splitlines()[0]
    assert head.startswith("# ") and len(head) < 90, head
    assert st["title"] in build(run)


def test_report_closes_the_last_phase(run: Run):  # noqa: F811
    """Завершённый прогон с фазой «идёт» — два противоречащих факта на панели."""
    run.enter_phase("accept")
    ceh(run.root, "report")
    st = Run(run.root, run.slug).state
    assert st["finished_at"]
    assert not [n for n, v in st["stages"].items() if v["status"] == "active"]
