"""Tests for the cost estimator."""

from __future__ import annotations

from agent_scout.llm.estimator import CostEstimator

# --- Estimate tests ---


def test_estimate_with_input_tokens():
    estimator = CostEstimator()
    est = estimator.estimate(
        model="anthropic/claude-sonnet-4-20250514",
        input_tokens=1000,
        expected_output_tokens=500,
    )
    assert est.model == "anthropic/claude-sonnet-4-20250514"
    assert est.estimated_input_tokens == 1000
    assert est.estimated_output_tokens == 500
    # $3/M input * 1000 + $15/M output * 500
    expected = (1000 / 1_000_000) * 3.0 + (500 / 1_000_000) * 15.0
    assert abs(est.estimated_cost_usd - expected) < 1e-9


def test_estimate_from_messages():
    estimator = CostEstimator()
    messages = [
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello, how are you?"},
    ]
    est = estimator.estimate(
        model="openai/gpt-4o",
        messages=messages,
        expected_output_tokens=100,
    )
    assert est.estimated_input_tokens > 0
    assert est.estimated_cost_usd > 0


def test_estimate_formatted():
    estimator = CostEstimator()
    est = estimator.estimate(
        model="anthropic/claude-haiku-3",
        input_tokens=500,
        expected_output_tokens=200,
    )
    formatted = est.formatted
    assert "~500" in formatted
    assert "~200" in formatted
    assert "$" in formatted


def test_estimate_unknown_model_fallback():
    estimator = CostEstimator()
    est = estimator.estimate(
        model="unknown/some-model",
        input_tokens=1000,
        expected_output_tokens=500,
    )
    # Should use fallback pricing (3.0, 15.0)
    expected = (1000 / 1_000_000) * 3.0 + (500 / 1_000_000) * 15.0
    assert abs(est.estimated_cost_usd - expected) < 1e-9


# --- Compare tests ---


def test_compare_estimate_vs_actual():
    estimator = CostEstimator()
    est = estimator.estimate(
        model="anthropic/claude-sonnet-4-20250514",
        input_tokens=1000,
        expected_output_tokens=500,
    )
    comparison = estimator.compare(est, actual_input_tokens=900, actual_output_tokens=400)
    assert comparison.estimated_cost == est.estimated_cost_usd
    assert comparison.actual_cost > 0
    assert comparison.accuracy_pct > 0


def test_compare_accuracy():
    estimator = CostEstimator()
    est = estimator.estimate(
        model="openai/gpt-4o",
        input_tokens=1000,
        expected_output_tokens=500,
    )
    # Exact match
    comparison = estimator.compare(est, actual_input_tokens=1000, actual_output_tokens=500)
    assert comparison.accuracy_pct == 100.0
    assert comparison.difference == 0.0


# --- calculate_cost ---


def test_calculate_cost():
    estimator = CostEstimator()
    cost = estimator.calculate_cost(
        "anthropic/claude-sonnet-4-20250514",
        input_tokens=1000,
        output_tokens=500,
    )
    expected = (1000 / 1_000_000) * 3.0 + (500 / 1_000_000) * 15.0
    assert abs(cost - expected) < 1e-9


# --- Pricing management ---


def test_get_pricing():
    estimator = CostEstimator()
    inp, out = estimator.get_pricing("openai/gpt-4o")
    assert inp == 2.5
    assert out == 10.0


def test_update_pricing():
    estimator = CostEstimator()
    estimator.update_pricing("custom/mymodel", 1.0, 2.0)
    inp, out = estimator.get_pricing("custom/mymodel")
    assert inp == 1.0
    assert out == 2.0


def test_pricing_override_in_constructor():
    estimator = CostEstimator(pricing_overrides={"openai/gpt-4o": (5.0, 20.0)})
    inp, out = estimator.get_pricing("openai/gpt-4o")
    assert inp == 5.0
    assert out == 20.0


# --- Message token estimation ---


def test_estimate_tokens_multipart_content():
    estimator = CostEstimator()
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What is this?"},
                {"type": "image_url", "url": "..."},
            ],
        },
    ]
    est = estimator.estimate(
        model="openai/gpt-4o",
        messages=messages,
        expected_output_tokens=100,
    )
    assert est.estimated_input_tokens > 0
