"""Tests for predictions collection."""

from datetime import date, timedelta

import pytest

from llm_kelt import NotFoundError, ValidationError


class TestPredictionsClient:
    """Test PredictionsClient functionality."""

    def test_record_prediction(self, kelt_client, clean_tables):
        """Test recording a prediction."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="BTC will exceed $100k by Q1 2025",
            confidence=0.65,
            resolution_date="2025-03-31",
            category="crypto",
            tags=["price", "bitcoin"],
        )

        assert fact_id > 0

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact is not None
        assert fact.content == "BTC will exceed $100k by Q1 2025"
        assert fact.confidence == 0.65
        assert fact.prediction_details.resolution_date == date(2025, 3, 31)
        assert fact.category == "crypto"
        assert fact.prediction_details.tags == ["price", "bitcoin"]
        assert fact.prediction_details.status == "pending"

    def test_record_with_date_object(self, kelt_client, clean_tables):
        """Test recording with date object."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Test prediction",
            confidence=0.5,
            resolution_date=date(2025, 6, 15),
        )

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.resolution_date == date(2025, 6, 15)
        assert fact.prediction_details.resolution_type == "date"

    def test_record_with_event(self, kelt_client, clean_tables):
        """Test recording with event-based resolution."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Company X will IPO",
            confidence=0.4,
            resolution_event="IPO announcement",
        )

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.resolution_type == "event"
        assert fact.prediction_details.resolution_event == "IPO announcement"

    def test_record_with_metric(self, kelt_client, clean_tables):
        """Test recording with metric-based resolution."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Stock will hit target",
            confidence=0.6,
            resolution_metric={"metric": "AAPL_price", "operator": ">", "value": 200},
        )

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.resolution_type == "metric"
        assert fact.prediction_details.resolution_metric["metric"] == "AAPL_price"

    def test_empty_hypothesis_raises(self, kelt_client, clean_tables):
        """Test that empty hypothesis raises ValidationError."""
        with pytest.raises(ValidationError, match="hypothesis cannot be empty"):
            kelt_client.atomic.predictions.record(
                hypothesis="",
                confidence=0.5,
            )

    def test_invalid_confidence_raises(self, kelt_client, clean_tables):
        """Test that invalid confidence raises ValidationError."""
        with pytest.raises(ValidationError, match="confidence must be"):
            kelt_client.atomic.predictions.record(
                hypothesis="Test",
                confidence=1.5,
            )

    def test_resolve_correct(self, kelt_client, clean_tables):
        """Test resolving prediction as correct."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Test prediction",
            confidence=0.7,
        )

        result = kelt_client.atomic.predictions.resolve(
            fact_id=fact_id,
            outcome="correct",
            actual="It happened as predicted",
        )

        assert result is True

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.status == "resolved"
        assert fact.prediction_details.outcome == "correct"
        assert fact.prediction_details.actual_result == "It happened as predicted"
        assert fact.prediction_details.resolved_at is not None

    def test_resolve_incorrect(self, kelt_client, clean_tables):
        """Test resolving prediction as incorrect."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Test prediction",
            confidence=0.7,
        )

        kelt_client.atomic.predictions.resolve(
            fact_id=fact_id,
            outcome="incorrect",
            actual="It did not happen",
        )

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.outcome == "incorrect"

    def test_resolve_partial(self, kelt_client, clean_tables):
        """Test resolving prediction as partial."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Test prediction",
            confidence=0.7,
        )

        kelt_client.atomic.predictions.resolve(
            fact_id=fact_id,
            outcome="partial",
            outcome_confidence=0.6,
        )

        fact = kelt_client.atomic.predictions.get(fact_id)
        assert fact.prediction_details.outcome == "partial"
        assert fact.prediction_details.outcome_confidence == 0.6

    def test_resolve_not_found_raises(self, kelt_client, clean_tables):
        """Test that resolving non-existent prediction raises NotFoundError."""
        with pytest.raises(NotFoundError):
            kelt_client.atomic.predictions.resolve(
                fact_id=99999,
                outcome="correct",
            )

    def test_resolve_already_resolved_raises(self, kelt_client, clean_tables):
        """Test that re-resolving a prediction raises ValidationError."""
        fact_id = kelt_client.atomic.predictions.record(
            hypothesis="Test prediction",
            confidence=0.7,
        )

        # First resolution should succeed
        kelt_client.atomic.predictions.resolve(fact_id=fact_id, outcome="correct")

        # Second resolution should fail
        with pytest.raises(ValidationError, match="already resolved"):
            kelt_client.atomic.predictions.resolve(fact_id=fact_id, outcome="incorrect")

    def test_list_pending(self, kelt_client, clean_tables):
        """Test listing pending predictions."""
        kelt_client.atomic.predictions.record(hypothesis="A", confidence=0.5)
        fact_id = kelt_client.atomic.predictions.record(hypothesis="B", confidence=0.6)
        kelt_client.atomic.predictions.record(hypothesis="C", confidence=0.7)

        # Resolve one
        kelt_client.atomic.predictions.resolve(fact_id, "correct")

        pending = kelt_client.atomic.predictions.list_pending()
        assert len(pending) == 2

    def test_list_due(self, kelt_client, clean_tables):
        """Test listing due predictions."""
        yesterday = date.today() - timedelta(days=1)
        tomorrow = date.today() + timedelta(days=1)

        kelt_client.atomic.predictions.record(
            hypothesis="Past due",
            confidence=0.5,
            resolution_date=yesterday,
        )
        kelt_client.atomic.predictions.record(
            hypothesis="Not due yet",
            confidence=0.5,
            resolution_date=tomorrow,
        )

        # list_pending returns all pending sorted by resolution_date
        # Due predictions are those with resolution_date <= today
        pending = kelt_client.atomic.predictions.list_pending()
        # Find the one that's past due
        due = [
            p
            for p in pending
            if p.prediction_details.resolution_date
            and p.prediction_details.resolution_date <= date.today()
        ]
        assert len(due) == 1
        assert due[0].content == "Past due"

    def test_get_calibration_stats(self, kelt_client, clean_tables):
        """Test getting calibration statistics."""
        # Record and resolve some predictions
        for conf, outcome in [(0.3, "incorrect"), (0.7, "correct"), (0.9, "correct")]:
            fact_id = kelt_client.atomic.predictions.record(
                hypothesis=f"Test {conf}",
                confidence=conf,
            )
            kelt_client.atomic.predictions.resolve(fact_id, outcome)

        stats = kelt_client.atomic.predictions.get_calibration_stats()
        # Stats are bucketed by confidence (0.3, 0.7, 0.9)
        assert "0.3" in stats
        assert "0.7" in stats
        assert "0.9" in stats
        assert stats["0.3"]["total"] == 1
        assert stats["0.3"]["correct"] == 0
        assert stats["0.7"]["correct"] == 1
        assert stats["0.9"]["correct"] == 1
