"""Calibration report helpers — generate structured calibration reports with actionable recommendations."""

from __future__ import annotations

import uuid
from datetime import UTC, datetime
from typing import Any

from abi_control_core.sdk.validate import validate_json


def generate_report(
    run_id: str,
    project: str,
    task_results: list[dict[str, Any]],
    failures: list[dict[str, Any]],
    duration: float,
    cost_data: dict[str, Any],
) -> dict[str, Any]:
    """Generate a calibration report from orchestrator run data.

    Args:
        run_id: The orchestrator run ID.
        project: Project identifier (e.g., 'StatTracker', 'ABIQuantLab').
        task_results: List of task result dicts, each with:
            - status: 'passed', 'failed', 'pass_unverified', 'skipped'
            - agent: agent identifier
            - task_category: category string
            - iterations: number of retry iterations
        failures: List of failure record dicts (from failure_taxonomy).
        duration: Total run duration in seconds.
        cost_data: Dict with:
            - total_tokens: total token count
            - total_cost_usd: total cost in USD
            - builder_tokens: builder agent tokens
            - verifier_tokens: verifier agent tokens

    Returns:
        A validated calibration report dict conforming to calibration_report.schema.json.

    Raises:
        SchemaValidationError: If the constructed report fails schema validation.

    Example:
        >>> task_results = [
        ...     {"status": "passed", "agent": "claude_code", "task_category": "code_generation", "iterations": 1},
        ...     {"status": "failed", "agent": "gpt-4.1", "task_category": "documentation", "iterations": 3},
        ... ]
        >>> failures = [
        ...     {"failure_class": "hallucinated_path", "agent": "gpt-4.1", "task_category": "documentation"}
        ... ]
        >>> cost_data = {
        ...     "total_tokens": 50000,
        ...     "total_cost_usd": 2.50,
        ...     "builder_tokens": 40000,
        ...     "verifier_tokens": 10000
        ... }
        >>> report = generate_report(
        ...     run_id="run-001",
        ...     project="StatTracker",
        ...     task_results=task_results,
        ...     failures=failures,
        ...     duration=120.5,
        ...     cost_data=cost_data
        ... )
    """
    total_tasks = len(task_results)
    passed = sum(1 for t in task_results if t.get("status") == "passed")
    failed = sum(1 for t in task_results if t.get("status") == "failed")
    pass_unverified = sum(
        1 for t in task_results if t.get("status") == "pass_unverified"
    )
    skipped = sum(1 for t in task_results if t.get("status") == "skipped")

    success_rate = passed / total_tasks if total_tasks > 0 else 0.0
    avg_iterations = (
        sum(t.get("iterations", 0) for t in task_results) / total_tasks
        if total_tasks > 0
        else 0.0
    )

    # Build agent breakdown
    agent_stats: dict[str, dict[str, Any]] = {}
    for task in task_results:
        agent = task.get("agent", "unknown")
        if agent not in agent_stats:
            agent_stats[agent] = {
                "tasks_assigned": 0,
                "tasks_passed": 0,
                "tasks_failed": 0,
                "total_iterations": 0,
                "failure_classes": {},
            }

        agent_stats[agent]["tasks_assigned"] += 1
        if task.get("status") == "passed":
            agent_stats[agent]["tasks_passed"] += 1
        elif task.get("status") == "failed":
            agent_stats[agent]["tasks_failed"] += 1
        agent_stats[agent]["total_iterations"] += task.get("iterations", 0)

    # Add failure class counts per agent
    for failure in failures:
        agent = failure.get("agent", "unknown")
        failure_class = failure.get("failure_class", "other")
        if agent in agent_stats:
            if failure_class not in agent_stats[agent]["failure_classes"]:
                agent_stats[agent]["failure_classes"][failure_class] = 0
            agent_stats[agent]["failure_classes"][failure_class] += 1

    agent_breakdown = [
        {
            "agent": agent,
            "tasks_assigned": stats["tasks_assigned"],
            "tasks_passed": stats["tasks_passed"],
            "tasks_failed": stats["tasks_failed"],
            "avg_iterations": (
                stats["total_iterations"] / stats["tasks_assigned"]
                if stats["tasks_assigned"] > 0
                else 0.0
            ),
            "failure_classes": stats["failure_classes"],
        }
        for agent, stats in agent_stats.items()
    ]

    # Build failure distribution (all agents combined)
    failure_distribution: dict[str, int] = {}
    for failure in failures:
        failure_class = failure.get("failure_class", "other")
        failure_distribution[failure_class] = (
            failure_distribution.get(failure_class, 0) + 1
        )

    # Build cost breakdown
    total_tokens = cost_data.get("total_tokens", 0)
    total_cost_usd = cost_data.get("total_cost_usd", 0.0)
    cost_per_task = total_cost_usd / total_tasks if total_tasks > 0 else 0.0
    cost_per_passed_task = total_cost_usd / passed if passed > 0 else 0.0

    cost_breakdown = {
        "total_tokens": total_tokens,
        "total_cost_usd": total_cost_usd,
        "cost_per_task": cost_per_task,
        "cost_per_passed_task": cost_per_passed_task,
        "builder_tokens": cost_data.get("builder_tokens", 0),
        "verifier_tokens": cost_data.get("verifier_tokens", 0),
    }

    # Build HIL metrics
    tasks_requiring_hil = sum(
        1 for t in task_results if t.get("required_hil", False)
    )
    tasks_auto_approved = sum(
        1 for t in task_results if not t.get("required_hil", False)
    )
    tasks_human_approved = sum(
        1 for t in task_results if t.get("human_approved", False)
    )
    hil_rate = tasks_requiring_hil / total_tasks if total_tasks > 0 else 0.0

    hil_metrics = {
        "tasks_requiring_hil": tasks_requiring_hil,
        "tasks_auto_approved": tasks_auto_approved,
        "tasks_human_approved": tasks_human_approved,
        "hil_rate": hil_rate,
    }

    # Build routing performance
    routing_stats: dict[tuple[str, str], dict[str, Any]] = {}
    for task in task_results:
        category = task.get("task_category", "unknown")
        agent = task.get("agent", "unknown")
        key = (category, agent)

        if key not in routing_stats:
            routing_stats[key] = {
                "total": 0,
                "passed": 0,
                "total_iterations": 0,
            }

        routing_stats[key]["total"] += 1
        if task.get("status") == "passed":
            routing_stats[key]["passed"] += 1
        routing_stats[key]["total_iterations"] += task.get("iterations", 0)

    routing_performance = [
        {
            "task_category": category,
            "agent": agent,
            "success_rate": (
                stats["passed"] / stats["total"] if stats["total"] > 0 else 0.0
            ),
            "avg_iterations": (
                stats["total_iterations"] / stats["total"]
                if stats["total"] > 0
                else 0.0
            ),
        }
        for (category, agent), stats in routing_stats.items()
    ]

    # Build the report structure
    report: dict[str, Any] = {
        "report_id": str(uuid.uuid4()),
        "run_id": run_id,
        "project": project,
        "generated_at": datetime.now(UTC).isoformat(),
        "summary": {
            "total_tasks": total_tasks,
            "passed": passed,
            "failed": failed,
            "pass_unverified": pass_unverified,
            "skipped": skipped,
            "success_rate": success_rate,
            "avg_iterations": avg_iterations,
            "total_duration_seconds": duration,
        },
        "agent_breakdown": agent_breakdown,
        "cost_breakdown": cost_breakdown,
        "failure_distribution": failure_distribution,
        "hil_metrics": hil_metrics,
        "routing_performance": routing_performance,
        "recommendations": [],  # Will be populated by generate_recommendations
    }

    # Generate recommendations
    report["recommendations"] = generate_recommendations(report)

    # Validate against schema before returning
    validate_json(report, "calibration_report.schema.json")
    return report


def generate_recommendations(report: dict[str, Any]) -> list[str]:
    """Generate actionable recommendations from a calibration report.

    Analyzes report metrics to identify improvement opportunities:
    - Low success rates (< 0.8)
    - Dominant failure classes (> 30% of failures)
    - High retry rates (avg_iterations > 2.0)
    - High HIL dependency (hil_rate > 0.5)
    - Per-agent routing issues (success_rate < 0.7 for a category)

    Args:
        report: A calibration report dict (can be partial for testing).

    Returns:
        List of recommendation strings.

    Example:
        >>> report = {
        ...     "summary": {"success_rate": 0.6, "avg_iterations": 2.5},
        ...     "failure_distribution": {"hallucinated_path": 50, "other": 10},
        ...     "hil_metrics": {"hil_rate": 0.6},
        ...     "routing_performance": [
        ...         {"task_category": "code_generation", "agent": "gpt-4.1", "success_rate": 0.5}
        ...     ]
        ... }
        >>> recommendations = generate_recommendations(report)
        >>> len(recommendations)
        5
    """
    recommendations: list[str] = []

    summary = report.get("summary", {})
    failure_distribution = report.get("failure_distribution", {})
    hil_metrics = report.get("hil_metrics", {})
    routing_performance = report.get("routing_performance", [])

    # Check overall success rate
    success_rate = summary.get("success_rate", 1.0)
    if success_rate < 0.8:
        recommendations.append(
            "Consider reviewing task prompts for failed categories"
        )

    # Check for dominant failure classes (> 30% of total failures)
    total_failures = sum(failure_distribution.values())
    if total_failures > 0:
        for failure_class, count in failure_distribution.items():
            if count / total_failures > 0.3:
                recommendations.append(
                    f"Dominant failure class '{failure_class}' — add targeted eval probe"
                )

    # Check retry rate
    avg_iterations = summary.get("avg_iterations", 0.0)
    if avg_iterations > 2.0:
        recommendations.append(
            "High retry rate suggests prompts may need refinement"
        )

    # Check HIL dependency
    hil_rate = hil_metrics.get("hil_rate", 0.0)
    if hil_rate > 0.5:
        recommendations.append(
            "Consider adding deterministic constraints to reduce HIL dependency"
        )

    # Check per-agent routing performance
    for routing in routing_performance:
        if routing.get("success_rate", 1.0) < 0.7:
            category = routing.get("task_category", "unknown")
            agent = routing.get("agent", "unknown")
            recommendations.append(
                f"Consider re-routing {category} tasks from {agent}"
            )

    return recommendations


def validate_calibration_report(report: dict[str, Any]) -> bool:
    """Validate a calibration report against the schema.

    Args:
        report: The calibration report dict to validate.

    Returns:
        True if validation succeeds.

    Raises:
        SchemaValidationError: If validation fails, with detailed error messages.
    """
    validate_json(report, "calibration_report.schema.json")
    return True
