"""Programmatic autonomy readiness checks for pilot execution gating."""

from __future__ import annotations

import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from .control_plane import control_plane_store
from .control_plane_job_queue import ControlPlaneJobQueue, control_plane_job_queue_db_path
from .event_queue import CrmEventQueue, event_queue_db_path
from .receipts import verify_receipt_chain
from .rollback_coverage import rollback_coverage_snapshot


_MAX_VERIFICATION_SKIP_RATE = 0.20
_CHECK_TELEMETRY_RE = re.compile(
    r"^step:(?P<step_id>[^:]+):(?P<phase>precheck|postcheck):(?P<check_name>[^:]+):(?P<status>pass|skip|fail)$"
)


def _utc_now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _parse_iso(value: Any) -> datetime | None:
    if not isinstance(value, str) or not value.strip():
        return None
    raw = value.strip()
    if raw.endswith("Z"):
        raw = raw[:-1] + "+00:00"
    try:
        parsed = datetime.fromisoformat(raw)
    except Exception:
        return None
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    return parsed.astimezone(timezone.utc)


def _recent_receipts(state_dir: Path, *, limit: int = 50) -> list[dict[str, Any]]:
    receipts_dir = state_dir / "receipts"
    if not receipts_dir.exists():
        return []
    rows: list[tuple[datetime, dict[str, Any]]] = []
    for run_dir in receipts_dir.iterdir():
        if not run_dir.is_dir():
            continue
        path = run_dir / "receipt.json"
        if not path.exists():
            continue
        try:
            payload = json.loads(path.read_text(encoding="utf-8"))
        except Exception:
            continue
        if not isinstance(payload, dict):
            continue
        timestamp = (
            _parse_iso(payload.get("completed_at"))
            or _parse_iso(payload.get("started_at"))
            or datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc)
        )
        rows.append((timestamp, payload))
    rows.sort(key=lambda item: item[0], reverse=True)
    return [payload for _timestamp, payload in rows[: max(1, int(limit))]]


def _is_verified_human_approval(approved_by: Any) -> bool:
    if not isinstance(approved_by, dict):
        return False
    actor = str(approved_by.get("actor") or "").strip()
    if not actor:
        return False
    principal_type = str(approved_by.get("principal_type") or "").strip().lower()
    verified_identity = bool(approved_by.get("verified_identity"))
    channel = str(approved_by.get("channel") or "").strip().lower()
    if principal_type == "user" and verified_identity:
        return True
    # Backward-compat for historical Slack approvals created before principal metadata existed.
    return channel == "slack"


def _verification_coverage_metrics(receipts: list[dict[str, Any]]) -> dict[str, Any]:
    total_checks = 0
    passed_checks = 0
    skipped_checks = 0
    failed_checks = 0
    receipts_with_telemetry = 0

    for payload in receipts:
        decision_path = payload.get("decision_path")
        if not isinstance(decision_path, list):
            continue
        receipt_has_telemetry = False
        for raw in decision_path:
            if not isinstance(raw, str):
                continue
            match = _CHECK_TELEMETRY_RE.match(raw.strip())
            if match is None:
                continue
            receipt_has_telemetry = True
            total_checks += 1
            status = match.group("status")
            if status == "pass":
                passed_checks += 1
            elif status == "skip":
                skipped_checks += 1
            elif status == "fail":
                failed_checks += 1
        if receipt_has_telemetry:
            receipts_with_telemetry += 1

    skip_rate = (skipped_checks / total_checks) if total_checks else None
    return {
        "total_checks": total_checks,
        "passed_checks": passed_checks,
        "skipped_checks": skipped_checks,
        "failed_checks": failed_checks,
        "receipts_with_telemetry": receipts_with_telemetry,
        "skip_rate": skip_rate,
        "max_skip_rate": _MAX_VERIFICATION_SKIP_RATE,
    }


def autonomy_operational_health(
    state_dir: Path,
    *,
    lookback_days: int = 30,
    event_window_hours: int = 24,
    receipts: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
    resolved_state_dir = Path(state_dir).resolve()
    bounded_lookback = max(1, int(lookback_days))
    bounded_event_window = max(1, int(event_window_hours))
    resolved_receipts = receipts if receipts is not None else _recent_receipts(resolved_state_dir, limit=200)

    crm_event_queue = CrmEventQueue(event_queue_db_path(resolved_state_dir))
    crm_queue_health = crm_event_queue.queue_health()
    crm_queue_metrics = crm_event_queue.operational_metrics(window_hours=bounded_event_window)
    control_plane_metrics = control_plane_store(resolved_state_dir).metrics_summary(window_days=bounded_lookback)
    control_plane_job_counts = ControlPlaneJobQueue(control_plane_job_queue_db_path(resolved_state_dir)).queue_counts()
    verification_metrics = _verification_coverage_metrics(resolved_receipts)
    rollback_coverage = rollback_coverage_snapshot()
    total_checks = int(verification_metrics.get("total_checks") or 0)
    failed_checks = int(verification_metrics.get("failed_checks") or 0)
    verification_failure_rate = (failed_checks / total_checks) if total_checks else None

    queue_counts = crm_queue_health.get("counts") if isinstance(crm_queue_health.get("counts"), dict) else {}
    processing = crm_queue_health.get("processing") if isinstance(crm_queue_health.get("processing"), dict) else {}
    issues: list[str] = []
    if int(processing.get("expired_leases") or 0) > 0:
        issues.append("crm_event_queue_expired_leases")
    if int(queue_counts.get("dead_letter") or 0) > 0:
        issues.append("crm_event_dead_letter_present")
    if (
        int(queue_counts.get("pending") or 0) > 0
        and int(processing.get("active_leases") or 0) == 0
        and int(crm_queue_metrics.get("processed_in_window") or 0) == 0
    ):
        issues.append("crm_event_queue_stalled")
    if int(control_plane_job_counts.get("dead_letter") or 0) > 0:
        issues.append("control_plane_job_dead_letter_present")
    if failed_checks > 0:
        issues.append("verification_failures_present")

    return {
        "status": "degraded" if issues else "ok",
        "healthy": not issues,
        "issues": issues,
        "generated_at": _utc_now_iso(),
        "lookback_days": bounded_lookback,
        "event_window_hours": bounded_event_window,
        "crm_event_queue": {
            "health": crm_queue_health,
            "throughput": crm_queue_metrics,
        },
        "control_plane": {
            "job_queue_counts": control_plane_job_counts,
            "summary": control_plane_metrics,
        },
        "verification": {
            **verification_metrics,
            "failure_rate": verification_failure_rate,
        },
        "rollback_coverage": rollback_coverage,
    }


def check_autonomy_readiness(state_dir: Path) -> dict[str, Any]:
    """Run concrete checks required by the Phase 4A autonomy readiness gate."""

    resolved_state_dir = Path(state_dir).resolve()
    checks: dict[str, dict[str, Any]] = {}

    # 1. RBAC enforcement hooks are available and callable.
    try:
        from . import server as server_mod

        resolver = getattr(server_mod, "_resolve_actor_context", None)
        require_roles = getattr(server_mod, "_require_roles", None)
        passed = callable(resolver) and callable(require_roles)
        detail = (
            "RBAC guards active on actor resolver and role gate hooks."
            if passed
            else "RBAC hook imports failed."
        )
    except Exception as exc:
        passed = False
        detail = f"RBAC check failed: {exc}"
    checks["rbac_enforced"] = {"passed": bool(passed), "detail": detail}

    # 2. Maker/checker artifacts are importable.
    try:
        from .contracts import PendingActionV1
        from .pending_actions import load_pending_action

        passed = callable(load_pending_action) and hasattr(PendingActionV1, "model_fields")
        detail = (
            "PendingActionV1 creator persistence and pending loader available."
            if passed
            else "Maker/checker imports unavailable."
        )
    except Exception as exc:
        passed = False
        detail = f"Maker/checker check failed: {exc}"
    checks["maker_checker_enforced"] = {"passed": bool(passed), "detail": detail}

    # 3. Durable control-plane queue can be opened and queried.
    try:
        queue = ControlPlaneJobQueue(control_plane_job_queue_db_path(resolved_state_dir))
        counts = queue.queue_counts()
        processed_count = int(counts.get("succeeded", 0)) + int(counts.get("dead_letter", 0))
        passed = isinstance(counts, dict)
        detail = f"ControlPlaneJobQueue operational, {processed_count} jobs processed."
    except Exception as exc:
        passed = False
        detail = f"Durable jobs check failed: {exc}"
    checks["durable_jobs"] = {"passed": bool(passed), "detail": detail}

    # 4. Recent receipts preserve approver identity field.
    receipts = _recent_receipts(resolved_state_dir, limit=50)
    receipts_with_approved_by_key = [payload for payload in receipts if "approved_by" in payload]
    receipts_with_approver = [
        payload
        for payload in receipts_with_approved_by_key
        if isinstance(payload.get("approved_by"), dict)
        and str((payload.get("approved_by") or {}).get("actor") or "").strip()
    ]
    verified_human_approvals = [
        payload
        for payload in receipts_with_approver
        if _is_verified_human_approval(payload.get("approved_by"))
    ]
    if receipts_with_approved_by_key:
        approver_passed = len(verified_human_approvals) == len(receipts_with_approved_by_key)
        if approver_passed:
            detail = (
                f"Verified human approver identity persisted on {len(verified_human_approvals)}/{len(receipts_with_approved_by_key)} recent approvals."
            )
        else:
            detail = (
                f"Verified human approver identity persisted on {len(verified_human_approvals)}/{len(receipts_with_approved_by_key)} recent approvals; "
                "service or unverified principals remain."
            )
    else:
        approver_passed = False
        detail = "No recent approvals with verified human identity evidence found."
    checks["receipt_approver_identity"] = {"passed": bool(approver_passed), "detail": detail}

    # 5. Recent receipts include enough non-skipped verification telemetry.
    verification_metrics = _verification_coverage_metrics(receipts)
    total_checks = int(verification_metrics.get("total_checks") or 0)
    skip_rate = verification_metrics.get("skip_rate")
    skipped_checks = int(verification_metrics.get("skipped_checks") or 0)
    receipts_with_telemetry = int(verification_metrics.get("receipts_with_telemetry") or 0)
    if total_checks <= 0 or skip_rate is None:
        verification_passed = False
        detail = "No recorded precheck/postcheck telemetry found in recent receipts."
    else:
        verification_passed = float(skip_rate) <= _MAX_VERIFICATION_SKIP_RATE
        detail = (
            f"Skipped {skipped_checks}/{total_checks} recorded verification checks across "
            f"{receipts_with_telemetry} recent receipt(s) "
            f"({float(skip_rate):.0%}, max {_MAX_VERIFICATION_SKIP_RATE:.0%})."
        )
        if not verification_passed:
            detail = "Verification coverage too low. " + detail
    checks["verification_coverage"] = {
        "passed": bool(verification_passed),
        "detail": detail,
        **verification_metrics,
    }

    # 6. Receipt chain integrity verifies cleanly.
    try:
        chain_status = verify_receipt_chain(resolved_state_dir)
        chain_ok = bool(chain_status.get("ok"))
        checked = int(chain_status.get("checked") or 0)
        if chain_ok:
            detail = f"Receipt chain verified across {checked} receipt(s)."
        else:
            failure_count = len(chain_status.get("failures") or [])
            detail = (
                f"Receipt chain verification failed with {failure_count} issue(s) "
                f"across {checked} receipt(s)."
            )
    except Exception as exc:
        chain_ok = False
        chain_status = {}
        detail = f"Receipt chain check failed: {exc}"
    checks["receipt_chain_integrity"] = {
        "passed": bool(chain_ok),
        "detail": detail,
        **chain_status,
    }

    # 7. Support bundle export function is importable and callable.
    try:
        from .support_bundle import build_support_bundle

        passed = callable(build_support_bundle)
        detail = "build_support_bundle callable." if passed else "build_support_bundle not callable."
    except Exception as exc:
        passed = False
        detail = f"Support bundle check failed: {exc}"
    checks["support_bundle_available"] = {"passed": bool(passed), "detail": detail}

    ready = all(bool(entry.get("passed")) for entry in checks.values())
    operational_health = autonomy_operational_health(
        resolved_state_dir,
        lookback_days=30,
        event_window_hours=24,
        receipts=receipts,
    )
    rollback_coverage = rollback_coverage_snapshot()
    return {
        "ready": ready,
        "checked_at": _utc_now_iso(),
        "checks": checks,
        "operational_health": operational_health,
        "rollback_coverage": rollback_coverage,
    }


__all__ = ["autonomy_operational_health", "check_autonomy_readiness"]

