"""Tests for the synthetic ground truth data generator."""

from __future__ import annotations

from orxaq.credit.ontology import build_credit_ontology
from orxaq.credit.synthetic import CreditDataGenerator


class TestCreditDataGenerator:
    def setup_method(self):
        self.dag = build_credit_ontology()
        self.gen = CreditDataGenerator(self.dag, seed=42)

    def test_generate_correct_count(self):
        samples = self.gen.generate(100)
        assert len(samples) == 100

    def test_all_variables_present(self):
        samples = self.gen.generate(10)
        for sample in samples:
            assert "unemployment_rate" in sample
            assert "credit_score" in sample
            assert "default_flag" in sample
            assert len(sample) == 19

    def test_values_within_bounds(self):
        samples = self.gen.generate(1000)
        for sample in samples:
            assert 0.0 <= sample["unemployment_rate"] <= 0.30
            assert 300.0 <= sample["credit_score"] <= 850.0
            assert sample["default_flag"] in (0.0, 1.0)
            assert sample["dpd_30"] in (0.0, 1.0)
            assert sample["dpd_60"] in (0.0, 1.0)
            assert sample["dpd_90"] in (0.0, 1.0)

    def test_reproducible_with_same_seed(self):
        gen1 = CreditDataGenerator(self.dag, seed=42)
        gen2 = CreditDataGenerator(self.dag, seed=42)
        samples1 = gen1.generate(50)
        samples2 = gen2.generate(50)
        assert samples1 == samples2

    def test_different_seeds_produce_different_data(self):
        gen1 = CreditDataGenerator(self.dag, seed=42)
        gen2 = CreditDataGenerator(self.dag, seed=99)
        samples1 = gen1.generate(50)
        samples2 = gen2.generate(50)
        assert samples1 != samples2

    def test_causal_ordering_respected(self):
        """Variables should follow topological order in generation."""
        samples = self.gen.generate(500)
        # Income should correlate with unemployment (negative relationship)
        high_unemp = [s for s in samples if s["unemployment_rate"] > 0.07]
        low_unemp = [s for s in samples if s["unemployment_rate"] < 0.03]

        if high_unemp and low_unemp:
            avg_income_high = sum(s["income"] for s in high_unemp) / len(high_unemp)
            avg_income_low = sum(s["income"] for s in low_unemp) / len(low_unemp)
            # Higher unemployment → lower income (causal direction)
            assert avg_income_high < avg_income_low

    def test_intervention_changes_outcomes(self):
        """do(unemployment=0.10) should increase default rate vs do(unemployment=0.03)."""
        high_unemp = self.gen.generate_with_intervention(2000, "unemployment_rate", 0.10)
        low_unemp = self.gen.generate_with_intervention(2000, "unemployment_rate", 0.03)

        high_default_rate = sum(s["default_flag"] for s in high_unemp) / len(high_unemp)
        low_default_rate = sum(s["default_flag"] for s in low_unemp) / len(low_unemp)

        # Higher unemployment should cause higher default rate
        assert high_default_rate > low_default_rate

    def test_intervention_fixes_treatment_value(self):
        """Intervention should fix the treatment variable to the specified value."""
        samples = self.gen.generate_with_intervention(100, "unemployment_rate", 0.08)
        for sample in samples:
            assert sample["unemployment_rate"] == 0.08

    def test_generate_empty(self):
        samples = self.gen.generate(0)
        assert samples == []
