"""Почему прогон стоит.

`chekan why` отвечает в терминале на один вопрос: что мешает идти дальше. Претензии
разбирает `gates.check` — второго разбора здесь нет и быть не должно, иначе два
текста про одно и то же разойдутся в первую же неделю. Новое тут ровно две вещи:
вход в терминале и связность претензии с починкой — какой командой она чинится и
чьими руками, оркестратора или исполнителя.

Разница между руками не косметическая. Оркестратор правит `.chekan/` и раскладку
работы: требования, таски, зоны, вердикты слепых читателей. Исполнитель пишет код
проекта и добивается зелёного прогона. Претензия, отданная не тем рукам, стоит
целого круга переделки.
"""

from __future__ import annotations

import re
from typing import NamedTuple

from . import gates, phases
from .store import Run

ORCHESTRATOR = "оркестратор"
EXECUTOR = "исполнитель"

_TASK_ID = re.compile(r"таск[а-я]*\s+(\S+)")
_REQ_ID = re.compile(r"\b(R\d+)\b")
# Многие претензии уже несут готовую команду починки в обратных кавычках — она
# точнее любой выведенной по шаблону, потому что написана рядом с самой проверкой.
_EMBEDDED = re.compile(r"`([^`]*\bceh\s[^`]*)`")


class Claim(NamedTuple):
    gate: str
    problem: str
    fix: str
    who: str


def _embedded(problem: str) -> str:
    hit = _EMBEDDED.search(problem)
    return hit.group(1).strip() if hit else ""


def _task_id(problem: str) -> str:
    hit = _TASK_ID.search(problem)
    return hit.group(1).strip(".,:;()«»") if hit else ""


def _req_id(problem: str) -> str:
    hit = _REQ_ID.search(problem)
    return hit.group(1) if hit else ""


def _verify(run: Run, tid: str) -> str:
    task = run.task(tid) if tid else None
    return (task or {}).get("verify") or "<команда проверки>"


def route(run: Run, gate: str, problem: str) -> tuple[str, str]:
    """Претензия → команда починки и тот, кто её выполняет.

    Текст претензии не переписывается: он приходит из гейта и печатается дословно.
    """
    embedded = _embedded(problem)
    tid = _task_id(problem)
    rid = _req_id(problem)

    if "манифест пуст" in problem:
        return 'chekan req add R01 --quote "<цитата из брифа>" --note "<почему>"', ORCHESTRATOR
    if "без записанной причины" in problem:
        return f"chekan req status {rid or '<R..>'} --value done", ORCHESTRATOR
    if "нет дословной цитаты" in problem:
        return f'chekan req add {rid or "<R..>"} --quote "<цитата из брифа>"', ORCHESTRATOR
    if "остался в статусе open" in problem:
        return f"chekan req status {rid or '<R..>'} --value done", ORCHESTRATOR
    if "не покрыто ни одним таском" in problem:
        return (
            f'chekan task add <NN> --title "<что делаем>" --requirements {rid or "<R..>"}',
            ORCHESTRATOR,
        )
    if "не привязан ни к одному требованию" in problem:
        return f"chekan task add {tid or '<NN>'} --requirements <R..> (перезавести с привязкой)", ORCHESTRATOR
    if "зависит от несуществующего" in problem:
        return f'chekan task add <NN> --title "<недостающий таск>" --requirements <R..>', ORCHESTRATOR
    if "делят зону" in problem:
        return f"chekan task set {tid or '<NN>'} --zone <непересекающаяся зона>", ORCHESTRATOR
    if "затих без движения" in problem:
        return f"chekan task reset {tid or '<NN>'}", ORCHESTRATOR
    if "закрыт переиспользованным доказательством" in problem:
        return embedded or f'chekan run "{_verify(run, tid)}" --task {tid} --no-reuse', EXECUTOR
    if "без зелёного доказательства" in problem or "не закрыт (статус" in problem:
        return f'chekan run "{_verify(run, tid)}" --task {tid or "<NN>"}, затем chekan task done {tid or "<NN>"}', EXECUTOR
    if "приёмка красная" in problem:
        return embedded or "chekan accept", EXECUTOR
    if "приёмка не проводилась" in problem:
        return "chekan accept", ORCHESTRATOR
    if "ничего не прогнала" in problem or "живой сценарий не подтверждён" in problem:
        return embedded or 'chekan run "<сборка или тесты>" --kind accept', ORCHESTRATOR
    if "нет вердикта слепого приёмщика" in problem:
        return "chekan prompt blind, затем chekan gate G4 --blind <файл>", ORCHESTRATOR
    if "слепой приёмщик не нашёл" in problem:
        return f'chekan task add <NN> --title "<чего не хватило>" --requirements <R..>', ORCHESTRATOR
    if "нет отчёта независимого читателя" in problem or "читатель нашёл пропуск" in problem:
        return embedded or "chekan prompt reader, затем chekan gate G2 --coverage <файл>", ORCHESTRATOR
    if embedded:
        return embedded, ORCHESTRATOR
    return f"chekan gate {gate} — разберите претензию дословно", ORCHESTRATOR


def claims(run: Run) -> list[Claim]:
    """Все претензии красных гейтов с адресом починки. Пусто — стоять не на чем."""
    found: list[Claim] = []
    current, _blockers = phases.resolve(run)
    active_gate = phases.GATE_AFTER.get(current) or "G3"
    for gate in phases.applicable_gates(run):
        for problem in gates.check(run, gate):
            fix, who = route(run, gate, problem)
            found.append(Claim(gate=gate, problem=problem, fix=fix, who=who))
    for problem in gates.stale_tasks(run):
        if not any(c.problem == problem for c in found):
            fix, who = route(run, active_gate, problem)
            found.append(Claim(gate=active_gate, problem=problem, fix=fix, who=who))
    return found


def _(text: str) -> str:
    try:
        from .i18n import translate
        return translate(text)
    except Exception:
        return text


def next_step(run: Run) -> str:
    """Следующий шаг фазы — тот же, что печатает `chekan next`, одной строкой."""
    st = run.state
    if st.get("finished_at"):
        return _("Следующий шаг: прогон закрыт, отчёт рядом с состоянием. Новая работа — `chekan init`")
    current, blockers = phases.resolve(run)
    plan = phases.active_phases(run)
    if current == "build" and blockers:
        first = next((t for t in run.ready_tasks()), None) or next(
            (t for t in st["tasks"] if t["status"] != "done"), None
        )
        if first is not None:
            return (
                f"{_('Следующий шаг: доведите таск')} {first['id']} {_('до зелёного — ')}"
                f"`chekan task start {first['id']} --model <{_('модель')}>`"
            )
    idx = plan.index(current)
    if idx + 1 < len(plan):
        nxt_phase = _(phases.PHASE_RU[plan[idx + 1]])
        return f"{_('Следующий шаг:')} `chekan advance` → {nxt_phase}"
    return _("Следующий шаг: закрыть прогон — `chekan report`")


def render(run: Run) -> list[str]:
    st = run.state
    current, _blockers = phases.resolve(run)
    found = claims(run)
    phase_label = _(phases.PHASE_RU[current])
    lines = [f"{_('Прогон:')} {st['title']}", f"{_('Фаза:')} {phase_label}", ""]

    if not found:
        passed = ", ".join(f"{g} ({_(gates.GATE_TITLE[g])})" for g in phases.applicable_gates(run))
        lines.append(_("Красных гейтов нет: стоять не на чем."))
        if passed:
            lines.append(f"{_('Пройдены:')} {passed}")
        lines += ["", next_step(run)]
        return lines

    lines.append(f"{_('Стоим на претензиях гейтов:')} {len(found)}.")
    for gate in [g for g in phases.applicable_gates(run) if any(c.gate == g for c in found)]:
        lines += ["", f"{gate} ({_(gates.GATE_TITLE[gate])}): {_('НЕ ПРОЙДЕН')}"]
        for claim in [c for c in found if c.gate == gate]:
            lines += [
                f"  · {_(claim.problem)}",
                f"      {_('чинит:')}   {_(claim.who)}",
                f"      {_('команда:')} {claim.fix}",
            ]
    lines += ["", _("Претензия гейта — список работы, а не остановка. Закрыли — `chekan gate`.")]
    return lines
