"""
Agent market discovery classifier
=================================

Classifies manually reviewed discovery repositories from primary GitHub
metadata. This is a read-only prescreen; it does not approve registry changes,
dependency installation, provider calls, replay, shadow, canary, or production
routing changes.
"""

from __future__ import annotations

from collections import Counter
from datetime import datetime, timezone
from typing import Any


def run_agent_market_discovery_classification(
    *,
    discovery_review: dict[str, Any],
    repository_metadata: dict[str, dict[str, Any]],
    generated_at: str | None = None,
) -> dict[str, Any]:
    """Classify unknown discovery repositories into next-review buckets."""
    if discovery_review.get("schema_version") != "agent_market_discovery_review_v1":
        raise ValueError("discovery_review must be agent_market_discovery_review_v1")

    candidates = [
        _classify_draft(draft, repository_metadata.get(draft["repository_full_name"], {}))
        for draft in discovery_review.get("candidate_drafts") or []
        if draft.get("status") == "needs_primary_source_classification"
    ]
    classification_counts = Counter(candidate["classification"] for candidate in candidates)
    recommendation_counts = Counter(candidate["recommendation"] for candidate in candidates)
    return {
        "schema_version": "agent_market_discovery_classification_v1",
        "generated_at": generated_at or datetime.now(timezone.utc).isoformat(),  # noqa: UP017
        "inputs": {
            "discovery_review_generated_at": discovery_review.get("generated_at"),
            "metadata_source": "github_repository_api_summary",
        },
        "policy": {
            "auto_watch_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,
            "raw_external_pages_committed": False,
        },
        "summary": {
            "classified_repositories": len(candidates),
            "recommended_watch_additions": sum(
                1 for candidate in candidates if candidate["watch_addition_recommended"]
            ),
            "watch_only_or_defer": sum(
                1 for candidate in candidates if not candidate["watch_addition_recommended"]
            ),
            "classification_counts": dict(sorted(classification_counts.items())),
            "recommendation_counts": dict(sorted(recommendation_counts.items())),
            "production_changes_approved": 0,
            "shadow_or_canary_approved": 0,
        },
        "candidates": candidates,
    }


def _classify_draft(
    draft: dict[str, Any],
    metadata: dict[str, Any],
) -> dict[str, Any]:
    repo = str(draft.get("repository_full_name", ""))
    text = _metadata_text(repo, metadata)
    classification = _classification(text)
    recommendation = _recommendation(classification)
    return {
        "repository_full_name": repo,
        "html_url": str(metadata.get("html_url") or draft.get("html_url") or ""),
        "homepage": metadata.get("homepage"),
        "description": metadata.get("description"),
        "topics": list(metadata.get("topics") or []),
        "language": metadata.get("language"),
        "stargazers_count": _to_int(
            metadata.get("stargazers_count", draft.get("stargazers_count_max"))
        ),
        "pushed_at": metadata.get("pushed_at"),
        "archived": bool(metadata.get("archived", False)),
        "classification": classification,
        "recommended_role": _recommended_role(classification),
        "recommendation": recommendation,
        "watch_addition_recommended": recommendation
        == "add_to_watch_registry_after_manual_source_review",
        "risk_flags": _risk_flags(text, metadata),
        "approval_boundary": {
            "approved_for_watch_registry_addition": False,
            "approved_for_sdk_install": False,
            "approved_for_paid_api_calls": False,
            "approved_for_replay": False,
            "approved_for_shadow_or_canary": False,
        },
        "required_next_gate": _required_next_gate(recommendation),
    }


def _classification(text: str) -> str:
    if _has_any(text, ["powerpoint", "presentation", "pptx", "slides"]):
        return "vertical_product_not_core_agent"
    if _has_any(text, ["governance", "policy", "owasp", "zero-trust", "audit-grade"]):
        return "agent_governance_candidate"
    if _has_any(text, ["web-ui", "dashboard", "cowork app", "chat-ui"]):
        return "agent_operator_console_candidate"
    if _has_any(
        text,
        [
            "agent-framework",
            "agent harness",
            "orchestrator",
            "multi-agent",
            "deep agents",
            "pydantic ai",
            "runtime tool",
            "agent teams",
            "mcp",
        ],
    ):
        return "agent_framework_candidate"
    if _has_any(text, ["hermes-agent", "openclaw", "codex", "claude-code"]):
        return "personal_agent_platform_candidate"
    return "needs_manual_research"


def _recommendation(classification: str) -> str:
    if classification in {
        "agent_framework_candidate",
        "agent_governance_candidate",
        "personal_agent_platform_candidate",
    }:
        return "add_to_watch_registry_after_manual_source_review"
    if classification == "agent_operator_console_candidate":
        return "watch_only_product_surface_signal"
    if classification == "vertical_product_not_core_agent":
        return "defer_not_core_agent_framework"
    return "manual_research_before_watch_registry"


def _recommended_role(classification: str) -> str:
    return {
        "agent_framework_candidate": "agent_framework_or_orchestrator_candidate",
        "agent_governance_candidate": "agent_governance_policy_evaluator_candidate",
        "personal_agent_platform_candidate": "personal_agent_platform_candidate",
        "agent_operator_console_candidate": "operator_console_or_agent_ui_candidate",
        "vertical_product_not_core_agent": "vertical_product_signal_not_openclaw_replacement",
        "needs_manual_research": "manual_research_required",
    }.get(classification, "manual_research_required")


def _risk_flags(text: str, metadata: dict[str, Any]) -> list[str]:
    flags = ["requires_dependency_boundary_review"]
    if _has_any(text, ["openai", "anthropic", "claude", "gemini"]):
        flags.append("likely_requires_paid_provider_boundary_review")
    if _has_any(text, ["sandbox", "shell", "cli", "headless", "tool-calling", "mcp"]):
        flags.append("requires_tool_execution_sandbox_review")
    if bool(metadata.get("archived", False)):
        flags.append("archived_repository")
    return flags


def _required_next_gate(recommendation: str) -> str:
    if recommendation == "add_to_watch_registry_after_manual_source_review":
        return "operator_confirms_primary_sources_then_add_watch_registry_only"
    if recommendation == "watch_only_product_surface_signal":
        return "operator_confirms_product_surface_relevance_before_watch_only_entry"
    return "manual_research_no_registry_change"


def _metadata_text(repo: str, metadata: dict[str, Any]) -> str:
    topics = " ".join(str(topic) for topic in metadata.get("topics") or [])
    parts = [
        repo,
        str(metadata.get("description") or ""),
        str(metadata.get("homepage") or ""),
        topics,
        str(metadata.get("language") or ""),
    ]
    return " ".join(parts).lower().replace("-", " ")


def _has_any(text: str, needles: list[str]) -> bool:
    return any(needle.replace("-", " ") in text for needle in needles)


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