"""
Agent market discovery review
=============================

Turns raw discovery search results from the market watch into a manual intake
queue. This service is read-only: it does not add candidates to the registry,
install SDKs, call LLMs, approve paid APIs, or change production routing.
"""

from __future__ import annotations

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


def run_agent_market_discovery_review(
    *,
    watch_report: dict[str, Any],
    candidate_registry: dict[str, Any],
    source_registry: dict[str, Any],
    previous_review: dict[str, Any] | None = None,
    generated_at: str | None = None,
) -> dict[str, Any]:
    """Build a read-only candidate-intake review from discovery results."""
    if watch_report.get("schema_version") != "agent_market_watch_report_v1":
        raise ValueError("watch_report must be agent_market_watch_report_v1")

    known_repositories = _known_repositories(candidate_registry, source_registry)
    previous_repositories = _previous_repositories(previous_review or {})
    drafts = _candidate_drafts(
        watch_report=watch_report,
        known_repositories=known_repositories,
        previous_repositories=previous_repositories,
    )
    return {
        "schema_version": "agent_market_discovery_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"),
            "candidate_registry_schema_version": str(candidate_registry.get("schema_version", "")),
            "source_registry_schema_version": str(source_registry.get("schema_version", "")),
            "previous_review_generated_at": (previous_review or {}).get("generated_at"),
        },
        "policy": {
            "auto_registry_addition_approved": False,
            "sdk_installation_approved": False,
            "paid_api_calls_approved": False,
            "production_changes_approved": False,
            "shadow_or_canary_approved": False,
            "replacement_decision_allowed": False,
        },
        "summary": _summary(watch_report, drafts),
        "candidate_drafts": drafts,
    }


def _candidate_drafts(
    *,
    watch_report: dict[str, Any],
    known_repositories: set[str],
    previous_repositories: set[str],
) -> list[dict[str, Any]]:
    merged: dict[str, dict[str, Any]] = {}
    for discovery in watch_report.get("new_candidate_discovery") or []:
        source_id = str(discovery.get("source_id", ""))
        for item in discovery.get("items") or []:
            full_name = _normalize_repo_name(item.get("full_name"))
            if not full_name:
                continue
            draft = merged.setdefault(
                full_name,
                {
                    "repository_full_name": full_name,
                    "html_url": str(item.get("html_url") or ""),
                    "source_ids": [],
                    "stargazers_count_max": 0,
                    "updated_at_latest": None,
                },
            )
            if source_id and source_id not in draft["source_ids"]:
                draft["source_ids"].append(source_id)
            stars = _to_int(item.get("stargazers_count"))
            draft["stargazers_count_max"] = max(draft["stargazers_count_max"], stars)
            updated_at = item.get("updated_at")
            if isinstance(updated_at, str) and (
                not draft["updated_at_latest"] or updated_at > draft["updated_at_latest"]
            ):
                draft["updated_at_latest"] = updated_at

    drafts = []
    for full_name, draft in sorted(
        merged.items(),
        key=lambda entry: (-entry[1]["stargazers_count_max"], entry[0]),
    ):
        known = full_name in known_repositories
        seen_before = full_name in previous_repositories
        status = "already_watched_or_registered" if known else "needs_primary_source_classification"
        decision = (
            "keep_existing_candidate_watch"
            if known
            else "manual_primary_source_classification_required"
        )
        next_gate = (
            "use_existing_market_watch_candidate"
            if known
            else "classify_official_sources_then_update_watch_registry"
        )
        drafts.append(
            {
                **draft,
                "status": status,
                "seen_before": seen_before,
                "new_since_previous_review": not seen_before,
                "decision": decision,
                "recommended_next_gate": next_gate,
                "approval_boundary": {
                    "approved_for_registry_addition": False,
                    "approved_for_sdk_install": False,
                    "approved_for_paid_api_calls": False,
                    "approved_for_shadow_or_canary": False,
                },
                "recommended_actions": _recommended_actions(known=known),
            }
        )
    return drafts


def _summary(watch_report: dict[str, Any], drafts: list[dict[str, Any]]) -> dict[str, int]:
    manual = [
        draft
        for draft in drafts
        if draft["status"] == "needs_primary_source_classification"
    ]
    return {
        "discovery_sources": len(watch_report.get("new_candidate_discovery") or []),
        "discovered_items": sum(
            len(discovery.get("items") or [])
            for discovery in watch_report.get("new_candidate_discovery") or []
        ),
        "unique_repositories": len(drafts),
        "already_watched_or_registered": sum(
            1 for draft in drafts if draft["status"] == "already_watched_or_registered"
        ),
        "manual_classification_required": len(manual),
        "new_manual_classification_required": sum(
            1 for draft in manual if draft["new_since_previous_review"]
        ),
        "source_failures": sum(
            1
            for discovery in watch_report.get("new_candidate_discovery") or []
            if discovery.get("error")
        ),
        "auto_registry_additions_approved": 0,
        "production_changes_approved": 0,
        "shadow_or_canary_approved": 0,
    }


def _known_repositories(
    candidate_registry: dict[str, Any],
    source_registry: dict[str, Any],
) -> set[str]:
    known: set[str] = set()
    for candidate in candidate_registry.get("candidates") or []:
        known.update(_extract_github_repositories(str(candidate.get("official_url", ""))))
    for candidate in source_registry.get("candidates") or []:
        for source in candidate.get("sources") or []:
            known.update(_extract_github_repositories(str(source.get("url", ""))))
    return known


def _previous_repositories(previous_review: dict[str, Any]) -> set[str]:
    return {
        _normalize_repo_name(draft.get("repository_full_name"))
        for draft in previous_review.get("candidate_drafts") or []
        if _normalize_repo_name(draft.get("repository_full_name"))
    }


def _extract_github_repositories(url: str) -> set[str]:
    matches = re.findall(
        r"(?:github\.com/|api\.github\.com/repos/)([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)",
        url,
    )
    return {_normalize_repo_name(match) for match in matches if _normalize_repo_name(match)}


def _normalize_repo_name(value: Any) -> str:
    if not isinstance(value, str):
        return ""
    parts = value.strip().strip("/").split("/")
    if len(parts) < 2:
        return ""
    return f"{parts[0]}/{parts[1]}".lower()


def _to_int(value: Any) -> int:
    try:
        return int(value)
    except (TypeError, ValueError):
        return 0


def _recommended_actions(*, known: bool) -> list[str]:
    if known:
        return ["keep_existing_watch_registry_entry", "do_not_duplicate_candidate"]
    return [
        "verify_official_or_primary_sources",
        "classify_role_against_awoooi_agent_taxonomy",
        "add_to_watch_registry_only_after_manual_review",
        "do_not_install_sdk_or_call_provider",
        "do_not_enter_replacement_replay_before_market_scorecard",
    ]
