"""Tests for core data models."""

import pytest

from agent_preflight.models import BudgetStatus, CacheStats, EjectDecision, RunResult


# ---------------------------------------------------------------------------
# BudgetStatus
# ---------------------------------------------------------------------------


class TestBudgetStatus:
    def test_remaining(self):
        s = BudgetStatus(used=0.10, limit=0.25)
        assert s.remaining == pytest.approx(0.15)

    def test_remaining_never_negative(self):
        s = BudgetStatus(used=0.30, limit=0.25)
        assert s.remaining == 0.0

    def test_pct_spent(self):
        s = BudgetStatus(used=0.05, limit=0.25)
        assert s.pct_spent == pytest.approx(0.2)

    def test_pct_spent_zero_limit(self):
        s = BudgetStatus(used=0.0, limit=0.0)
        assert s.pct_spent == 1.0

    def test_exceeded_false(self):
        assert not BudgetStatus(used=0.10, limit=0.25).exceeded

    def test_exceeded_exact(self):
        assert BudgetStatus(used=0.25, limit=0.25).exceeded

    def test_exceeded_over(self):
        assert BudgetStatus(used=0.30, limit=0.25).exceeded

    def test_format_simple(self):
        s = BudgetStatus(used=0.10, limit=0.25)
        text = s.format_simple()
        assert "$0.150 remaining" in text
        assert "$0.25" in text
        assert "60% left" in text

    def test_str_matches_format_simple(self):
        s = BudgetStatus(used=0.10, limit=0.25)
        assert str(s) == s.format_simple()


# ---------------------------------------------------------------------------
# CacheStats
# ---------------------------------------------------------------------------


class TestCacheStats:
    def test_defaults(self):
        c = CacheStats()
        assert c.hit_rate == 0.0
        assert c.to_dict()["hit_rate"] == 0.0

    def test_hit_rate(self):
        c = CacheStats(total_prompt_tokens=1000, cached_tokens=750)
        assert c.hit_rate == pytest.approx(0.75)

    def test_to_dict_roundtrip_keys(self):
        c = CacheStats(
            total_prompt_tokens=2000,
            cached_tokens=1500,
            cache_creation_tokens=500,
            request_count=10,
            cache_hit_requests=8,
        )
        d = c.to_dict()
        assert set(d.keys()) == {
            "total_prompt_tokens",
            "cached_tokens",
            "cache_creation_tokens",
            "request_count",
            "cache_hit_requests",
            "hit_rate",
        }
        assert d["hit_rate"] == 0.75


# ---------------------------------------------------------------------------
# RunResult
# ---------------------------------------------------------------------------


class TestRunResult:
    def test_ok_when_no_error(self):
        r = RunResult(alias="test", model_id="m", budget_usd=0.25)
        assert r.ok

    def test_not_ok_when_error(self):
        r = RunResult(alias="test", model_id="m", budget_usd=0.25, error="boom")
        assert not r.ok

    def test_to_dict_includes_cache(self):
        r = RunResult(alias="test", model_id="m", budget_usd=0.25)
        d = r.to_dict()
        assert "cache" in d
        assert d["cache"]["hit_rate"] == 0.0

    def test_to_dict_null_cost(self):
        """cost_usd=None should serialize cleanly (the Codex review bug)."""
        r = RunResult(alias="test", model_id="m", budget_usd=0.25, cost_usd=None)
        d = r.to_dict()
        assert d["cost_usd"] is None
