"""Human-first implementation-planning view and approval state."""
from __future__ import annotations

import re
from dataclasses import dataclass

from ..common import evidence_index
from ..models import HumanReportView, VisualEdge, VisualNode
from ..visualizations import stage_map_figure


@dataclass(frozen=True)
class PlanApprovalState:
    option_names: tuple[str, ...]
    recommended_option: str
    disabled_reason: str
    blocker_ids: tuple[str, ...]
    show_option_selector: bool = True
    reentry_command: str = ""


def resolve_recommended_option(rec_name: str, names: tuple[str, ...]) -> str:
    if rec_name in names:
        return rec_name
    matches = [
        name
        for name in names
        if rec_name and (name.startswith(rec_name) or rec_name.startswith(name))
    ]
    return matches[0] if len(matches) == 1 else names[0]


def plan_approval_state(data: dict) -> PlanApprovalState | None:
    planning = data.get("implementationPlanning")
    if not isinstance(planning, dict):
        return None
    blockers = tuple(
        row["id"]
        for row in data.get("clarificationItems", [])
        if row.get("blocks") == "approval"
        and row.get("status") in {"open", "answered"}
    )
    reason = f"{len(blockers)} approval blocker(s) unresolved" if blockers else ""
    if planning.get("planningContract") == "selected-direction":
        if planning.get("outcome") == "direction-invalidated" and not reason:
            reason = "selected direction invalidated"
        task_key = (data.get("header") or {}).get("taskKey", "")
        reentry_command = (
            f"/okstra-run task-key={task_key} "
            "task-type=implementation-option-selection"
            if planning.get("outcome") == "direction-invalidated" and task_key
            else ""
        )
        return PlanApprovalState(
            (), "", reason, blockers, False, reentry_command
        )
    names = tuple(
        row["name"]
        for row in planning.get("optionCandidates", [])
        if isinstance(row, dict) and row.get("name")
    )
    if not names:
        return None
    recommended = planning.get("recommendedOption", {}).get("name", "")
    return PlanApprovalState(
        names,
        resolve_recommended_option(recommended, names),
        reason,
        blockers,
    )


def _stage_figure(planning: dict):
    nodes = tuple(
        VisualNode(
            f"stage-{row['stage']}",
            f"Stage {row['stage']} · {row['title']}",
            "stage",
            "planned",
            row["exitContractSummary"],
        )
        for row in planning["stageMap"]
    )
    edges = tuple(
        VisualEdge(f"stage-{dependency}", f"stage-{row['stage']}", "precedes", "dependency")
        for row in planning["stageMap"]
        for dependency in re.findall(r"\d+", row["dependsOn"])
    )
    return stage_map_figure(nodes=nodes, edges=edges, title="Implementation stage dependencies")


# 승인자가 보지 않는 구현자·사고 대응 표. 계획 본문 검증은 판정과 근거가
# 승인 판단이라 여기 두지 않는다.
_OMITTED_FIELDS = (
    "validationChecklist",
    "crossProjectDependencies",
    "dependencyMigrationRisk",
    "rollbackStrategy",
)


def build_implementation_planning_view(data: dict) -> HumanReportView:
    planning = data["implementationPlanning"]
    figure = _stage_figure(planning) if planning.get("stageMap") else None
    approval = plan_approval_state(data)
    activities = tuple(
        row for row in data.get("agentActivity", []) if isinstance(row, dict)
    )
    decision_cards = tuple(
        row
        for row in data.get("clarificationItems", [])
        if isinstance(row, dict)
        and row.get("blocks") == "approval"
        and isinstance(row.get("approvalContext"), dict)
    )
    context = {
        "humanSummary": data["humanSummary"],
        "planning": planning,
        "narrative": planning.get("userNarrative", {}),
        "stageFigure": figure,
        "approval": approval,
        "openDecisions": [
            row
            for row in data.get("clarificationItems", [])
            if isinstance(row, dict)
            and row.get("blocks") == "approval"
            and row.get("status") in {"open", "answered"}
        ],
        "agentActivities": activities,
        "activityById": {
            row["activityId"]: row for row in activities if row.get("activityId")
        },
        "decisionCards": decision_cards,
        "evidenceIndex": evidence_index(data, _OMITTED_FIELDS),
    }
    return HumanReportView(
        "implementation-planning",
        "html/tasks/implementation-planning.template.html",
        context,
        (figure,) if figure is not None else (),
        _OMITTED_FIELDS,
    )
