#!/usr/bin/env python3
"""Inspect or merge a reviewed NestQuest feature PR into dev only."""

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path
import re
import sys
import urllib.error
import urllib.request


API = "https://code.cubecraftlabs.com/api/v1/repos/Maniacs_Mansion/NestQuest"
CREDENTIALS = Path("/home/overseer/.llms/credentials.env")
FULL_SHA = re.compile(r"^[0-9a-f]{40}$")


def token_from_file() -> str:
    for line in CREDENTIALS.read_text().splitlines():
        if line.startswith("GITEA_APPROVER_AGENT_TOKEN="):
            return line.split("=", 1)[1].strip().strip("\"'")
    raise RuntimeError("GITEA_APPROVER_AGENT_TOKEN is unavailable")


def request(path: str, token: str, payload: dict | None = None) -> dict | list:
    body = None if payload is None else json.dumps(payload).encode()
    headers = {"Authorization": "token " + token, "Accept": "application/json"}
    if body is not None:
        headers["Content-Type"] = "application/json"
    call = urllib.request.Request(API + path, data=body, headers=headers)
    try:
        with urllib.request.urlopen(call, timeout=20) as response:
            raw = response.read()
            return json.loads(raw) if raw else {}
    except urllib.error.HTTPError as error:
        raise RuntimeError(f"Gitea returned HTTP {error.code}") from None


def inspect(pr_number: int, token: str) -> dict:
    pr = request(f"/pulls/{pr_number}", token)
    head = (pr.get("head") or {}).get("sha")
    base = (pr.get("base") or {}).get("ref")
    if not isinstance(head, str) or not FULL_SHA.fullmatch(head):
        raise RuntimeError("Gitea returned an invalid PR head")
    status = request(f"/commits/{head}/status", token)
    return {
        "number": pr.get("number"),
        "base": base,
        "head": head,
        "state": pr.get("state"),
        "merged": pr.get("merged"),
        "mergeable": pr.get("mergeable"),
        "check_count": status.get("total_count") or 0,
        "check_state": status.get("state"),
    }


def validate_review(path: Path, head: str) -> None:
    if not path.is_file() or not path.resolve().is_relative_to(Path("/tmp/opencode").resolve()):
        raise RuntimeError("review evidence must be a file under /tmp/opencode")
    text = path.read_text(errors="replace")
    reviewed = re.findall(r"(?m)^REVIEWED_HEAD:\s*([0-9a-f]{40})\s*$", text)
    verdicts = re.findall(r"(?m)^REVIEW_VERDICT:\s*(APPROVED|CHANGES_REQUIRED)\s*$", text)
    if len(reviewed) != 1 or len(verdicts) != 1 or reviewed[0] != head or verdicts[0] != "APPROVED":
        raise RuntimeError("independent review does not approve this exact head")


def has_platform_review(pr_number: int, head: str, token: str) -> bool:
    reviews = request(f"/pulls/{pr_number}/reviews", token)
    if not isinstance(reviews, list):
        raise RuntimeError("Gitea returned invalid review records")
    by_reviewer = [
        review for review in reviews
        if isinstance(review, dict)
        and (review.get("user") or {}).get("login") == "review_agent"
        and review.get("commit_id") == head
    ]
    if not by_reviewer:
        return False
    latest = max(by_reviewer, key=lambda review: int(review.get("id") or 0))
    return latest.get("state") == "APPROVED"


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("operation", choices=("inspect", "merge"))
    parser.add_argument("pr", type=int)
    parser.add_argument("reviewed_head", nargs="?")
    parser.add_argument("review_file", nargs="?", type=Path)
    args = parser.parse_args()
    if args.pr < 1:
        parser.error("PR number must be positive")
    if args.operation == "merge" and (not args.reviewed_head or not args.review_file):
        parser.error("merge requires reviewed_head and review_file")
    token = token_from_file()
    before = inspect(args.pr, token)
    if args.operation == "inspect":
        print(json.dumps(before, sort_keys=True))
        return 0
    if not FULL_SHA.fullmatch(args.reviewed_head):
        parser.error("reviewed head must be a full SHA")
    validate_review(args.review_file, args.reviewed_head)
    if not has_platform_review(args.pr, args.reviewed_head, token):
        raise RuntimeError("Gitea lacks an exact-head approval by review_agent")
    if before["base"] != "dev" or before["head"] != args.reviewed_head:
        raise RuntimeError("PR target or current head differs from the approved review")
    if before["state"] != "open" or before["merged"] or before["mergeable"] is not True:
        raise RuntimeError("PR is closed, merged, or not mergeable")
    if before["check_count"] and before["check_state"] != "success":
        raise RuntimeError("a configured commit check has not passed")
    # Gitea rechecks branch protection at the merge endpoint. The caller must
    # independently verify Maestro and task-level evidence before invoking us.
    # Gitea checks head_commit_id atomically while processing the merge.
    request(f"/pulls/{args.pr}/merge", token,
            {"do": "squash", "head_commit_id": args.reviewed_head})
    after = inspect(args.pr, token)
    if after["merged"] is not True or after["head"] != args.reviewed_head:
        raise RuntimeError("merge result is missing or differs from the reviewed head")
    print(json.dumps(after, sort_keys=True))
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except (OSError, ValueError, RuntimeError) as error:
        print(f"approval gate failed: {error}", file=sys.stderr)
        raise SystemExit(1)
