"""Targeted tests for remaining coverage gaps across multiple modules.

Covers uncovered lines in:
- enterprise/auth.py (lines 91, 203-204, 212-213, 393)
- intelligence/validation.py (lines 68, 87, 219, 223)
- fabric/regulatory_export.py (lines 94-97, 137-138)
- credit/regulatory_report.py (lines 263, 333, 363, 515, 570, 584, 595)
"""

from __future__ import annotations

import hashlib
import hmac as hmac_mod
import random

from orxaq.credit.cecl import ECLResult
from orxaq.credit.portfolio import LoanExposure
from orxaq.credit.regulatory_report import (
    RegulatorReportConfig,
    RegulatorReportExporter,
)
from orxaq.credit.reporting import ReportSection, RiskReport
from orxaq.credit.stress_report import StressTestReport
from orxaq.enterprise.auth import (
    APIKeyStore,
    AuthMiddleware,
    RateLimiter,
    TokenProvider,
    _b64url_encode,
)
from orxaq.fabric.regulatory_export import (
    CCARProjectionBuilder,
    ScheduleH1Builder,
)
from orxaq.intelligence.validation import DSeparationTester, RefutationSuite
from orxaq.kernel import CausalEdge, CausalRole, Entity, VariableType, WorldDAG

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _build_chain_dag() -> WorldDAG:
    """Build X -> Z -> Y DAG."""
    dag = WorldDAG()
    for name in ["X", "Z", "Y"]:
        dag.add_entity(
            Entity(
                entity_id=name,
                name=name,
                causal_role=CausalRole.EXOGENOUS,
                variable_type=VariableType.STATIC,
            )
        )
    dag.add_edge(CausalEdge(source="X", target="Z", mechanism="direct"))
    dag.add_edge(CausalEdge(source="Z", target="Y", mechanism="direct"))
    return dag


def _make_chain_data(n: int = 500, seed: int = 42) -> list[dict]:
    rng = random.Random(seed)
    data = []
    for _ in range(n):
        x = rng.gauss(0, 1)
        z = 2.0 * x + rng.gauss(0, 0.3)
        y = 1.5 * z + rng.gauss(0, 0.3)
        data.append({"X": x, "Z": z, "Y": y})
    return data


def _make_cecl_stub(segments=None, ecl=1000.0, pd=0.05, lgd=0.45, ead=20000.0):
    """Return a SimpleNamespace mimicking a CECLResult."""
    import types

    if segments is None:
        segments = {
            "Retail": {"ead": 10000.0, "pd": 0.04, "lgd": 0.45, "ecl": 400.0},
            "Corp": {"ead": 10000.0, "pd": 0.06, "lgd": 0.45, "ecl": 600.0},
        }
    return types.SimpleNamespace(segments=segments, ecl=ecl, pd=pd, lgd=lgd, ead=ead)


def _make_stress_stub(quarterly_ecl=None, confidence_intervals=None):
    import types

    if quarterly_ecl is None:
        quarterly_ecl = [100.0 * (i + 1) for i in range(9)]
    if confidence_intervals is None:
        confidence_intervals = [(q * 0.9, q * 1.1) for q in quarterly_ecl]
    return types.SimpleNamespace(
        quarterly_ecl=quarterly_ecl,
        confidence_intervals=confidence_intervals,
    )


def _make_ecl_result(
    total_ecl: float = 12_000.0,
    ead_total: float = 1_000_000.0,
    pd_mean: float = 0.05,
) -> ECLResult:
    return ECLResult(
        total_ecl=total_ecl,
        ecl_by_segment={
            "retail": total_ecl * 0.6,
            "commercial": total_ecl * 0.4,
        },
        horizon_quarters=1,
        horizon_method="soliton_dynamic",
        confidence_band=(total_ecl * 0.8, total_ecl * 1.3),
        pd_mean=pd_mean,
        lgd=0.45,
        ead_total=ead_total,
    )


def _make_risk_report() -> RiskReport:
    return RiskReport(
        report_id="RPT-2026-001",
        generated_at="2026-02-18T09:00:00+00:00",
        sections=[
            ReportSection(
                title="Credit Risk Overview",
                metrics={"expected_loss": 12_000.0, "hhi": 0.12},
                narrative="Portfolio exhibits moderate concentration.",
            )
        ],
        summary="Portfolio risk is moderate.",
        risk_rating="moderate",
    )


def _make_stress_report(scenario: str = "adverse") -> StressTestReport:
    return StressTestReport(
        scenario_name=scenario,
        quarterly_ecl=[10_000.0] * 9,
        portfolio_loss_rate=0.0875,
        capital_impact=0.007,
        migration_matrices=[
            {
                "AAA": {"AAA": 0.9, "AA": 0.08, "A": 0.02},
                "AA": {"AAA": 0.01, "AA": 0.88, "A": 0.11},
                "A": {"AA": 0.02, "A": 0.85, "BBB": 0.13},
            },
        ],
        confidence_intervals={
            "total_ecl": (80_000.0, 130_000.0),
            "portfolio_loss_rate": (0.07, 0.11),
            "capital_impact": (0.006, 0.009),
        },
        generated_at="2026-02-18T09:00:00+00:00",
    )


# ---------------------------------------------------------------------------
# enterprise/auth.py coverage
# ---------------------------------------------------------------------------


class TestAuthKeyStoreOrphan:
    """Cover line 91: hash_index points to missing key_id."""

    def test_hash_index_orphan_returns_none(self):
        store = APIKeyStore()
        raw_key, record = store.generate_key(name="orphan")
        del store._keys[record.key_id]
        assert store.validate(raw_key) is None


class TestAuthMalformedSignature:
    """Cover lines 203-204: malformed base64 in signature."""

    def test_invalid_base64_signature(self):
        tp = TokenProvider(secret="test-secret")
        token = tp.issue("user")
        parts = token.split(".")
        parts[2] = "!!!not-valid-base64!!!"
        assert tp.validate(".".join(parts)) is None


class TestAuthMalformedPayload:
    """Cover lines 212-213: valid signature but non-JSON payload."""

    def test_non_json_payload(self):
        secret = b"test-secret"
        tp = TokenProvider(secret="test-secret")
        header_b64 = _b64url_encode(b'{"alg":"HS256","typ":"JWT"}')
        payload_b64 = _b64url_encode(b"\xff\xfe")
        signing_input = f"{header_b64}.{payload_b64}"
        sig = hmac_mod.new(secret, signing_input.encode(), hashlib.sha256).digest()
        sig_b64 = _b64url_encode(sig)
        token = f"{header_b64}.{payload_b64}.{sig_b64}"
        assert tp.validate(token) is None


class TestAuthBearerRateLimited:
    """Cover line 393: Bearer token rate-limited."""

    def test_bearer_token_rate_limited(self):
        tp = TokenProvider(secret="test-secret")
        mw = AuthMiddleware(
            token_provider=tp,
            rate_limiter=RateLimiter(capacity=1, refill_rate=0.0),
        )
        token = tp.issue("user123")
        headers = {"Authorization": f"Bearer {token}"}
        r1 = mw.authenticate(headers, "/api/data")
        assert r1.authenticated and not r1.rate_limited
        r2 = mw.authenticate(headers, "/api/data")
        assert r2.authenticated and r2.rate_limited


# ---------------------------------------------------------------------------
# intelligence/validation.py coverage
# ---------------------------------------------------------------------------


class TestValidationVariablesNone:
    """Cover line 68: variables=None defaults to dag entity keys."""

    def test_variables_default_to_entities(self):
        dag = _build_chain_dag()
        tester = DSeparationTester(dag)
        data = _make_chain_data(n=100)
        report = tester.test_all_implications(data, variables=None)
        assert report.total_implications > 0


class TestValidationMaxTestsBreak:
    """Cover line 87: inner break when max_tests in conditioning."""

    def test_max_tests_limits_conditioning(self):
        dag = _build_chain_dag()
        tester = DSeparationTester(dag)
        data = _make_chain_data(n=100)
        report = tester.test_all_implications(data, max_tests=2)
        assert report.total_implications == 2


class TestValidationSubsetEdgeCases:
    """Cover lines 219, 223: empty effects and tiny mean_effect."""

    def test_zero_subsets_returns_zero(self):
        """Cover line 219: empty effects list."""
        data = [{"treatment": float(i), "outcome": float(i)} for i in range(50)]
        suite = RefutationSuite(seed=42)
        cv = suite.subset_test(data, "treatment", "outcome", n_subsets=0)
        assert cv == 0.0

    def test_constant_outcome_returns_zero(self):
        """Cover line 223: near-zero mean_effect."""
        data = [{"treatment": float(i), "outcome": 5.0} for i in range(100)]
        suite = RefutationSuite(seed=42)
        cv = suite.subset_test(data, "treatment", "outcome", n_subsets=3)
        assert cv == 0.0


# ---------------------------------------------------------------------------
# fabric/regulatory_export.py coverage
# ---------------------------------------------------------------------------


class TestExportFloatSegData:
    """Cover lines 94-97: seg_data is a float (not dict)."""

    def test_float_segment_values(self) -> None:
        cecl = _make_cecl_stub(segments={"Retail": 400.0, "Corp": 600.0})
        builder = ScheduleH1Builder(cecl)
        rows = builder.build()
        assert len(rows) == 2
        assert rows[0]["ecl"] == 400.0
        assert rows[1]["ecl"] == 600.0


class TestExportDictCI:
    """Cover lines 137-138: confidence_intervals is a dict."""

    def test_dict_ci_broadcast(self) -> None:
        stress = _make_stress_stub(
            quarterly_ecl=[100.0] * 9,
            confidence_intervals={"total_ecl": (90.0, 110.0)},
        )
        builder = CCARProjectionBuilder(stress)
        rows = builder.build()
        assert len(rows) == 9
        for row in rows:
            assert row["ci_lower"] == 90.0
            assert row["ci_upper"] == 110.0


# ---------------------------------------------------------------------------
# credit/regulatory_report.py coverage
# ---------------------------------------------------------------------------


class TestReportECLOutsideBand:
    """Cover line 263: ECL outside confidence band triggers alert."""

    def test_alert_div_appears(self) -> None:
        ecl = ECLResult(
            total_ecl=50_000.0,
            ecl_by_segment={
                "retail": 30_000.0,
                "commercial": 20_000.0,
            },
            horizon_quarters=1,
            horizon_method="soliton_dynamic",
            confidence_band=(8_000.0, 13_000.0),
            pd_mean=0.05,
            lgd=0.45,
            ead_total=1_000_000.0,
        )
        exp = RegulatorReportExporter(
            stress_report=_make_stress_report(),
            risk_report=_make_risk_report(),
            ecl_result=ecl,
        )
        out = exp.to_html()
        assert "alert" in out


class TestReportEmptyMigration:
    """Cover lines 333, 515: empty migration matrices."""

    @staticmethod
    def _no_migration() -> StressTestReport:
        return StressTestReport(
            scenario_name="adverse",
            quarterly_ecl=[10_000.0] * 9,
            portfolio_loss_rate=0.08,
            capital_impact=0.007,
            migration_matrices=[],
            confidence_intervals={
                "total_ecl": (80_000.0, 130_000.0),
                "portfolio_loss_rate": (0.07, 0.11),
                "capital_impact": (0.006, 0.009),
            },
            generated_at="2026-02-18T09:00:00+00:00",
        )

    def test_html_no_migration_message(self) -> None:
        exp = RegulatorReportExporter(
            stress_report=self._no_migration(),
            risk_report=_make_risk_report(),
            ecl_result=_make_ecl_result(),
        )
        assert "No migration data available" in exp.to_html()

    def test_md_no_migration_message(self) -> None:
        exp = RegulatorReportExporter(
            stress_report=self._no_migration(),
            risk_report=_make_risk_report(),
            ecl_result=_make_ecl_result(),
        )
        assert "No migration data available" in exp.to_markdown()


class TestReportCapitalAdequacyOff:
    """Cover line 363: include_capital_adequacy=False."""

    def test_no_capital_heading(self) -> None:
        cfg = RegulatorReportConfig(include_capital_adequacy=False)
        exp = RegulatorReportExporter(
            stress_report=_make_stress_report(),
            risk_report=_make_risk_report(),
            ecl_result=_make_ecl_result(),
            config=cfg,
        )
        assert "<h2>Capital Adequacy" not in exp.to_html()


class TestReportZeroEadSector:
    """Cover line 570: zero total_ead in _sector_concentration."""

    def test_zero_ead_returns_na(self) -> None:
        portfolio = [
            LoanExposure(
                loan_id="LN-0001",
                pd=0.05,
                lgd=0.4,
                ead=0.0,
                sector="retail",
                vintage="2024Q1",
            ),
        ]
        exp = RegulatorReportExporter(
            stress_report=_make_stress_report(),
            risk_report=_make_risk_report(),
            ecl_result=_make_ecl_result(),
            portfolio=portfolio,
        )
        assert exp._sector_concentration() == [("N/A", 0.0)]


class TestReportFormatHelpers:
    """Cover lines 584, 595: _fmt_currency/_fmt_pct with NaN/Inf."""

    def test_fmt_currency_nan(self) -> None:
        result = RegulatorReportExporter._fmt_currency(float("nan"))
        assert result == "N/A"

    def test_fmt_currency_inf(self) -> None:
        result = RegulatorReportExporter._fmt_currency(float("inf"))
        assert result == "N/A"

    def test_fmt_pct_nan(self) -> None:
        result = RegulatorReportExporter._fmt_pct(float("nan"))
        assert result == "N/A"

    def test_fmt_pct_inf(self) -> None:
        result = RegulatorReportExporter._fmt_pct(float("inf"))
        assert result == "N/A"
