"""Tests for calibration_report SDK — generate and validate calibration reports."""

from __future__ import annotations

import uuid

import pytest

from abi_control_core.sdk.calibration_report import (
    generate_recommendations,
    generate_report,
    validate_calibration_report,
)
from abi_control_core.sdk.validate import SchemaValidationError

# ── Valid fixture ───────────────────────────────────────────────────────

VALID_CALIBRATION_REPORT = {
    "report_id": "550e8400-e29b-41d4-a716-446655440000",
    "run_id": "run123",
    "project": "StatTracker",
    "generated_at": "2026-03-05T10:00:00Z",
    "summary": {
        "total_tasks": 100,
        "passed": 85,
        "failed": 10,
        "pass_unverified": 3,
        "skipped": 2,
        "success_rate": 0.85,
        "avg_iterations": 1.5,
        "total_duration_seconds": 3600.5,
    },
    "agent_breakdown": [
        {
            "agent": "claude_code",
            "tasks_assigned": 60,
            "tasks_passed": 55,
            "tasks_failed": 5,
            "avg_iterations": 1.3,
            "failure_classes": {"hallucinated_path": 3, "schema_drift": 2},
        },
    ],
    "cost_breakdown": {
        "total_tokens": 500000,
        "total_cost_usd": 25.50,
        "cost_per_task": 0.255,
        "cost_per_passed_task": 0.30,
        "builder_tokens": 400000,
        "verifier_tokens": 100000,
    },
    "failure_distribution": {"hallucinated_path": 3, "schema_drift": 2},
    "hil_metrics": {
        "tasks_requiring_hil": 20,
        "tasks_auto_approved": 80,
        "tasks_human_approved": 15,
        "hil_rate": 0.20,
    },
    "routing_performance": [
        {
            "task_category": "code_generation",
            "agent": "claude_code",
            "success_rate": 0.90,
            "avg_iterations": 1.2,
        },
    ],
    "recommendations": ["High retry rate suggests prompts may need refinement"],
}


# ── Schema validation tests ─────────────────────────────────────────────


def test_valid_calibration_report_passes_validation() -> None:
    """Test that a valid calibration report passes schema validation."""
    validate_calibration_report(VALID_CALIBRATION_REPORT)
    assert True  # No exception raised


def test_minimal_calibration_report_passes_validation() -> None:
    """Test that a minimal calibration report (empty arrays/objects) passes validation."""
    minimal = {
        "report_id": "550e8400-e29b-41d4-a716-446655440000",
        "run_id": "run001",
        "project": "TestProject",
        "generated_at": "2026-03-07T10:00:00Z",
        "summary": {
            "total_tasks": 0,
            "passed": 0,
            "failed": 0,
            "pass_unverified": 0,
            "skipped": 0,
            "success_rate": 0.0,
            "avg_iterations": 0.0,
            "total_duration_seconds": 0.0,
        },
        "agent_breakdown": [],
        "cost_breakdown": {
            "total_tokens": 0,
            "total_cost_usd": 0.0,
            "cost_per_task": 0.0,
            "cost_per_passed_task": 0.0,
            "builder_tokens": 0,
            "verifier_tokens": 0,
        },
        "failure_distribution": {},
        "hil_metrics": {
            "tasks_requiring_hil": 0,
            "tasks_auto_approved": 0,
            "tasks_human_approved": 0,
            "hil_rate": 0.0,
        },
        "routing_performance": [],
        "recommendations": [],
    }
    validate_calibration_report(minimal)
    assert True


def test_missing_required_field_fails_validation() -> None:
    """Test that missing required fields cause validation to fail."""
    invalid = VALID_CALIBRATION_REPORT.copy()
    del invalid["run_id"]

    with pytest.raises(SchemaValidationError) as exc_info:
        validate_calibration_report(invalid)
    assert "run_id" in str(exc_info.value).lower() or "required" in str(
        exc_info.value
    ).lower()


def test_invalid_success_rate_fails_validation() -> None:
    """Test that success_rate > 1.0 fails validation."""
    invalid = VALID_CALIBRATION_REPORT.copy()
    invalid["summary"] = {**invalid["summary"], "success_rate": 1.5}

    with pytest.raises(SchemaValidationError) as exc_info:
        validate_calibration_report(invalid)
    assert "success_rate" in str(exc_info.value).lower() or "maximum" in str(
        exc_info.value
    ).lower()


def test_negative_total_tasks_fails_validation() -> None:
    """Test that negative total_tasks fails validation."""
    invalid = VALID_CALIBRATION_REPORT.copy()
    invalid["summary"] = {**invalid["summary"], "total_tasks": -1}

    with pytest.raises(SchemaValidationError) as exc_info:
        validate_calibration_report(invalid)
    assert "total_tasks" in str(exc_info.value).lower() or "minimum" in str(
        exc_info.value
    ).lower()


def test_invalid_hil_rate_fails_validation() -> None:
    """Test that hil_rate > 1.0 fails validation."""
    invalid = VALID_CALIBRATION_REPORT.copy()
    invalid["hil_metrics"] = {**invalid["hil_metrics"], "hil_rate": 1.5}

    with pytest.raises(SchemaValidationError) as exc_info:
        validate_calibration_report(invalid)
    assert "hil_rate" in str(exc_info.value).lower() or "maximum" in str(
        exc_info.value
    ).lower()


# ── generate_report tests ───────────────────────────────────────────────


def test_generate_report_basic() -> None:
    """Test that generate_report creates a valid report from basic task results."""
    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,
    )

    # Verify structure
    assert report["run_id"] == "run-001"
    assert report["project"] == "StatTracker"
    assert report["summary"]["total_tasks"] == 2
    assert report["summary"]["passed"] == 1
    assert report["summary"]["failed"] == 1
    assert report["summary"]["success_rate"] == 0.5
    assert report["summary"]["total_duration_seconds"] == 120.5

    # Verify agent breakdown
    assert len(report["agent_breakdown"]) == 2  # claude_code and gpt-4.1

    # Verify cost breakdown
    assert report["cost_breakdown"]["total_tokens"] == 50000
    assert report["cost_breakdown"]["total_cost_usd"] == 2.50
    assert report["cost_breakdown"]["cost_per_task"] == 1.25  # 2.50 / 2

    # Verify failure distribution
    assert report["failure_distribution"]["hallucinated_path"] == 1

    # Should validate against schema
    validate_calibration_report(report)


def test_generate_report_with_hil() -> None:
    """Test that generate_report correctly calculates HIL metrics."""
    task_results = [
        {
            "status": "passed",
            "agent": "claude_code",
            "task_category": "code_generation",
            "iterations": 1,
            "required_hil": True,
            "human_approved": True,
        },
        {
            "status": "passed",
            "agent": "claude_code",
            "task_category": "code_generation",
            "iterations": 1,
            "required_hil": False,
            "human_approved": False,
        },
    ]
    cost_data = {
        "total_tokens": 10000,
        "total_cost_usd": 0.50,
        "builder_tokens": 8000,
        "verifier_tokens": 2000,
    }

    report = generate_report(
        run_id="run-002",
        project="TestProject",
        task_results=task_results,
        failures=[],
        duration=60.0,
        cost_data=cost_data,
    )

    # Verify HIL metrics
    assert report["hil_metrics"]["tasks_requiring_hil"] == 1
    assert report["hil_metrics"]["tasks_auto_approved"] == 1
    assert report["hil_metrics"]["tasks_human_approved"] == 1
    assert report["hil_metrics"]["hil_rate"] == 0.5  # 1 out of 2

    validate_calibration_report(report)


def test_generate_report_with_routing_performance() -> None:
    """Test that generate_report calculates routing performance correctly."""
    task_results = [
        {
            "status": "passed",
            "agent": "claude_code",
            "task_category": "code_generation",
            "iterations": 1,
        },
        {
            "status": "passed",
            "agent": "claude_code",
            "task_category": "code_generation",
            "iterations": 2,
        },
        {
            "status": "failed",
            "agent": "gpt-4.1",
            "task_category": "documentation",
            "iterations": 3,
        },
    ]
    cost_data = {
        "total_tokens": 10000,
        "total_cost_usd": 0.50,
        "builder_tokens": 8000,
        "verifier_tokens": 2000,
    }

    report = generate_report(
        run_id="run-003",
        project="TestProject",
        task_results=task_results,
        failures=[],
        duration=60.0,
        cost_data=cost_data,
    )

    # Verify routing performance
    assert len(report["routing_performance"]) == 2  # Two category-agent pairs

    code_gen_routing = next(
        r
        for r in report["routing_performance"]
        if r["task_category"] == "code_generation"
    )
    assert code_gen_routing["agent"] == "claude_code"
    assert code_gen_routing["success_rate"] == 1.0  # 2 passed out of 2
    assert code_gen_routing["avg_iterations"] == 1.5  # (1 + 2) / 2

    doc_routing = next(
        r
        for r in report["routing_performance"]
        if r["task_category"] == "documentation"
    )
    assert doc_routing["agent"] == "gpt-4.1"
    assert doc_routing["success_rate"] == 0.0  # 0 passed out of 1
    assert doc_routing["avg_iterations"] == 3.0

    validate_calibration_report(report)


def test_generate_report_generates_uuid() -> None:
    """Test that generate_report generates a valid UUID for report_id."""
    task_results = [
        {
            "status": "passed",
            "agent": "claude_code",
            "task_category": "code_generation",
            "iterations": 1,
        }
    ]
    cost_data = {
        "total_tokens": 10000,
        "total_cost_usd": 0.50,
        "builder_tokens": 8000,
        "verifier_tokens": 2000,
    }

    report = generate_report(
        run_id="run-004",
        project="TestProject",
        task_results=task_results,
        failures=[],
        duration=60.0,
        cost_data=cost_data,
    )

    # Should be a valid UUID
    parsed_uuid = uuid.UUID(report["report_id"])
    assert isinstance(parsed_uuid, uuid.UUID)


def test_generate_report_empty_tasks() -> None:
    """Test that generate_report handles empty task_results gracefully."""
    cost_data = {
        "total_tokens": 0,
        "total_cost_usd": 0.0,
        "builder_tokens": 0,
        "verifier_tokens": 0,
    }

    report = generate_report(
        run_id="run-005",
        project="TestProject",
        task_results=[],
        failures=[],
        duration=0.0,
        cost_data=cost_data,
    )

    assert report["summary"]["total_tasks"] == 0
    assert report["summary"]["success_rate"] == 0.0
    assert report["summary"]["avg_iterations"] == 0.0
    assert len(report["agent_breakdown"]) == 0

    validate_calibration_report(report)


# ── generate_recommendations tests ─────────────────────────────────────


def test_generate_recommendations_low_success_rate() -> None:
    """Test that low success rate triggers a recommendation."""
    report = {
        "summary": {"success_rate": 0.6, "avg_iterations": 1.0},
        "failure_distribution": {},
        "hil_metrics": {"hil_rate": 0.1},
        "routing_performance": [],
    }

    recommendations = generate_recommendations(report)
    assert any(
        "reviewing task prompts" in rec.lower() for rec in recommendations
    )


def test_generate_recommendations_dominant_failure_class() -> None:
    """Test that dominant failure class triggers a recommendation."""
    report = {
        "summary": {"success_rate": 0.9, "avg_iterations": 1.0},
        "failure_distribution": {
            "hallucinated_path": 50,
            "schema_drift": 10,
            "other": 5,
        },
        "hil_metrics": {"hil_rate": 0.1},
        "routing_performance": [],
    }

    recommendations = generate_recommendations(report)
    # hallucinated_path is 50/65 = 76.9%, which is > 30%
    assert any("hallucinated_path" in rec for rec in recommendations)
    assert any("eval probe" in rec.lower() for rec in recommendations)


def test_generate_recommendations_high_retry_rate() -> None:
    """Test that high retry rate triggers a recommendation."""
    report = {
        "summary": {"success_rate": 0.9, "avg_iterations": 2.5},
        "failure_distribution": {},
        "hil_metrics": {"hil_rate": 0.1},
        "routing_performance": [],
    }

    recommendations = generate_recommendations(report)
    assert any("retry rate" in rec.lower() for rec in recommendations)
    assert any("prompts may need refinement" in rec.lower() for rec in recommendations)


def test_generate_recommendations_high_hil_rate() -> None:
    """Test that high HIL rate triggers a recommendation."""
    report = {
        "summary": {"success_rate": 0.9, "avg_iterations": 1.0},
        "failure_distribution": {},
        "hil_metrics": {"hil_rate": 0.6},
        "routing_performance": [],
    }

    recommendations = generate_recommendations(report)
    assert any("deterministic constraints" in rec.lower() for rec in recommendations)
    assert any("hil dependency" in rec.lower() for rec in recommendations)


def test_generate_recommendations_poor_routing() -> None:
    """Test that poor agent routing triggers a recommendation."""
    report = {
        "summary": {"success_rate": 0.9, "avg_iterations": 1.0},
        "failure_distribution": {},
        "hil_metrics": {"hil_rate": 0.1},
        "routing_performance": [
            {
                "task_category": "code_generation",
                "agent": "gpt-4.1",
                "success_rate": 0.5,
                "avg_iterations": 2.0,
            }
        ],
    }

    recommendations = generate_recommendations(report)
    assert any("re-routing" in rec.lower() for rec in recommendations)
    assert any("code_generation" in rec for rec in recommendations)
    assert any("gpt-4.1" in rec for rec in recommendations)


def test_generate_recommendations_no_issues() -> None:
    """Test that no recommendations are generated when metrics are good."""
    report = {
        "summary": {"success_rate": 0.95, "avg_iterations": 1.2},
        "failure_distribution": {
            "hallucinated_path": 3,
            "schema_drift": 3,
            "import_chain_break": 3,
            "other": 3,
        },  # No single class > 30% (each is 25%)
        "hil_metrics": {"hil_rate": 0.1},
        "routing_performance": [
            {
                "task_category": "code_generation",
                "agent": "claude_code",
                "success_rate": 0.9,
                "avg_iterations": 1.1,
            }
        ],
    }

    recommendations = generate_recommendations(report)
    assert len(recommendations) == 0


def test_generate_recommendations_multiple_issues() -> None:
    """Test that multiple issues generate multiple recommendations."""
    report = {
        "summary": {"success_rate": 0.6, "avg_iterations": 2.5},
        "failure_distribution": {"hallucinated_path": 40, "other": 10},
        "hil_metrics": {"hil_rate": 0.6},
        "routing_performance": [
            {
                "task_category": "code_generation",
                "agent": "gpt-4.1",
                "success_rate": 0.5,
                "avg_iterations": 3.0,
            }
        ],
    }

    recommendations = generate_recommendations(report)
    # Should have multiple recommendations
    assert len(recommendations) >= 4

    # Check that each issue is represented
    assert any("reviewing task prompts" in rec.lower() for rec in recommendations)
    assert any("hallucinated_path" in rec for rec in recommendations)
    assert any("retry rate" in rec.lower() for rec in recommendations)
    assert any("hil dependency" in rec.lower() for rec in recommendations)
    assert any("re-routing" in rec.lower() for rec in recommendations)
