"""`chekan why` — почему прогон стоит (R08).

Рычаги порчи, каждый обязан давать красное:

1. Заменить разбор общим текстом «гейт не пройден» — покраснеет
   `test_claims_are_the_gate_verdict_word_for_word`.
2. На зелёном прогоне не назвать следующий шаг — покраснеет
   `test_all_green_says_so_and_names_the_next_step`.
3. Не завести команду в разборе аргументов (она же список разговорного режима) —
   покраснеет `test_why_is_offered_in_the_conversational_mode`.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

import pytest

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

from chekan import gates, phases, shell, why  # noqa: E402
from chekan.__main__ import main  # noqa: E402
from chekan.store import Run  # noqa: E402


@pytest.fixture
def repo(tmp_path: Path) -> Path:
    subprocess.run(["git", "init", "-q"], cwd=tmp_path, check=True)
    subprocess.run(["git", "config", "user.email", "t@t"], cwd=tmp_path, check=True)
    subprocess.run(["git", "config", "user.name", "t"], cwd=tmp_path, check=True)
    (tmp_path / "app.py").write_text("print('ok')\n", encoding="utf-8")
    subprocess.run(["git", "add", "-A"], cwd=tmp_path, check=True)
    subprocess.run(["git", "commit", "-qm", "init"], cwd=tmp_path, check=True)
    return tmp_path


@pytest.fixture
def run(repo: Path) -> Run:
    run = Run.create(repo, "Тестовый прогон", "normal", "T1")
    run.add_requirement("R01", "делай раз", note="причина")
    run.add_task("01", "Раз", ["R01"], zone="app.py", verify="pytest -q")
    return run


def _green(run: Run) -> None:
    """Прогон без единой претензии: гейты T0 сведены к одному G4, и он закрыт."""
    st = run.state
    st["tier"] = "T0"
    st["requirements"] = []
    st["tasks"] = []
    st.setdefault("gates", {})["G4"] = {
        "acceptance": {"profile": "python", "cmd": "pytest -q", "exit": 0, "checks": []},
        "blind": {"done": ["всё на месте"], "missing": []},
    }
    run.save(st)


# --- R08.1: разбор берётся у гейта, а не сочиняется заново ---


def test_claims_are_the_gate_verdict_word_for_word(run: Run):
    """Претензии совпадают с `gates.check` знак в знак: второго разбора нет."""
    from_gates = [p for g in phases.applicable_gates(run) for p in gates.check(run, g)]
    assert from_gates, "прогон обязан стоять хоть на чём-то, иначе проверять нечего"
    assert [c.problem for c in why.claims(run)] == from_gates


def test_output_carries_every_claim_text(run: Run):
    printed = "\n".join(why.render(run))
    for problem in [p for g in phases.applicable_gates(run) for p in gates.check(run, g)]:
        assert problem in printed, f"претензия пропала из вывода: {problem}"
    assert "таск 01" in printed


def test_each_claim_names_a_fix_command_and_the_hands(run: Run):
    for claim in why.claims(run):
        assert claim.fix.strip(), f"претензия без команды починки: {claim.problem}"
        assert "chekan " in claim.fix, f"починка не командой инструмента: {claim.fix}"
        assert claim.who in (why.ORCHESTRATOR, why.EXECUTOR), claim.who


def test_unfinished_task_goes_to_the_executor_with_its_own_verify(run: Run):
    claim = next(c for c in why.claims(run) if "не закрыт (статус" in c.problem)
    assert claim.who == why.EXECUTOR
    assert "pytest -q" in claim.fix, "команда починки не берёт проверку самого таска"
    assert "--task 01" in claim.fix


def test_manifest_claims_go_to_the_orchestrator(repo: Path):
    run = Run.create(repo, "Без цитат", "normal", "T1")
    run.add_requirement("R01", "", note="")
    claim = next(c for c in why.claims(run) if "нет дословной цитаты" in c.problem)
    assert claim.who == why.ORCHESTRATOR
    assert claim.fix.startswith("chekan req add R01")


def test_reused_evidence_keeps_the_command_the_gate_itself_offered(run: Run):
    """У претензии про переиспользование команда починки уже написана гейтом."""
    problem = (
        "таск 01 закрыт переиспользованным доказательством (`pytest -q` засчитана по "
        'отпечатку, а не прогнана). Прогоните по-настоящему: chekan run "pytest -q" '
        "--task 01 --no-reuse"
    )
    fix, who = why.route(run, "G4", problem)
    assert who == why.EXECUTOR
    assert "--no-reuse" in fix


# --- R08.2: всё зелено — так и сказано, код 0, назван следующий шаг ---


def test_all_green_says_so_and_names_the_next_step(run: Run, repo: Path, monkeypatch, capsys):
    _green(run)
    assert why.claims(run) == []
    monkeypatch.chdir(repo)
    code = main(["why"])
    printed = capsys.readouterr().out
    assert code == 0
    assert "Красных гейтов нет" in printed
    assert "Следующий шаг:" in printed
    assert why.next_step(Run.load(repo)) in printed


def test_next_step_on_green_points_at_the_phase_that_follows(run: Run):
    _green(run)
    step = why.next_step(Run.load(run.root))
    assert "chekan advance" in step and phases.PHASE_RU["build"] in step


def test_red_run_returns_nonzero(run: Run, repo: Path, monkeypatch, capsys):
    monkeypatch.chdir(repo)
    code = main(["why"])
    printed = capsys.readouterr().out
    assert code == 1
    assert "НЕ ПРОЙДЕН" in printed
    assert "чинит:" in printed and "команда:" in printed


# --- R08 связность: команда есть в CLI и в разговорном режиме ---


def test_why_is_offered_in_the_conversational_mode():
    listed = {c.full: c.help for c in shell.commands()}
    assert "why" in listed, "команды нет в списке разговорного режима"
    assert listed["why"].strip(), "команда без пояснения"
    assert "\n" not in listed["why"]


def test_why_does_not_touch_the_state(run: Run, repo: Path, monkeypatch):
    """Диагностика только читает: вердикты в состояние пишет `chekan gate`."""
    before = (run.dir / "state.json").read_text(encoding="utf-8")
    monkeypatch.chdir(repo)
    main(["why"])
    assert (run.dir / "state.json").read_text(encoding="utf-8") == before
