"""Gate 6C worktree patch proposal and human approval support."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

from .ledger import stable_hash
from .patch_candidates import PatchCandidate
from .patch_execution_sandbox import SandboxPatchExecutionResult, SandboxPatchExecutor


PATCH_PROPOSAL_CONFIRMATION_TOKEN = "APPROVE_GATE6C_PATCH_PROPOSAL"


@dataclass(slots=True)
class WorktreePatchProposalReport:
    proposal_id: str
    candidate_id: str
    source_root: str
    proposal_worktree_root: str
    baseline_root: str
    proposed: bool
    proposal_verifier_passed: bool
    verifier_after_patch: bool
    patch_report: dict[str, Any]
    risk_diff: dict[str, Any]
    verifier_summary: dict[str, Any]
    execution_result: dict[str, Any]
    auto_apply_count: int = 0
    merge_count: int = 0
    commit_count_by_agent: int = 0

    def to_dict(self) -> dict[str, Any]:
        return {
            "proposal_id": self.proposal_id,
            "candidate_id": self.candidate_id,
            "source_root": self.source_root,
            "proposal_worktree_root": self.proposal_worktree_root,
            "baseline_root": self.baseline_root,
            "proposed": self.proposed,
            "proposal_verifier_passed": self.proposal_verifier_passed,
            "verifier_after_patch": self.verifier_after_patch,
            "patch_report": dict(self.patch_report),
            "risk_diff": dict(self.risk_diff),
            "verifier_summary": dict(self.verifier_summary),
            "execution_result": dict(self.execution_result),
            "auto_apply_count": self.auto_apply_count,
            "merge_count": self.merge_count,
            "commit_count_by_agent": self.commit_count_by_agent,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "WorktreePatchProposalReport":
        return cls(
            proposal_id=str(data["proposal_id"]),
            candidate_id=str(data["candidate_id"]),
            source_root=str(data["source_root"]),
            proposal_worktree_root=str(data["proposal_worktree_root"]),
            baseline_root=str(data["baseline_root"]),
            proposed=bool(data["proposed"]),
            proposal_verifier_passed=bool(data["proposal_verifier_passed"]),
            verifier_after_patch=bool(data["verifier_after_patch"]),
            patch_report=dict(data.get("patch_report") or {}),
            risk_diff=dict(data.get("risk_diff") or {}),
            verifier_summary=dict(data.get("verifier_summary") or {}),
            execution_result=dict(data.get("execution_result") or {}),
            auto_apply_count=int(data.get("auto_apply_count", 0)),
            merge_count=int(data.get("merge_count", 0)),
            commit_count_by_agent=int(data.get("commit_count_by_agent", 0)),
        )


class WorktreePatchProposalRunner:
    def __init__(
        self,
        state_dir: str | Path,
        *,
        main_checkout_root: str | Path,
        timeout_seconds: float = 120.0,
    ) -> None:
        self.state_dir = Path(state_dir)
        self.state_dir.mkdir(parents=True, exist_ok=True)
        self.main_checkout_root = Path(main_checkout_root).resolve()
        self.timeout_seconds = timeout_seconds

    def propose_patch(
        self,
        *,
        proposal_id: str,
        source_root: str | Path,
        candidate: PatchCandidate,
    ) -> WorktreePatchProposalReport:
        executor = SandboxPatchExecutor(
            self.state_dir / "proposal_worktrees",
            main_checkout_root=self.main_checkout_root,
            timeout_seconds=self.timeout_seconds,
        )
        result = executor.execute_candidate(
            execution_id=proposal_id,
            source_root=source_root,
            candidate=candidate,
            expected_verifier_success=True,
        )
        audit = dict(result.diff_minimization_audit)
        patch_report = _patch_report_from_audit(audit)
        risk_diff = _risk_diff(candidate, patch_report, result)
        return WorktreePatchProposalReport(
            proposal_id=proposal_id,
            candidate_id=candidate.candidate_id,
            source_root=str(Path(source_root).resolve()),
            proposal_worktree_root=result.sandbox_root,
            baseline_root=result.snapshot_root,
            proposed=result.executed and result.patch_apply_success and result.diff_minimized,
            proposal_verifier_passed=result.verifier_passed,
            verifier_after_patch=result.verifier_after_patch,
            patch_report=patch_report,
            risk_diff=risk_diff,
            verifier_summary=dict(result.verifier_summary),
            execution_result=result.to_dict(),
            auto_apply_count=0,
            merge_count=0,
            commit_count_by_agent=0,
        )


def _patch_report_from_audit(audit: dict[str, Any]) -> dict[str, Any]:
    diff_text = str(audit.get("diff_text", ""))
    changed_files = [str(item) for item in audit.get("changed_files", [])]
    return {
        "diff_text": diff_text,
        "diff_hash": str(audit.get("diff_hash") or stable_hash({"changed_files": changed_files, "diff_text": diff_text})),
        "changed_file_count": int(audit.get("changed_file_count", len(changed_files))),
        "diff_line_count": int(audit.get("diff_line_count", len(diff_text.splitlines()))),
        "changed_files": changed_files,
    }


def _risk_diff(
    candidate: PatchCandidate,
    patch_report: dict[str, Any],
    result: SandboxPatchExecutionResult,
) -> dict[str, Any]:
    changed_files = [str(item) for item in patch_report.get("changed_files", [])]
    return {
        "candidate_id": candidate.candidate_id,
        "candidate_risk_class": candidate.risk_class,
        "changed_file_count": int(patch_report.get("changed_file_count", 0)),
        "diff_line_count": int(patch_report.get("diff_line_count", 0)),
        "changed_files": changed_files,
        "touches_source_file_count": sum(1 for item in changed_files if item.startswith("src/") and item.endswith(".py")),
        "touches_test_file_count": sum(1 for item in changed_files if item.startswith("tests/")),
        "requires_human_approval": True,
        "patch_escape_count": result.patch_escape_count,
        "real_repo_mutation_count": result.real_repo_mutation_count,
        "verifier_after_patch": result.verifier_after_patch,
        "proposal_verifier_passed": result.verifier_passed,
    }
