"""Reviewer inventory completeness and worktree state verifier (#293)."""

from __future__ import annotations

import re
from typing import Any

_PAGINATION_FINALITY_EVIDENCE = re.compile(
    r"pagination_complete\s*:\s*true|inventory_complete\s*:\s*true|"
    r"is_final_page\s*:\s*true|pages_fetched|has_more\s*:\s*false|"
    r"no next page|final[- ]page|pr_inventory_trust_gate\.status|"
    r"total_count\s*:|pagination.*(?:final|complete)",
    re.I,
)

_INCOMPLETE_PAGINATION_PROOF = re.compile(
    r"first page only|partial page|page 1 only|truncated|"
    r"returned \d+ open prs?(?:\s*$|\s*[,;])",
    re.I,
)

_DEFAULT_PAGE_SIZE_ASSUMPTION = re.compile(
    r"(?:less|fewer) than (?:the )?(?:default )?(?:gitea )?page[- ]?(?:size|limit)|"
    r"default (?:gitea )?page[- ]?size|under (?:the )?50[- ]?(?:item )?limit|"
    r"(?:complete|exhaustive).*(?:default )?page[- ]?size|"
    r"page[- ]?size assumption|assumed complete",
    re.I,
)

_ELIGIBILITY_CLAIM_RE = re.compile(
    r"\b(?:oldest eligible pr|next eligible pr|next pr to review|"
    r"inventory (?:is )?complete|inventory exhaustive|exhaustive inventory)\b",
    re.I,
)

_EXACT_PAGE_LIMIT_RE = re.compile(
    r"(?:returned|listed|fetched)\s+(\d+)\s+open prs?|"
    r"open pr count\s*:\s*(\d+)|"
    r"page[- ]?size\s*(?:=|:)\s*(\d+)",
    re.I,
)

_LEGACY_SCRATCH_FALSE_RE = re.compile(
    r"scratch worktree used\s*:\s*false",
    re.I,
)

_BRANCHES_WORKTREE_RE = re.compile(r"\bbranches/", re.I)

_CONTRADICTORY_HEAD_RE = re.compile(
    r"detached head\s*/\s*branch\s+master|"
    r"branch\s+master\s*/\s*detached head|"
    r"detached head.*branch\s+(?:master|main|dev)\b.*detached|"
    r"checkout\s*:\s*detached head\s*/\s*branch",
    re.I,
)

_MAIN_CHECKOUT_BRANCH_RE = re.compile(
    r"main checkout branch\s*:\s*(\S+)",
    re.I,
)

_REVIEW_HEAD_STATE_RE = re.compile(
    r"review worktree head state\s*:\s*(\S+)",
    re.I,
)

_HANDOFF_SECTION_RE = re.compile(
    r"^##\s*Controller Handoff\s*$",
    re.I | re.M,
)


def _handoff_field_map(report_text: str) -> dict[str, str]:
    """Parse ``- Field: value`` lines from the Controller Handoff section."""
    text = report_text or ""
    match = _HANDOFF_SECTION_RE.search(text)
    if not match:
        return {}
    section = text[match.end() :]
    fields: dict[str, str] = {}
    for line in section.splitlines():
        stripped = line.strip().lstrip("-*").strip()
        if ":" not in stripped:
            continue
        key, value = stripped.split(":", 1)
        fields[key.strip().lower()] = value.strip()
    return fields


def _truthy_field(value: str) -> bool:
    lowered = (value or "").strip().lower()
    if not lowered:
        return False
    if lowered in {"false", "no", "none", "not applicable", "n/a", "—", "-"}:
        return False
    return True


def _field_proves_pagination(value: str) -> bool:
    proof = (value or "").strip()
    if not proof or proof.lower() in {"none", "n/a", "not applicable", "unknown"}:
        return False
    if _DEFAULT_PAGE_SIZE_ASSUMPTION.search(proof):
        return False
    if _INCOMPLETE_PAGINATION_PROOF.search(proof):
        return False
    return bool(_PAGINATION_FINALITY_EVIDENCE.search(proof))


def _pagination_proven(text: str, session: dict, *, pagination_field: str = "") -> bool:
    if session.get("pagination_complete") or session.get("inventory_complete"):
        return True
    session_proof = session.get("inventory_pagination_proof") or ""
    if _field_proves_pagination(str(session_proof)):
        return True
    if _field_proves_pagination(pagination_field):
        return True
    if _PAGINATION_FINALITY_EVIDENCE.search(text):
        return True
    return False


def _exact_page_limit_unproven(
    text: str, session: dict, *, pagination_proven: bool = False
) -> bool:
    """True when a full page was returned without final-page proof."""
    if pagination_proven:
        return False
    requested = session.get("requested_page_size")
    returned = session.get("returned_page_size")
    if isinstance(requested, int) and isinstance(returned, int):
        if returned >= requested > 0:
            return True
    for match in _EXACT_PAGE_LIMIT_RE.finditer(text):
        count = next((g for g in match.groups() if g), None)
        if not count:
            continue
        try:
            n = int(count)
        except ValueError:
            continue
        if n in {10, 20, 50}:
            return True
    return False


def assess_inventory_worktree_report(
    report_text: str,
    *,
    inventory_session: dict | None = None,
) -> dict[str, Any]:
    """Validate PR inventory pagination proof and worktree field consistency (#293)."""
    text = report_text or ""
    session = dict(inventory_session or {})
    reasons: list[str] = []

    fields = _handoff_field_map(text)
    pagination_proof_field = fields.get("inventory pagination proof", "")

    pagination_proven = _pagination_proven(
        text, session, pagination_field=pagination_proof_field
    )

    if _ELIGIBILITY_CLAIM_RE.search(text):
        if not pagination_proven:
            reasons.append(
                "oldest/next eligible PR or inventory-complete claim requires "
                "final-page/no-next-page/pagination_complete proof"
            )

    if _DEFAULT_PAGE_SIZE_ASSUMPTION.search(text):
        if not pagination_proven:
            reasons.append(
                "inventory pagination assumed from default page size without "
                "final-page/no-next-page/total-count/traversal proof"
            )

    if pagination_proof_field:
        if _DEFAULT_PAGE_SIZE_ASSUMPTION.search(pagination_proof_field):
            reasons.append(
                "Inventory pagination proof field relies on page-size assumption"
            )
        elif _INCOMPLETE_PAGINATION_PROOF.search(pagination_proof_field):
            reasons.append(
                "Inventory pagination proof field does not prove final page"
            )
        elif not _field_proves_pagination(pagination_proof_field):
            if _ELIGIBILITY_CLAIM_RE.search(text) or _EXACT_PAGE_LIMIT_RE.search(text):
                reasons.append(
                    "Inventory pagination proof field missing final-page metadata"
                )

    if _exact_page_limit_unproven(text, session, pagination_proven=pagination_proven):
        reasons.append(
            "exactly-full first page returned without final-page or "
            "no-next-page proof"
        )

    review_worktree_used = fields.get("review worktree used", "")
    review_worktree_path = fields.get("review worktree path", "")
    scratch_used = fields.get("scratch worktree used", "")
    inside_branches = fields.get("review worktree inside branches:", "") or fields.get(
        "review worktree inside branches", ""
    )

    branches_path_in_text = bool(
        _BRANCHES_WORKTREE_RE.search(review_worktree_path)
        or _BRANCHES_WORKTREE_RE.search(text)
    )

    if branches_path_in_text:
        if review_worktree_used and not _truthy_field(review_worktree_used):
            reasons.append(
                "reports using a branches/ review worktree must set "
                "Review worktree used: true"
            )
        if scratch_used and re.search(r"\bfalse\b", scratch_used, re.I):
            reasons.append(
                "Scratch worktree used: false rejected when a branches/ "
                "review worktree was created or used"
            )
        if _LEGACY_SCRATCH_FALSE_RE.search(text) and not _truthy_field(
            review_worktree_used
        ):
            reasons.append(
                "legacy Scratch worktree used: false contradicts branches/ "
                "review worktree usage"
            )

    if review_worktree_path and _truthy_field(review_worktree_used):
        if "branches/" not in review_worktree_path.replace("\\", "/").lower():
            reasons.append(
                "Review worktree path must be under branches/ when "
                "Review worktree used is true"
            )
        if inside_branches and re.search(r"\bfalse\b", inside_branches, re.I):
            reasons.append(
                "Review worktree inside branches must be true when path is "
                "under branches/"
            )

    main_branch = (
        fields.get("main checkout branch", "")
        or _MAIN_CHECKOUT_BRANCH_RE.search(text).group(1)
        if _MAIN_CHECKOUT_BRANCH_RE.search(text)
        else ""
    )
    head_state = (
        fields.get("review worktree head state", "")
        or (
            _REVIEW_HEAD_STATE_RE.search(text).group(1)
            if _REVIEW_HEAD_STATE_RE.search(text)
            else ""
        )
    )
    if main_branch and head_state:
        main_norm = main_branch.strip().lower()
        head_norm = head_state.strip().lower()
        if head_norm in {"branch", "on branch"} and main_norm in {
            "master",
            "main",
            "dev",
        }:
            if "detached" in text.lower() and "review worktree" in text.lower():
                reasons.append(
                    "review worktree HEAD state must not reuse main checkout "
                    "branch wording"
                )

    if _CONTRADICTORY_HEAD_RE.search(text):
        reasons.append(
            "contradictory checkout wording such as 'Detached HEAD / Branch master'"
        )

    if review_worktree_used and _truthy_field(review_worktree_used):
        if not review_worktree_path or review_worktree_path.lower() in {
            "none",
            "n/a",
            "not applicable",
        }:
            reasons.append(
                "Review worktree used: true requires Review worktree path"
            )
        if not head_state or head_state.lower() in {
            "none",
            "n/a",
            "not applicable",
            "unknown",
        }:
            reasons.append(
                "Review worktree used: true requires Review worktree HEAD state"
            )

    proven = not reasons
    return {
        "proven": proven,
        "block": not proven,
        "reasons": list(dict.fromkeys(reasons)),
        "pagination_proven": pagination_proven,
        "branches_worktree_used": branches_path_in_text,
        "safe_next_action": (
            "prove inventory pagination with final-page metadata; align "
            "Review worktree used/path/HEAD state with branches/ usage"
            if reasons
            else "proceed"
        ),
    }