"""Already-landed open PR reconciliation gates (#310).

Reconciler workflows may close an open PR only when the PR head SHA is proven
an ancestor of a freshly fetched target branch. Arbitrary PR closure is denied.
"""

from __future__ import annotations

import subprocess
from typing import Any

from merged_cleanup_reconcile import extract_linked_issue, is_head_ancestor_of_ref

ELIGIBILITY_ALREADY_LANDED = "ALREADY_LANDED_RECONCILE_REQUIRED"
ELIGIBILITY_NOT_LANDED = "NOT_ALREADY_LANDED"
ELIGIBILITY_STALE_TARGET = "TARGET_BRANCH_UNVERIFIED"
ELIGIBILITY_PR_NOT_OPEN = "PR_NOT_OPEN"


def fetch_target_branch(project_root: str, remote: str, branch: str) -> dict[str, Any]:
    """Fetch *branch* from *remote* and return the resolved SHA."""
    ref = f"{remote}/{branch}"
    fetch = subprocess.run(
        ["git", "-C", project_root, "fetch", remote, branch],
        capture_output=True,
        text=True,
        check=False,
    )
    if fetch.returncode != 0:
        return {
            "success": False,
            "target_branch": branch,
            "target_ref": ref,
            "target_branch_sha": None,
            "reasons": [
                f"git fetch {remote} {branch} failed: "
                f"{(fetch.stderr or fetch.stdout or '').strip()}"
            ],
        }

    rev = subprocess.run(
        ["git", "-C", project_root, "rev-parse", ref],
        capture_output=True,
        text=True,
        check=False,
    )
    if rev.returncode != 0:
        return {
            "success": False,
            "target_branch": branch,
            "target_ref": ref,
            "target_branch_sha": None,
            "reasons": [
                f"git rev-parse {ref} failed: "
                f"{(rev.stderr or rev.stdout or '').strip()}"
            ],
        }

    return {
        "success": True,
        "target_branch": branch,
        "target_ref": ref,
        "target_branch_sha": (rev.stdout or "").strip(),
        "reasons": [],
        "git_fetch_command": f"git fetch {remote} {branch}",
    }


def assess_already_landed_reconciliation(
    *,
    pr: dict[str, Any],
    project_root: str,
    remote: str,
    target_branch: str,
    target_fetch: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Return eligibility and proof for reconciling an open PR."""
    pr_number = int(pr.get("number") or 0)
    pr_state = (pr.get("state") or "").strip().lower()
    head_sha = (pr.get("head") or {}).get("sha") if isinstance(pr.get("head"), dict) else None
    if not head_sha and isinstance(pr.get("head"), str):
        head_sha = None
    head_ref = (pr.get("head") or {}).get("ref") if isinstance(pr.get("head"), dict) else pr.get("head")
    base_ref = (pr.get("base") or {}).get("ref") if isinstance(pr.get("base"), dict) else pr.get("base")
    title = pr.get("title") or ""
    body = pr.get("body") or ""

    fetch_result = target_fetch or fetch_target_branch(project_root, remote, target_branch)
    linked_issue = extract_linked_issue(title, body)

    result: dict[str, Any] = {
        "pr_number": pr_number,
        "pr_state": pr_state,
        "candidate_head_sha": head_sha,
        "head_ref": head_ref,
        "base_ref": base_ref or target_branch,
        "target_branch": target_branch,
        "target_branch_sha": fetch_result.get("target_branch_sha"),
        "linked_issue": linked_issue,
        "git_ref_mutations": [],
        "reasons": [],
    }
    if fetch_result.get("git_fetch_command"):
        result["git_ref_mutations"].append(fetch_result["git_fetch_command"])

    if pr_state != "open":
        result["eligibility_class"] = ELIGIBILITY_PR_NOT_OPEN
        result["ancestor_proof"] = None
        result["close_allowed"] = False
        result["reasons"].append(f"PR #{pr_number} state is {pr_state!r}, not open")
        return result

    if not fetch_result.get("success"):
        result["eligibility_class"] = ELIGIBILITY_STALE_TARGET
        result["ancestor_proof"] = None
        result["close_allowed"] = False
        result["reasons"].extend(fetch_result.get("reasons") or [])
        return result

    target_ref = fetch_result.get("target_ref") or f"{remote}/{target_branch}"
    ancestor = is_head_ancestor_of_ref(project_root, head_sha, target_ref)
    result["ancestor_proof"] = ancestor

    if ancestor is None:
        result["eligibility_class"] = ELIGIBILITY_STALE_TARGET
        result["close_allowed"] = False
        result["reasons"].append(
            f"ancestor check failed for head {head_sha!r} against {target_ref}"
        )
        return result

    if ancestor:
        result["eligibility_class"] = ELIGIBILITY_ALREADY_LANDED
        result["close_allowed"] = True
        return result

    result["eligibility_class"] = ELIGIBILITY_NOT_LANDED
    result["close_allowed"] = False
    result["reasons"].append(
        f"PR head {head_sha} is not an ancestor of {target_ref}"
    )
    return result