"""Reconciler profile model for already-landed PR closure (#304).

Defines the narrowly scoped operation set for a dedicated reconciler profile
such as ``prgs-reconciler``. Close MCP tooling and ancestry gates ship in the
#310 stack; this module validates profile shape only.
"""

from __future__ import annotations

import gitea_config

RECONCILER_REQUIRED_OPERATIONS = (
    "gitea.read",
    "gitea.pr.close",
)

RECONCILER_RECOMMENDED_OPERATIONS = (
    "gitea.pr.comment",
    "gitea.issue.comment",
    "gitea.issue.close",
    # Merged-branch cleanup is reconciler-owned (task_capability_map maps
    # cleanup_merged_pr_branch -> reconciler). The permission is only
    # exercisable through the guarded gitea_cleanup_merged_pr_branch path
    # (#514): merged proof, protected-branch refusal, explicit confirmation.
    "gitea.branch.delete",
)

RECONCILER_FORBIDDEN_OPERATIONS = (
    "gitea.pr.approve",
    "gitea.pr.merge",
    "gitea.pr.review",
    "gitea.pr.create",
    "gitea.branch.push",
    "gitea.repo.commit",
)


def _normalized_allowed(allowed: list[str]) -> set[str]:
    normalized: set[str] = set()
    for entry in allowed or []:
        try:
            normalized.add(gitea_config.normalize_operation(entry))
        except gitea_config.ConfigError:
            continue
    return normalized


def _forbidden_in_allowed(allowed: list[str], ops: tuple[str, ...]) -> list[str]:
    """Return forbidden ops that are explicitly listed in *allowed*."""
    allowed_n = _normalized_allowed(allowed)
    present: list[str] = []
    for op in ops:
        try:
            if gitea_config.normalize_operation(op) in allowed_n:
                present.append(op)
        except gitea_config.ConfigError:
            continue
    return present


def is_reconciler_profile(allowed: list[str], forbidden: list[str]) -> bool:
    """Return True when *allowed*/*forbidden* describe a reconciler profile."""
    def can(op: str) -> bool:
        return gitea_config.check_operation(op, allowed, forbidden)[0]

    if not can("gitea.pr.close"):
        return False
    if _forbidden_in_allowed(allowed, RECONCILER_FORBIDDEN_OPERATIONS):
        return False
    return True


def assess_reconciler_profile(allowed: list[str], forbidden: list[str]) -> dict:
    """Validate reconciler profile operations (read-only, fail closed)."""
    allowed = list(allowed or [])
    forbidden = list(forbidden or [])
    reasons: list[str] = []

    for op in RECONCILER_REQUIRED_OPERATIONS:
        ok, _ = gitea_config.check_operation(op, allowed, forbidden)
        if not ok:
            reasons.append(f"missing required operation {op}")

    for op in _forbidden_in_allowed(allowed, RECONCILER_FORBIDDEN_OPERATIONS):
        reasons.append(f"forbidden operation must not be allowed: {op}")

    missing_recommended = [
        op for op in RECONCILER_RECOMMENDED_OPERATIONS
        if not gitea_config.check_operation(op, allowed, forbidden)[0]
    ]

    return {
        "is_reconciler_profile": is_reconciler_profile(allowed, forbidden),
        "valid": not reasons and is_reconciler_profile(allowed, forbidden),
        "reasons": reasons,
        "missing_recommended_operations": missing_recommended,
        "required_operations": list(RECONCILER_REQUIRED_OPERATIONS),
        "forbidden_operations": list(RECONCILER_FORBIDDEN_OPERATIONS),
    }