"""Tests for orxaq.credit.regulatory_export — CECL/CCAR export package."""

from __future__ import annotations

import json

import pytest

from orxaq.credit.regulatory_export import RegulatoryExporter


@pytest.fixture(scope="module")
def exporter() -> RegulatoryExporter:
    return RegulatoryExporter(institution_id="TEST001", reporting_period="2024-Q4")


@pytest.fixture(scope="module")
def cecl_export(exporter: RegulatoryExporter) -> dict:
    result = exporter.export_cecl(format="json")
    assert isinstance(result, dict)
    return result


@pytest.fixture(scope="module")
def ccar_export(exporter: RegulatoryExporter) -> dict:
    result = exporter.export_ccar(format="json")
    assert isinstance(result, dict)
    return result


class TestRegulatoryExporterInit:
    def test_defaults_set(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1")
        assert e.methodology == "PD/LGD"

    def test_custom_institution_id(self) -> None:
        e = RegulatoryExporter(institution_id="BANK99", reporting_period="2024-Q1")
        assert e.institution_id == "BANK99"

    def test_reporting_period_stored(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2025-Q2")
        assert e.reporting_period == "2025-Q2"

    def test_custom_methodology(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1", methodology="WARM")
        assert e.methodology == "WARM"


class TestCECLExportJSON:
    def test_framework_field_is_cecl(self, cecl_export: dict) -> None:
        assert cecl_export["framework"] == "CECL"

    def test_institution_id_present(self, cecl_export: dict) -> None:
        assert "institution_id" in cecl_export
        assert cecl_export["institution_id"] == "TEST001"

    def test_reporting_period_present(self, cecl_export: dict) -> None:
        assert "reporting_period" in cecl_export
        assert cecl_export["reporting_period"] == "2024-Q4"

    def test_methodology_attestation_is_string(self, cecl_export: dict) -> None:
        assert isinstance(cecl_export["methodology_attestation"], str)
        assert len(cecl_export["methodology_attestation"]) > 20

    def test_quantitative_results_has_ecl(self, cecl_export: dict) -> None:
        qr = cecl_export["quantitative_results"]
        assert "ecl_estimate" in qr
        assert isinstance(qr["ecl_estimate"], float)

    def test_confidence_band_ordered(self, cecl_export: dict) -> None:
        cb = cecl_export["quantitative_results"]["confidence_band"]
        assert len(cb) == 2
        assert cb[0] <= cb[1]

    def test_segments_present(self, cecl_export: dict) -> None:
        segs = cecl_export["quantitative_results"]["segments"]
        assert isinstance(segs, dict)

    def test_model_governance_has_validation_summary(self, cecl_export: dict) -> None:
        mg = cecl_export["model_governance"]
        assert "validation_summary" in mg
        assert isinstance(mg["validation_summary"], str)

    def test_output_is_json_serializable(self, cecl_export: dict) -> None:
        dumped = json.dumps(cecl_export)
        reloaded = json.loads(dumped)
        assert reloaded["framework"] == "CECL"

    def test_asc326_disclosure_present(self, cecl_export: dict) -> None:
        assert "asc_326_disclosure" in cecl_export
        assert "ASC 326" in cecl_export["asc_326_disclosure"]


class TestCCARExportJSON:
    def test_framework_field_is_ccar(self, ccar_export: dict) -> None:
        assert ccar_export["framework"] == "CCAR"

    def test_scenario_results_list(self, ccar_export: dict) -> None:
        assert isinstance(ccar_export["scenario_results"], list)

    def test_scenario_results_has_three_scenarios(self, ccar_export: dict) -> None:
        assert len(ccar_export["scenario_results"]) == 3

    def test_fr_y14a_fields_present(self, ccar_export: dict) -> None:
        assert "fr_y14a_fields" in ccar_export
        assert isinstance(ccar_export["fr_y14a_fields"], dict)

    def test_call_report_rc_r_present(self, ccar_export: dict) -> None:
        assert "call_report_rc_r" in ccar_export

    def test_model_governance_present(self, ccar_export: dict) -> None:
        assert "model_governance" in ccar_export

    def test_each_scenario_has_ecl(self, ccar_export: dict) -> None:
        for sr in ccar_export["scenario_results"]:
            assert "ecl" in sr
            assert isinstance(sr["ecl"], float)


class TestMarkdownOutput:
    @pytest.fixture(scope="class")
    def cecl_md(self) -> str:
        e = RegulatoryExporter(institution_id="BANK001", reporting_period="2024-Q4")
        result = e.export_cecl(format="markdown")
        assert isinstance(result, str)
        return result

    @pytest.fixture(scope="class")
    def ccar_md(self) -> str:
        e = RegulatoryExporter(institution_id="BANK001", reporting_period="2024-Q4")
        result = e.export_ccar(format="markdown")
        assert isinstance(result, str)
        return result

    def test_cecl_markdown_has_h1(self, cecl_md: str) -> None:
        assert cecl_md.startswith("# CECL")

    def test_cecl_markdown_has_institution_id(self, cecl_md: str) -> None:
        assert "BANK001" in cecl_md

    def test_cecl_markdown_has_reporting_period(self, cecl_md: str) -> None:
        assert "2024-Q4" in cecl_md

    def test_ccar_markdown_has_scenario_table(self, ccar_md: str) -> None:
        assert "| Scenario |" in ccar_md or "Scenario Results" in ccar_md

    def test_markdown_has_methodology_section(self, cecl_md: str) -> None:
        assert "## Methodology" in cecl_md

    def test_markdown_has_attestation_block(self, cecl_md: str) -> None:
        assert "Model Governance Attestation" in cecl_md


class TestEdgeCases:
    def test_unknown_format_raises(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1")
        with pytest.raises(ValueError, match="Unknown format"):
            e.export_cecl(format="xml")  # type: ignore[arg-type]

    def test_export_ccar_single_scenario(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1")
        result = e.export_ccar(format="json", scenarios=["baseline"])
        assert isinstance(result, dict)
        assert len(result["scenario_results"]) == 1
        assert result["scenario_results"][0]["scenario_name"] == "baseline"

    def test_ccar_unknown_format_raises(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1")
        with pytest.raises(ValueError, match="Unknown format"):
            e.export_ccar(format="xml")  # type: ignore[arg-type]

    def test_ccar_unknown_scenario_skipped(self) -> None:
        e = RegulatoryExporter(institution_id="X", reporting_period="2024-Q1")
        result = e.export_ccar(format="json", scenarios=["nonexistent_scenario"])
        assert isinstance(result, dict)
        assert len(result["scenario_results"]) == 0


# ── Module-level helpers ─────────────────────────────────


class TestExtractSegmentWeights:
    """Cover lines 682-688: _extract_segment_weights with analytics."""

    def test_with_ecl_by_segment(self):
        from orxaq.credit.regulatory_export import _extract_segment_weights

        class FakeAnalytics:
            ecl_by_segment = {"commercial": 0.6, "retail": 0.4}

        weights = _extract_segment_weights(FakeAnalytics())
        assert abs(weights["commercial"] - 0.6) < 1e-6
        assert abs(weights["retail"] - 0.4) < 1e-6

    def test_with_segment_breakdown(self):
        from orxaq.credit.regulatory_export import _extract_segment_weights

        class FakeAnalytics:
            ecl_by_segment = None
            segment_breakdown = {"commercial": 30.0, "retail": 70.0}

        weights = _extract_segment_weights(FakeAnalytics())
        assert abs(weights["commercial"] - 0.3) < 1e-6
        assert abs(weights["retail"] - 0.7) < 1e-6

    def test_none_analytics_uses_defaults(self):
        from orxaq.credit.regulatory_export import _extract_segment_weights

        weights = _extract_segment_weights(None)
        assert len(weights) > 0
        assert abs(sum(weights.values()) - 1.0) < 1e-6


class TestMeanQuarterlyEclProxy:
    """Cover lines 770, 774: _mean_quarterly_ecl_to_pd_proxy."""

    def test_empty_returns_zero(self):
        from orxaq.credit.regulatory_export import _mean_quarterly_ecl_to_pd_proxy

        assert _mean_quarterly_ecl_to_pd_proxy([]) == 0.0

    def test_normal_computation(self):
        from orxaq.credit.regulatory_export import _mean_quarterly_ecl_to_pd_proxy

        result = _mean_quarterly_ecl_to_pd_proxy([0.01, 0.02, 0.04, 0.03])
        assert 0.0 < result <= 1.0

    def test_all_zeros(self):
        from orxaq.credit.regulatory_export import _mean_quarterly_ecl_to_pd_proxy

        assert _mean_quarterly_ecl_to_pd_proxy([0.0, 0.0]) == 0.0


class TestLoadCreditOntologyFailure:
    """Cover lines 763-764: _load_credit_ontology outer exception."""

    def test_handles_iteration_failure(self):
        from unittest.mock import patch

        from orxaq.credit.regulatory_export import _load_credit_ontology

        class BrokenIterable:
            def __iter__(self):
                msg = "iteration broken"
                raise TypeError(msg)

        # Patch CREDIT_VARIABLES to something that fails on iteration
        with patch("orxaq.credit.ontology.CREDIT_VARIABLES", BrokenIterable()):
            # Should not raise — outer except catches all
            _load_credit_ontology(object())
