"""Unit Tests for Machine Learning Classifiers, Calibration & Model Registry."""
from __future__ import annotations

import unittest

from backend.app.models.calibration import (
    PlattCalibrator,
    calculate_brier_score,
    compute_reliability_curve,
)
from backend.app.models.classifier import LogisticRegressionBaseline
from backend.app.models.registry import ModelRegistry


class TestMLModels(unittest.TestCase):
    def setUp(self) -> None:
        self.registry = ModelRegistry()

    def test_brier_score_calculation(self) -> None:
        """Verify Brier score calculation: 0.0 for perfect predictions, 0.25 for uninformative 0.5."""
        y_true = [1, 0, 1, 0]
        y_perf = [1.0, 0.0, 1.0, 0.0]
        self.assertEqual(calculate_brier_score(y_true, y_perf), 0.0)

        y_guess = [0.5, 0.5, 0.5, 0.5]
        self.assertEqual(calculate_brier_score(y_true, y_guess), 0.25)

    def test_platt_calibrator_and_reliability_curve(self) -> None:
        """Verify sigmoid calibrator and reliability binning."""
        scores = [-2.0, -1.0, 0.0, 1.0, 2.0]
        y_true = [0, 0, 0, 1, 1]
        cal = PlattCalibrator()
        cal.fit(scores, y_true)

        p_low = cal.calibrate(-2.0)
        p_high = cal.calibrate(2.0)
        self.assertLess(p_low, p_high)

        curve = compute_reliability_curve(y_true, [p_low, 0.3, 0.5, 0.7, p_high], n_bins=5)
        self.assertEqual(len(curve), 5)

    def test_logistic_classifier_fit_and_evaluate(self) -> None:
        """Verify baseline classifier trains without errors and produces valid calibrated probabilities."""
        # Simple linearly separable 2D feature set
        X = [
            [0.1, 0.2],
            [0.2, 0.1],
            [0.8, 0.9],
            [0.9, 0.8],
        ]
        y = [0, 0, 1, 1]
        clf = LogisticRegressionBaseline(l2_penalty=0.01, lr=0.1, n_epochs=50)
        clf.fit(X, y)

        eval_res = clf.evaluate(X, y)
        self.assertGreaterEqual(eval_res.accuracy, 0.5)
        self.assertGreaterEqual(eval_res.brier_score, 0.0)

    def test_model_registry_governance_rules(self) -> None:
        """Verify newly trained candidate cannot automatically become Champion."""
        import uuid
        v_tag = f"challenger_test_{uuid.uuid4().hex[:8]}"
        model_id = self.registry.register_challenger(
            version=v_tag,
            algorithm="LogisticRegression",
            brier_score=0.21,
            val_accuracy=0.61,
            metrics={"test": True},
            artifact_path="models/test.joblib",
        )
        self.assertIsNotNone(model_id)

        # Champion must remain active champion
        champ = self.registry.get_champion()
        self.assertNotEqual(champ["version"], v_tag)

        # Explicit promotion
        promoted = self.registry.promote_challenger(v_tag, authorized_by="TEST_ADMIN")
        self.assertTrue(promoted)
        new_champ = self.registry.get_champion()
        self.assertEqual(new_champ["version"], v_tag)


if __name__ == "__main__":
    unittest.main()
