"""Guarded merger adoption of an existing reviewer PR lease (#536).

Replaces manual in-process ``_SESSION_LEASE`` seeding with an auditable,
comment-backed adoption path for merger sessions that inherit a reviewer lease
after formal approval at the current PR head.
"""

from __future__ import annotations

import re
from datetime import datetime, timezone
from typing import Any

import reviewer_pr_lease as leases

ADOPTION_MARKER = "<!-- mcp-review-lease-adoption:v1 -->"
DEFAULT_ADOPTION_REASON = "merger-handoff-approved-head"

SOURCE_ADOPT = "gitea_adopt_merger_pr_lease"
SOURCE_ACQUIRE = "gitea_acquire_reviewer_pr_lease"
SOURCE_ACQUIRE_MERGER = "gitea_acquire_merger_pr_lease"
SOURCE_HEARTBEAT = "gitea_heartbeat_reviewer_pr_lease"
SOURCE_RELEASE_MERGER = "gitea_release_merger_pr_lease"

SANCTIONED_PROVENANCE_SOURCES = frozenset({
    SOURCE_ADOPT,
    SOURCE_ACQUIRE,
    SOURCE_ACQUIRE_MERGER,
    SOURCE_HEARTBEAT,
})

_MERGER_ADOPTABLE_FRESHNESS = frozenset({"active", "stale_warning"})

# #742: owner-session terminal finalization of a merger-held lease. Append-only
# marker; ledger history is never edited or deleted.
MERGER_FINALIZATION_MARKER = "<!-- mcp-merger-lease-final:v1 -->"
OUTCOME_RELEASED = "released"
OUTCOME_ABANDONED = "abandoned"
MERGER_FINALIZATION_OUTCOMES = frozenset({OUTCOME_RELEASED, OUTCOME_ABANDONED})
DEFAULT_MERGER_FINALIZATION_REASON = "merge-not-performed"

# Provenance sources whose in-session lease is merger-owned and therefore
# finalizable by its owning merger session.
MERGER_OWNED_PROVENANCE_SOURCES = frozenset({
    SOURCE_ACQUIRE_MERGER,
    SOURCE_ADOPT,
})


def format_adoption_body(
    *,
    repo: str,
    pr_number: int,
    issue_number: int | None,
    adopter_identity: str,
    adopter_profile: str,
    adopter_session_id: str,
    worktree: str,
    candidate_head: str | None,
    target_branch: str,
    target_branch_sha: str | None,
    adopted_from_session_id: str,
    adopted_from_profile: str,
    adopted_from_reviewer_identity: str,
    adopted_from_comment_id: int | None,
    adoption_reason: str = DEFAULT_ADOPTION_REASON,
    adopted_at: datetime | None = None,
) -> str:
    """Render a durable merger adoption proof comment."""
    adopted_at = adopted_at or datetime.now(timezone.utc)
    adopted_text = adopted_at.astimezone(timezone.utc).replace(microsecond=0).isoformat().replace(
        "+00:00", "Z"
    )
    lease_body = leases.format_lease_body(
        repo=repo,
        pr_number=pr_number,
        issue_number=issue_number,
        reviewer_identity=adopter_identity,
        profile=adopter_profile,
        session_id=adopter_session_id,
        worktree=worktree,
        phase="adopted",
        candidate_head=candidate_head,
        target_branch=target_branch,
        target_branch_sha=target_branch_sha,
        last_activity=adopted_at,
        blocker="none",
    )
    lines = [
        ADOPTION_MARKER,
        f"adopted_at: {adopted_text}",
        f"adopted_by_identity: {adopter_identity}",
        f"adopted_by_profile: {adopter_profile}",
        f"adopted_from_session_id: {adopted_from_session_id}",
        f"adopted_from_profile: {adopted_from_profile}",
        f"adopted_from_reviewer_identity: {adopted_from_reviewer_identity}",
        f"adopted_from_comment_id: {adopted_from_comment_id or 'none'}",
        f"adoption_reason: {adoption_reason}",
        lease_body,
    ]
    return "\n".join(lines)


def is_adoption_comment(body: str) -> bool:
    return ADOPTION_MARKER in (body or "")


def build_lease_provenance(
    *,
    source: str,
    comment_id: int | None = None,
    adopted_from_session_id: str | None = None,
    adopted_from_profile: str | None = None,
    adopted_from_reviewer_identity: str | None = None,
    adoption_reason: str | None = None,
    native_token_fingerprint: str | None = None,
    recorded_at: datetime | None = None,
) -> dict[str, Any]:
    recorded_at = recorded_at or datetime.now(timezone.utc)
    recorded_text = recorded_at.astimezone(timezone.utc).replace(microsecond=0).isoformat().replace(
        "+00:00", "Z"
    )
    proof = {
        "source": source,
        "recorded_at": recorded_text,
    }
    if comment_id is not None:
        proof["comment_id"] = comment_id
    if adopted_from_session_id:
        proof["adopted_from_session_id"] = adopted_from_session_id
    if adopted_from_profile:
        proof["adopted_from_profile"] = adopted_from_profile
    if adopted_from_reviewer_identity:
        proof["adopted_from_reviewer_identity"] = adopted_from_reviewer_identity
    if adoption_reason:
        proof["adoption_reason"] = adoption_reason
    if native_token_fingerprint:
        proof["native_token_fingerprint"] = native_token_fingerprint
    return proof


def assess_acquired_merger_lease_integrity(
    session: dict[str, Any] | None,
) -> list[str]:
    """Ownership/integrity reasons blocking a ``SOURCE_ACQUIRE_MERGER`` lease (#742).

    A merger lease minted by ``gitea_acquire_merger_pr_lease`` authorizes an
    irreversible merge, so it is sanctioned only when the in-session record is
    complete and self-consistent: comment marker, exact session identity, merger
    profile/role, repository, PR number, and pinned candidate head. Any missing
    or contradictory field fails closed — an incomplete record is not proof.
    """
    if not session:
        return ["no in-session lease recorded"]
    provenance = session.get("lease_provenance") or {}
    if not isinstance(provenance, dict):
        provenance = {}
    reasons: list[str] = []

    provenance_comment_id = provenance.get("comment_id")
    session_comment_id = session.get("comment_id")
    if not (provenance_comment_id or session_comment_id):
        reasons.append(
            "acquired merger lease has no comment marker id (comment-backed "
            "proof required)"
        )
    elif (
        provenance_comment_id is not None
        and session_comment_id is not None
        and provenance_comment_id != session_comment_id
    ):
        reasons.append(
            "acquired merger lease provenance comment_id does not match the "
            "session lease comment_id"
        )

    if not (session.get("session_id") or "").strip():
        reasons.append("acquired merger lease has no session_id")
    if not (session.get("reviewer_identity") or "").strip():
        reasons.append("acquired merger lease has no holder identity")

    profile = (session.get("profile") or "").strip()
    if not profile:
        reasons.append("acquired merger lease has no profile")
    elif "merger" not in profile.lower():
        reasons.append(
            f"acquired merger lease profile '{profile}' is not a merger profile "
            "(merger-only; fail closed)"
        )

    if not (session.get("repo") or "").strip():
        reasons.append("acquired merger lease has no repository")

    pr_number = session.get("pr_number")
    if not isinstance(pr_number, int) or isinstance(pr_number, bool) or pr_number <= 0:
        reasons.append("acquired merger lease has no valid PR number")

    if not leases._normalize_sha(session.get("candidate_head")):
        reasons.append(
            "acquired merger lease has no pinned candidate_head (exact-head "
            "scoping required)"
        )

    return reasons


def is_sanctioned_session_lease(session: dict[str, Any] | None) -> bool:
    if not session:
        return False
    provenance = session.get("lease_provenance") or {}
    source = (provenance.get("source") or "").strip()
    if source not in SANCTIONED_PROVENANCE_SOURCES:
        return False
    if source == SOURCE_ADOPT:
        return bool(provenance.get("comment_id")) and bool(
            provenance.get("adopted_from_session_id")
        )
    if source == SOURCE_ACQUIRE_MERGER:
        return not assess_acquired_merger_lease_integrity(session)
    if source in {SOURCE_ACQUIRE, SOURCE_HEARTBEAT}:
        return bool(session.get("comment_id") or provenance.get("comment_id"))
    return False


def describe_session_lease_proof(
    session: dict[str, Any] | None,
) -> dict[str, Any]:
    """Canonical merge-report lease proof fields (#535).

    Surfaces how the in-session lease was established so merge handoffs can
    distinguish sanctioned MCP acquire/adopt paths from bare manual seeding.
    """
    if not session:
        return {
            "lease_proof_source": None,
            "lease_recovery_reason": None,
            "lease_proof_kind": "none",
            "lease_proof_sanctioned": False,
            "lease_proof_comment_id": None,
            "lease_adopted_from_session_id": None,
        }

    provenance = session.get("lease_provenance") or {}
    if not isinstance(provenance, dict):
        provenance = {}
    source = (provenance.get("source") or "").strip() or None
    sanctioned = is_sanctioned_session_lease(session)
    reason = provenance.get("adoption_reason")
    if isinstance(reason, str):
        reason = reason.strip() or None
    else:
        reason = None

    if source == SOURCE_ADOPT and sanctioned:
        kind = "sanctioned_adoption"
        reason = reason or DEFAULT_ADOPTION_REASON
    elif source == SOURCE_ACQUIRE_MERGER and sanctioned:
        kind = "sanctioned_acquire_merger"
    elif source == SOURCE_ACQUIRE and sanctioned:
        kind = "sanctioned_acquire"
    elif source == SOURCE_HEARTBEAT and sanctioned:
        kind = "sanctioned_heartbeat"
    elif source:
        kind = "unsanctioned"
    else:
        # Session present without provenance = classic manual _SESSION_LEASE seed.
        kind = "unsanctioned_manual_seed"

    comment_id = provenance.get("comment_id")
    if comment_id is None:
        comment_id = session.get("comment_id")

    return {
        "lease_proof_source": source,
        "lease_recovery_reason": reason,
        "lease_proof_kind": kind,
        "lease_proof_sanctioned": sanctioned,
        "lease_proof_comment_id": comment_id,
        "lease_adopted_from_session_id": provenance.get("adopted_from_session_id"),
    }


# Phrases that claim non-MCP / fabricated lease proof in handoffs (#535).
_UNSAFE_LEASE_PROOF_CLAIM_RE = re.compile(
    r"(?is)\b("
    r"manually\s+seeded\s+lease|"
    r"manual(?:ly)?\s+seed(?:ed)?\s+(?:lease|proof)|"
    r"seeded\s+lease\s+proof|"
    r"injected\s+lease|"
    r"lease\s+proof\s+injected|"
    r"manual\s+_?SESSION_LEASE|"
    r"_SESSION_LEASE\s+seed|"
    r"unsanctioned\s+lease\s+proof"
    r")\b"
)

# Evidence that the report used the sanctioned MCP adoption/acquire path.
_SANCTIONED_LEASE_EVIDENCE_RE = re.compile(
    r"(?is)\b("
    r"gitea_adopt_merger_pr_lease|"
    r"gitea_acquire_reviewer_pr_lease|"
    r"gitea_acquire_merger_pr_lease|"
    r"lease_proof_source\s*[:=]\s*gitea_adopt_merger_pr_lease|"
    r"lease_proof_source\s*[:=]\s*gitea_acquire_reviewer_pr_lease|"
    r"lease_proof_source\s*[:=]\s*gitea_acquire_merger_pr_lease|"
    r"lease_proof_kind\s*[:=]\s*sanctioned_adoption|"
    r"lease_proof_kind\s*[:=]\s*sanctioned_acquire_merger|"
    r"lease_proof_kind\s*[:=]\s*sanctioned_acquire|"
    r"adoption_comment_id\s*[:=]|"
    r"sanctioned_adoption|"
    r"mcp-review-lease-adoption"
    r")\b"
)


def assess_manual_lease_proof_handoff(report_text: str) -> dict[str, Any]:
    """Controller audit: block handoffs that claim unsafe lease seeding (#535).

    Reports may discuss manual seeding as a blocked/historical anti-pattern only
    when they also cite sanctioned MCP adoption/acquire evidence. Bare claims of
    manual/seeded/injected lease proof without that evidence fail closed.
    """
    text = report_text or ""
    if not _UNSAFE_LEASE_PROOF_CLAIM_RE.search(text):
        return {"proven": True, "block": False, "reasons": []}
    if _SANCTIONED_LEASE_EVIDENCE_RE.search(text):
        return {"proven": True, "block": False, "reasons": []}
    return {
        "proven": False,
        "block": True,
        "reasons": [
            "report claims manual/seeded/injected/unsanctioned lease proof without "
            "sanctioned MCP adoption evidence (use gitea_adopt_merger_pr_lease / "
            "gitea_acquire_reviewer_pr_lease and cite lease_proof_source; #535)"
        ],
    }


def format_merger_finalization_body(
    *,
    repo: str,
    pr_number: int,
    issue_number: int | None,
    merger_identity: str,
    merger_profile: str,
    merger_session_id: str,
    worktree: str,
    candidate_head: str | None,
    target_branch: str,
    target_branch_sha: str | None,
    outcome: str,
    reason: str,
    lease_comment_id: int | None,
    finalized_at: datetime | None = None,
) -> str:
    """Render the append-only terminal marker for a merger-owned lease (#742).

    The body carries a standard lease marker in a terminal phase, so the
    existing newest-wins ledger (#577) ends the lease without editing or
    deleting any prior comment.
    """
    finalized_at = finalized_at or datetime.now(timezone.utc)
    finalized_text = finalized_at.astimezone(timezone.utc).replace(
        microsecond=0
    ).isoformat().replace("+00:00", "Z")
    lease_body = leases.format_lease_body(
        repo=repo,
        pr_number=pr_number,
        issue_number=issue_number,
        reviewer_identity=merger_identity,
        profile=merger_profile,
        session_id=merger_session_id,
        worktree=worktree,
        phase=outcome,
        candidate_head=candidate_head,
        target_branch=target_branch,
        target_branch_sha=target_branch_sha,
        last_activity=finalized_at,
        blocker=reason,
    )
    lines = [
        MERGER_FINALIZATION_MARKER,
        f"finalized_at: {finalized_text}",
        f"finalized_by_identity: {merger_identity}",
        f"finalized_by_profile: {merger_profile}",
        f"finalized_by_session_id: {merger_session_id}",
        f"finalization_outcome: {outcome}",
        f"finalization_reason: {reason}",
        f"finalized_lease_comment_id: {lease_comment_id or 'none'}",
        lease_body,
    ]
    return "\n".join(lines)


def is_merger_finalization_comment(body: str) -> bool:
    return MERGER_FINALIZATION_MARKER in (body or "")


def assess_merger_lease_finalization(
    comments: list[dict],
    *,
    pr_number: int,
    session: dict[str, Any] | None,
    actor_identity: str,
    actor_profile: str,
    actor_session_id: str | None,
    repo: str,
    worktree: str,
    candidate_head: str | None,
    live_head_sha: str | None = None,
    outcome: str = OUTCOME_RELEASED,
    reason: str | None = None,
    runtime_token_fingerprint: str | None = None,
    issue_number: int | None = None,
    target_branch: str = "master",
    target_branch_sha: str | None = None,
    now: datetime | None = None,
) -> dict[str, Any]:
    """Decide whether a merger may terminally finalize its own lease (#742).

    Owner-session only: the caller must hold the exact comment-backed merger
    lease it is finalizing. Foreign sessions, reviewer profiles, and mismatched
    repository/PR/head/token-fingerprint callers fail closed. Already-terminal
    leases return ``already_terminal`` so repeat calls are idempotent and post
    no second marker.
    """
    now = now or datetime.now(timezone.utc)
    reasons: list[str] = []
    outcome = (outcome or "").strip().lower()
    finalization_reason = (reason or "").strip() or DEFAULT_MERGER_FINALIZATION_REASON

    if outcome not in MERGER_FINALIZATION_OUTCOMES:
        reasons.append(
            f"outcome '{outcome or 'none'}' is not a terminal merger "
            f"finalization outcome ({sorted(MERGER_FINALIZATION_OUTCOMES)})"
        )

    if "merger" not in (actor_profile or "").lower():
        reasons.append(
            f"profile '{actor_profile or 'unknown'}' is not a merger profile; "
            "merger lease finalization is merger-only (fail closed). Reviewer "
            "sessions use gitea_release_reviewer_pr_lease."
        )

    session = session or None
    provenance = (session or {}).get("lease_provenance") or {}
    if not isinstance(provenance, dict):
        provenance = {}
    source = (provenance.get("source") or "").strip()

    if not session:
        reasons.append(
            "no in-session merger lease recorded; only the owning session may "
            "finalize a merger lease"
        )
    elif source not in MERGER_OWNED_PROVENANCE_SOURCES:
        reasons.append(
            f"in-session lease provenance '{source or 'none'}' is not a "
            "merger-owned lease; refusing to finalize"
        )
    elif not is_sanctioned_session_lease(session):
        reasons.extend(
            assess_acquired_merger_lease_integrity(session)
            if source == SOURCE_ACQUIRE_MERGER
            else ["in-session merger lease lacks sanctioned provenance"]
        )

    pinned = leases._normalize_sha(candidate_head)
    live = leases._normalize_sha(live_head_sha)
    if not pinned:
        reasons.append(
            "candidate_head is required for merger lease finalization "
            "(exact-head scoping; fail closed)"
        )
    if live and pinned and live != pinned:
        reasons.append(
            "candidate_head does not match live PR head (fail closed)"
        )

    if session:
        session_sid = (session.get("session_id") or "").strip()
        actor_sid = (actor_session_id or "").strip()
        if not actor_sid or session_sid != actor_sid:
            reasons.append(
                "session_id does not match the in-session merger lease owner; "
                "foreign-session release is not permitted (fail closed)"
            )
        if session.get("pr_number") != pr_number:
            reasons.append(
                f"in-session merger lease is for PR #{session.get('pr_number')}, "
                f"not #{pr_number}"
            )
        session_repo = (session.get("repo") or "").strip()
        if session_repo and session_repo != (repo or "").strip():
            reasons.append(
                f"in-session merger lease repository '{session_repo}' does not "
                f"match '{repo}' (fail closed)"
            )
        session_head = leases._normalize_sha(session.get("candidate_head"))
        if pinned and session_head and session_head != pinned:
            reasons.append(
                "in-session merger lease candidate_head does not match the "
                "supplied candidate_head (fail closed)"
            )
        session_identity = (session.get("reviewer_identity") or "").strip()
        if session_identity and session_identity != (actor_identity or "").strip():
            reasons.append(
                "authenticated identity does not match the in-session merger "
                "lease holder (fail closed)"
            )
        session_profile = (session.get("profile") or "").strip()
        if session_profile and session_profile != (actor_profile or "").strip():
            reasons.append(
                "active profile does not match the in-session merger lease "
                "profile (fail closed)"
            )
        recorded_fingerprint = (
            provenance.get("native_token_fingerprint") or ""
        ).strip()
        live_fingerprint = (runtime_token_fingerprint or "").strip()
        if (
            recorded_fingerprint
            and live_fingerprint
            and recorded_fingerprint != live_fingerprint
        ):
            reasons.append(
                "native runtime token fingerprint does not match the one "
                "recorded when the merger lease was acquired (fail closed)"
            )

    lease_comment_id = (session or {}).get("comment_id") or provenance.get("comment_id")
    entries = leases._lease_entries(comments, pr_number=pr_number)
    if session and lease_comment_id is not None:
        if not any(entry.get("comment_id") == lease_comment_id for entry in entries):
            reasons.append(
                f"lease marker comment {lease_comment_id} is not present on PR "
                f"#{pr_number} (fail closed)"
            )

    active = leases.find_active_reviewer_lease(comments, pr_number=pr_number, now=now)
    already_terminal = False
    if active:
        owner_session = (active.get("session_id") or "").strip()
        if owner_session and owner_session != (actor_session_id or "").strip():
            reasons.append(
                f"active PR lease is owned by session_id={owner_session}; a "
                "merger may only finalize its own lease (fail closed)"
            )
    else:
        newest = entries[-1] if entries else None
        newest_phase = ((newest or {}).get("phase") or "").strip().lower()
        if newest and newest_phase in leases._TERMINAL_PHASES:
            already_terminal = True
        elif not entries:
            reasons.append(
                f"no comment-backed lease marker found on PR #{pr_number}"
            )

    finalize_allowed = not reasons and not already_terminal
    body = None
    if finalize_allowed and session:
        body = format_merger_finalization_body(
            repo=(session.get("repo") or repo),
            pr_number=pr_number,
            issue_number=(
                issue_number
                if issue_number is not None
                else session.get("issue_number")
            ),
            merger_identity=actor_identity,
            merger_profile=actor_profile,
            merger_session_id=(actor_session_id or ""),
            worktree=worktree or (session.get("worktree") or ""),
            candidate_head=pinned,
            target_branch=(
                session.get("target_branch") or target_branch or "master"
            ),
            target_branch_sha=(
                session.get("target_branch_sha") or target_branch_sha
            ),
            outcome=outcome,
            reason=finalization_reason,
            lease_comment_id=lease_comment_id,
            finalized_at=now,
        )

    return {
        "finalize_allowed": finalize_allowed,
        "already_terminal": already_terminal,
        "reasons": reasons,
        "outcome": outcome,
        "finalization_reason": finalization_reason,
        "active_lease": active,
        "finalization_body": body,
        "lease_comment_id": lease_comment_id,
        "candidate_head": pinned,
    }


def assess_adopt_merger_lease(
    comments: list[dict],
    *,
    pr_number: int,
    adopter_identity: str,
    adopter_profile: str,
    adopter_session_id: str,
    repo: str,
    issue_number: int | None,
    worktree: str,
    expected_head_sha: str | None,
    live_head_sha: str | None,
    approval_at_head: bool,
    pr_open: bool = True,
    now: datetime | None = None,
) -> dict[str, Any]:
    """Decide whether a merger may adopt the active reviewer lease (#536)."""
    now = now or datetime.now(timezone.utc)
    reasons: list[str] = []
    pinned = leases._normalize_sha(expected_head_sha)
    live = leases._normalize_sha(live_head_sha)

    if not pr_open:
        reasons.append(
            f"PR #{pr_number} is not open; merger lease adoption is only for open PRs"
        )
    if not approval_at_head:
        reasons.append(
            "formal APPROVED review at the current PR head is required before "
            "merger lease adoption (fail closed)"
        )
    if not pinned:
        reasons.append("expected_head_sha is required for merger lease adoption")
    if not live:
        reasons.append("live PR head SHA unavailable (fail closed)")
    elif pinned and live and pinned != live:
        reasons.append(
            "expected_head_sha does not match live PR head; refresh approval before adoption"
        )

    active = leases.find_active_reviewer_lease(
        comments, pr_number=pr_number, now=now
    )
    if not active:
        reasons.append(
            f"no active reviewer lease on PR #{pr_number} to adopt; reviewer "
            "must acquire first"
        )
    else:
        owner_session = (active.get("session_id") or "").strip()
        freshness = active.get("freshness") or leases.classify_lease_freshness(
            active, now=now
        )
        if freshness not in _MERGER_ADOPTABLE_FRESHNESS:
            reasons.append(
                f"active reviewer lease freshness is '{freshness}'; explicit "
                "reclaim is not implemented (fail closed)"
            )
        lease_head = active.get("candidate_head")
        if lease_head and live and lease_head != live:
            reasons.append(
                "active reviewer lease candidate_head differs from live PR head"
            )
        if owner_session and owner_session == adopter_session_id:
            # Same session already holds the thread lease — allow idempotent adopt
            # only when the newest entry is not yet an adoption by this session.
            entries = leases._lease_entries(comments, pr_number=pr_number)
            newest = entries[-1] if entries else None
            if newest and (newest.get("phase") or "") == "adopted":
                reasons.append(
                    "merger session already recorded an adoption lease on this PR"
                )
        elif owner_session and owner_session != adopter_session_id:
            # Cross-session merger handoff: adopt the foreign reviewer lease.
            pass

    if not (adopter_identity or "").strip():
        reasons.append("adopter identity required")
    if not (adopter_session_id or "").strip():
        reasons.append("adopter session_id required")
    if not (worktree or "").strip():
        reasons.append("merger worktree path required")
    if "merger" not in (adopter_profile or "").lower():
        reasons.append(
            f"profile '{adopter_profile}' is not a merger profile; adoption is "
            "merger-only (fail closed)"
        )

    adopt_allowed = not reasons
    adoption_body = None
    if adopt_allowed and active:
        adoption_body = format_adoption_body(
            repo=repo,
            pr_number=pr_number,
            issue_number=issue_number or active.get("issue_number"),
            adopter_identity=adopter_identity,
            adopter_profile=adopter_profile,
            adopter_session_id=adopter_session_id,
            worktree=worktree,
            candidate_head=live,
            target_branch=active.get("target_branch") or "master",
            target_branch_sha=active.get("target_branch_sha"),
            adopted_from_session_id=active.get("session_id") or "",
            adopted_from_profile=active.get("profile") or "unknown",
            adopted_from_reviewer_identity=active.get("reviewer_identity") or "",
            adopted_from_comment_id=active.get("comment_id"),
            adopted_at=now,
        )

    return {
        "adopt_allowed": adopt_allowed,
        "reasons": reasons,
        "active_lease": active,
        "adoption_body": adoption_body,
        "adopter_session_id": adopter_session_id,
        "expected_head_sha": pinned,
        "live_head_sha": live,
    }