"""Tests for post-training verification."""

import json
import math
from pathlib import Path

import pytest

from orbit.verify import (
    VerificationResult,
    _assess_accuracy,
    _extract_final_loss,
    _generate_feedback,
    _store_calibration,
    verify_training,
)


class TestExtractFinalLoss:
    def test_valid_entries(self):
        entries = [
            {"step": 1000, "loss": 0.5},
            {"step": 5000, "loss": 0.3},
            {"step": 10000, "loss": 0.12},
        ]
        assert _extract_final_loss(entries) == 0.12

    def test_nan_entries_skipped(self):
        entries = [
            {"step": 1000, "loss": 0.5},
            {"step": 5000, "loss": 0.3},
            {"step": 10000, "loss": float("nan")},
        ]
        assert _extract_final_loss(entries) == 0.3

    def test_empty_entries(self):
        assert math.isnan(_extract_final_loss([]))

    def test_all_nan(self):
        entries = [{"step": 1000, "loss": float("nan")}]
        assert math.isnan(_extract_final_loss(entries))


class TestAssessAccuracy:
    def test_correct_with_success_rate_in_range(self):
        result = _assess_accuracy(
            predicted_grade="B",
            predicted_range=(0.65, 0.80),
            training_status="converged_well",
            actual_final_loss=0.05,
            success_rate=0.72,
        )
        assert result == "correct"

    def test_optimistic_with_low_success_rate(self):
        result = _assess_accuracy(
            predicted_grade="B",
            predicted_range=(0.65, 0.80),
            training_status="converged_well",
            actual_final_loss=0.05,
            success_rate=0.45,
        )
        assert result == "optimistic"

    def test_pessimistic_with_high_success_rate(self):
        result = _assess_accuracy(
            predicted_grade="B",
            predicted_range=(0.65, 0.80),
            training_status="converged_well",
            actual_final_loss=0.05,
            success_rate=0.95,
        )
        assert result == "pessimistic"

    def test_correct_with_good_training_and_good_grade(self):
        result = _assess_accuracy(
            predicted_grade="B",
            predicted_range=(0.0, 0.0),
            training_status="converged_well",
            actual_final_loss=0.05,
            success_rate=None,
        )
        assert result == "correct"

    def test_correct_with_bad_training_and_bad_grade(self):
        result = _assess_accuracy(
            predicted_grade="F",
            predicted_range=(0.0, 0.0),
            training_status="nan_loss",
            actual_final_loss=float("nan"),
            success_rate=None,
        )
        assert result == "correct"

    def test_optimistic_when_good_grade_bad_outcome(self):
        result = _assess_accuracy(
            predicted_grade="A",
            predicted_range=(0.0, 0.0),
            training_status="nan_loss",
            actual_final_loss=float("nan"),
            success_rate=None,
        )
        assert result == "optimistic"

    def test_unknown_with_no_prediction(self):
        result = _assess_accuracy(
            predicted_grade="?",
            predicted_range=(0.0, 0.0),
            training_status="converged_well",
            actual_final_loss=0.05,
            success_rate=None,
        )
        assert result == "unknown"


class TestGenerateFeedback:
    def test_correct_feedback(self):
        feedback = _generate_feedback(
            predicted_grade="B", predicted_score=84,
            predicted_range=(0.65, 0.80),
            training_status="converged_well",
            actual_final_loss=0.05,
            accuracy="correct",
            success_rate=None,
        )
        assert "matches" in feedback.lower() or "sufficient" in feedback.lower()

    def test_optimistic_feedback(self):
        feedback = _generate_feedback(
            predicted_grade="A", predicted_score=95,
            predicted_range=(0.85, 0.95),
            training_status="plateau",
            actual_final_loss=0.3,
            accuracy="optimistic",
            success_rate=None,
        )
        assert "optimistic" in feedback.lower()

    def test_unknown_feedback(self):
        feedback = _generate_feedback(
            predicted_grade="?", predicted_score=0,
            predicted_range=(0.0, 0.0),
            training_status="converged_well",
            actual_final_loss=0.05,
            accuracy="unknown",
            success_rate=None,
        )
        assert "no prior prediction" in feedback.lower()


class TestStoreCalibration:
    def test_calibration_stored(self, tmp_path):
        import orbit.verify as verify_module

        # Temporarily redirect calibration dir
        original = verify_module.CALIBRATION_DIR
        verify_module.CALIBRATION_DIR = tmp_path / "calibration"

        try:
            result = VerificationResult(
                predicted_grade="B",
                predicted_score=84,
                predicted_success_range=(0.65, 0.80),
                actual_final_loss=0.05,
                actual_loss_converged=True,
                training_status="converged_well",
                prediction_accuracy="correct",
                feedback="test",
            )
            _store_calibration(result, success_rate=0.73, dataset="test/data")

            cal_file = tmp_path / "calibration" / "calibration_log.jsonl"
            assert cal_file.exists()
            entry = json.loads(cal_file.read_text().strip())
            assert entry["predicted_grade"] == "B"
            assert entry["actual_success_rate"] == 0.73
        finally:
            verify_module.CALIBRATION_DIR = original


class TestVerifyTraining:
    def test_verify_with_log_files(self, tmp_path):
        # Create minimal training logs
        log_file = tmp_path / "train.log"
        log_file.write_text(
            "step:1000 loss:0.8 grdn:5.0\n"
            "step:5000 loss:0.4 grdn:2.0\n"
            "step:10000 loss:0.12 grdn:1.0\n"
        )

        result = verify_training(str(tmp_path))
        assert isinstance(result, VerificationResult)
        assert result.actual_final_loss == pytest.approx(0.12)
        assert result.training_status == "converged_well"

    def test_verify_nan_loss(self, tmp_path):
        log_file = tmp_path / "train.log"
        log_file.write_text(
            "step:1000 loss:0.8\n"
            "step:2000 loss:nan\n"
        )

        result = verify_training(str(tmp_path))
        assert result.training_status == "nan_loss"

    def test_verify_no_logs(self, tmp_path):
        result = verify_training(str(tmp_path))
        assert result.training_status == "no_logs"
