"""
Agent market integration review
===============================

Turns a read-only market watch signal into an operator-reviewable integration
decision. This service does not install SDKs, call LLMs, execute tools, approve
shadow/canary, or mutate production routing.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any


def run_agent_market_integration_review(
    *,
    watch_report: dict[str, Any],
    candidate_registry: dict[str, Any],
    scorecard: dict[str, Any],
    review_scope: str = "actionable",
    generated_at: str | None = None,
) -> dict[str, Any]:
    """Build the monthly/triggered integration review from market watch output."""
    if watch_report.get("schema_version") != "agent_market_watch_report_v1":
        raise ValueError("watch_report must be agent_market_watch_report_v1")
    if review_scope not in {"changed", "actionable", "all"}:
        raise ValueError("review_scope must be 'changed', 'actionable', or 'all'")

    registry_by_id = {
        str(candidate.get("candidate_id")): candidate
        for candidate in candidate_registry.get("candidates") or []
        if candidate.get("candidate_id")
    }
    scorecard_by_id = {
        str(candidate.get("candidate_id")): candidate
        for candidate in scorecard.get("candidates") or []
        if candidate.get("candidate_id")
    }

    reviews = [
        _review_candidate(
            candidate,
            registry_by_id.get(str(candidate.get("candidate_id")), {}),
            scorecard_by_id.get(str(candidate.get("candidate_id")), {}),
        )
        for candidate in watch_report.get("candidates") or []
        if _candidate_in_scope(candidate, review_scope)
    ]

    return {
        "schema_version": "agent_market_integration_review_v1",
        "generated_at": generated_at or datetime.now(timezone.utc).isoformat(),  # noqa: UP017
        "inputs": {
            "watch_report_generated_at": watch_report.get("generated_at"),
            "watch_report_mode": watch_report.get("mode"),
            "watch_summary": dict(watch_report.get("summary") or {}),
            "candidate_registry_schema_version": str(candidate_registry.get("schema_version", "")),
            "scorecard_schema_version": str(scorecard.get("schema_version", "")),
            "scorecard_scoring_version": str(scorecard.get("scoring_version", "")),
            "review_scope": review_scope,
        },
        "policy": {
            "production_changes_approved": False,
            "replacement_decision_allowed": False,
            "sdk_installation_approved": False,
            "paid_api_calls_approved": False,
            "shadow_or_canary_approved": False,
            "raw_external_pages_committed": False,
        },
        "summary": _summary(reviews, watch_report),
        "reviews": reviews,
    }


def _candidate_in_scope(candidate: dict[str, Any], review_scope: str) -> bool:
    if review_scope == "all":
        return True
    if bool(candidate.get("changed")):
        return True
    if review_scope == "actionable":
        return any(source.get("error") for source in candidate.get("sources") or [])
    return False


def _review_candidate(
    watch_candidate: dict[str, Any],
    registry_candidate: dict[str, Any],
    scorecard_candidate: dict[str, Any],
) -> dict[str, Any]:
    candidate_id = str(watch_candidate.get("candidate_id", "")).strip()
    changed_sources = [
        _changed_source(source)
        for source in watch_candidate.get("sources") or []
        if source.get("changed_since_reference") or source.get("error")
    ]
    readiness = _readiness(candidate_id, registry_candidate)
    decision = _decision(readiness)
    recommendations = _recommendations(
        readiness=readiness,
        watch_candidate=watch_candidate,
        registry_candidate=registry_candidate,
    )
    return {
        "candidate_id": candidate_id,
        "display_name": str(
            watch_candidate.get("display_name")
            or registry_candidate.get("display_name")
            or candidate_id
        ),
        "market_watch": {
            "decision": str(watch_candidate.get("decision", "")),
            "recommended_actions": list(watch_candidate.get("recommended_actions") or []),
            "changed_sources": changed_sources,
        },
        "market_score": _market_score(scorecard_candidate),
        "registry_status": _registry_status(registry_candidate),
        "approval_boundary": {
            "requires_cost_approval": bool(watch_candidate.get("requires_cost_approval", False)),
            "requires_dependency_approval": bool(
                watch_candidate.get("requires_dependency_approval", False)
            ),
            "approved_for_sdk_install": False,
            "approved_for_paid_api_calls": False,
            "approved_for_shadow_or_canary": False,
        },
        "readiness": readiness,
        "decision": decision,
        "recommendations": recommendations,
        "unblock_conditions": _unblock_conditions(readiness, watch_candidate),
    }


def _changed_source(source: dict[str, Any]) -> dict[str, Any]:
    return {
        "source_id": str(source.get("source_id", "")),
        "type": str(source.get("type", "")),
        "url": str(source.get("url", "")),
        "status": str(source.get("status", "")),
        "http_status": source.get("http_status"),
        "version": source.get("version"),
        "published_at": source.get("published_at"),
        "content_hash": source.get("content_hash"),
        "error": source.get("error"),
        "change_basis": "version_or_content_hash_changed",
    }


def _market_score(scorecard_candidate: dict[str, Any]) -> dict[str, Any]:
    if not scorecard_candidate:
        return {
            "known": False,
            "rank": None,
            "total_score": None,
            "replay_priority": "refresh_scorecard_required",
            "beats_baseline_capability": None,
            "strengths": [],
            "gaps": [],
            "risks": ["candidate missing from current market scorecard"],
        }
    return {
        "known": True,
        "rank": scorecard_candidate.get("rank"),
        "total_score": scorecard_candidate.get("total_score"),
        "replay_priority": scorecard_candidate.get("replay_priority"),
        "beats_baseline_capability": scorecard_candidate.get("beats_baseline_capability"),
        "strengths": list(scorecard_candidate.get("strengths") or []),
        "gaps": list(scorecard_candidate.get("gaps") or []),
        "risks": list(scorecard_candidate.get("risks") or []),
    }


def _registry_status(registry_candidate: dict[str, Any]) -> dict[str, Any]:
    return {
        "role": registry_candidate.get("role"),
        "evaluation_priority": registry_candidate.get("evaluation_priority"),
        "required_stage": registry_candidate.get("required_stage"),
        "current_decision": registry_candidate.get("current_decision"),
        "next_variant_id": registry_candidate.get("next_variant_id"),
        "next_variant_stage": registry_candidate.get("next_variant_stage"),
        "latest_replay_summary": registry_candidate.get("latest_replay_summary"),
        "latest_smoke_model": registry_candidate.get("latest_smoke_model"),
        "latest_smoke_gate": registry_candidate.get("latest_smoke_gate"),
        "latest_smoke_matrix": registry_candidate.get("latest_smoke_matrix"),
    }


def _readiness(candidate_id: str, registry_candidate: dict[str, Any]) -> dict[str, Any]:
    current_decision = str(registry_candidate.get("current_decision", ""))
    evaluation_priority = str(registry_candidate.get("evaluation_priority", ""))
    required_stage = str(registry_candidate.get("required_stage", ""))
    latest_smoke_matrix = registry_candidate.get("latest_smoke_matrix")
    latest_replay_summary = registry_candidate.get("latest_replay_summary")
    if evaluation_priority == "watch_only" or required_stage == "watch_only_primary_source_monitoring":
        return {
            "stage": "watch_only_primary_source_monitoring",
            "reason": "Candidate is approved only for primary-source market monitoring, not replay or integration.",
            "allowed_next_gate": "manual_primary_source_review_then_watch_registry_baseline",
        }
    if candidate_id == "nemo_nemotron_fabric" and (
        "blocked" in current_decision or latest_smoke_matrix
    ):
        return {
            "stage": "blocked_existing_replay_evidence",
            "reason": "Nemotron smoke/replay evidence blocks full replay, shadow, and canary.",
            "allowed_next_gate": "refresh_source_evidence_then_5_record_smoke_only",
        }
    if latest_replay_summary:
        return {
            "stage": "has_offline_replay_summary",
            "reason": "Candidate has an offline replay summary and must re-enter promotion gate after evidence refresh.",
            "allowed_next_gate": "refresh_scorecard_then_offline_replay_or_promotion_gate",
        }
    return {
        "stage": "not_yet_replayed",
        "reason": "Candidate has no AWOOOI offline replay evidence yet.",
        "allowed_next_gate": "create_no_sdk_no_api_adapter_then_offline_replay",
    }


def _decision(readiness: dict[str, Any]) -> str:
    stage = readiness.get("stage")
    if stage == "blocked_existing_replay_evidence":
        return "do_not_integrate_refresh_evidence_then_smoke_gate"
    if stage == "watch_only_primary_source_monitoring":
        return "do_not_integrate_watch_only_primary_source_monitoring"
    if stage == "not_yet_replayed":
        return "do_not_integrate_prepare_no_cost_offline_adapter"
    return "do_not_integrate_refresh_replay_gate"


def _recommendations(
    *,
    readiness: dict[str, Any],
    watch_candidate: dict[str, Any],
    registry_candidate: dict[str, Any],
) -> list[str]:
    recommendations = [
        "refresh_market_capability_evidence_from_changed_primary_sources",
        "do_not_replace_openclaw_from_market_watch_signal",
        "do_not_enter_shadow_or_canary_without_offline_replay_promotion_gate",
    ]
    stage = readiness.get("stage")
    if stage == "blocked_existing_replay_evidence":
        recommendations.extend(
            [
                "keep_candidate_as_offline_specialist_or_evaluator",
                "rerun_only_5_record_smoke_after_a_specific_runtime_or_model_hypothesis",
                "do_not_run_full_50_replay_until_smoke_gate_passes",
            ]
        )
    elif stage == "watch_only_primary_source_monitoring":
        recommendations.extend(
            [
                "keep_candidate_in_watch_registry_only",
                "do_not_build_replay_adapter_until_operator_promotes_candidate_priority",
                "refresh_watch_baseline_after_primary_source_review",
            ]
        )
    elif stage == "not_yet_replayed":
        recommendations.extend(
            [
                "build_no_sdk_no_api_contract_adapter_first",
                "request_cost_and_dependency_approval_before_official_sdk_or_paid_api_use",
                "run_50_record_offline_replay_before_any_production_role",
            ]
        )
    else:
        recommendations.append("rerun_same_contract_offline_replay_before_promotion_gate")

    if watch_candidate.get("requires_cost_approval"):
        recommendations.append("cost_boundary_review_required")
    if watch_candidate.get("requires_dependency_approval"):
        recommendations.append("dependency_boundary_review_required")
    if registry_candidate.get("role"):
        recommendations.append(f"candidate_role_scope:{registry_candidate['role']}")
    return recommendations


def _unblock_conditions(
    readiness: dict[str, Any],
    watch_candidate: dict[str, Any],
) -> list[str]:
    conditions = [
        "changed_sources_reviewed_by_operator",
        "market_scorecard_refreshed_if_primary_sources_changed_semantically",
        "no_sdk_install_without_dependency_approval",
        "no_paid_provider_use_without_cost_and_data_boundary_approval",
    ]
    stage = readiness.get("stage")
    if stage == "blocked_existing_replay_evidence":
        conditions.extend(
            [
                "5_record_smoke_gate_passes",
                "latency_and_output_contract_blockers_resolved",
            ]
        )
    elif stage == "watch_only_primary_source_monitoring":
        conditions.extend(
            [
                "operator_confirms_primary_sources",
                "watch_registry_baseline_refreshed",
                "explicit_priority_upgrade_before_replay",
            ]
        )
    else:
        conditions.extend(
            [
                "offline_adapter_contract_valid",
                "50_record_hidden_label_replay_beats_openclaw_baseline",
            ]
        )
    if watch_candidate.get("requires_cost_approval"):
        conditions.append("cost_approval_recorded")
    return conditions


def _summary(reviews: list[dict[str, Any]], watch_report: dict[str, Any]) -> dict[str, int]:
    return {
        "reviewed_candidates": len(reviews),
        "blocked_from_integration": len(reviews),
        "requires_cost_approval": sum(
            1 for review in reviews if review["approval_boundary"]["requires_cost_approval"]
        ),
        "requires_dependency_approval": sum(
            1 for review in reviews if review["approval_boundary"]["requires_dependency_approval"]
        ),
        "source_failures": int((watch_report.get("summary") or {}).get("failure_count", 0)),
        "production_changes_approved": 0,
        "shadow_or_canary_approved": 0,
    }
