#!/usr/bin/env python3
"""CECL Lifetime Expected Credit Loss — Reference Implementation.

End-to-end pipeline: ontology → synthetic data → PD model → ECL computation.
Demonstrates soliton-dynamic horizons and per-segment ECL breakdown.

Usage:
    python -m examples.cecl_pipeline
    # or
    python examples/cecl_pipeline.py
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

# Ensure src is importable when run directly
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))

from orxaq.credit.causal_pd import CausalPDModel
from orxaq.credit.cecl import CECLEngine, ECLResult
from orxaq.credit.ontology import build_credit_ontology
from orxaq.credit.synthetic import CreditDataGenerator
from orxaq.kernel import WorldDAG


def build_ontology() -> WorldDAG:
    """Load the 19-variable credit ontology."""
    dag = build_credit_ontology()
    print(
        f"Ontology loaded: {dag.summary()['entity_count']} entities, "
        f"{dag.summary()['edge_count']} edges"
    )
    return dag


def generate_portfolio(dag: WorldDAG, n: int = 500, seed: int = 42) -> list[dict]:
    """Generate a synthetic credit portfolio with known causal structure."""
    gen = CreditDataGenerator(dag, seed=seed)
    data = gen.generate(n)
    print(f"Generated {len(data)} borrower records")
    return data


def fit_pd_model(dag: WorldDAG, data: list[dict]) -> CausalPDModel:
    """Fit causal PD model using backdoor adjustment."""
    model = CausalPDModel(dag)
    model.fit(data)

    importance = model.feature_importance()
    top_3 = list(importance.items())[:3]
    print(
        f"PD model fitted — top causal drivers: "
        f"{', '.join(f'{k} ({v:+.4f})' for k, v in top_3)}"
    )
    return model


def compute_ecl(model: CausalPDModel, dag: WorldDAG, data: list[dict]) -> ECLResult:
    """Compute CECL expected credit loss with soliton-dynamic horizons."""
    engine = CECLEngine(model, dag)

    # Dynamic horizon (soliton-based)
    result = engine.expected_credit_loss(data)
    print(f"\n{'='*60}")
    print("CECL EXPECTED CREDIT LOSS")
    print(f"{'='*60}")
    print(f"  Total ECL:       ${result.total_ecl:,.2f}")
    print(f"  PD (mean):       {result.pd_mean:.4f}")
    print(f"  LGD:             {result.lgd:.2%}")
    print(f"  EAD (total):     ${result.ead_total:,.2f}")
    print(f"  Horizon:         {result.horizon_quarters}Q ({result.horizon_method})")
    print(
        f"  Confidence band: ${result.confidence_band[0]:,.2f} — "
        f"${result.confidence_band[1]:,.2f}"
    )

    if result.ecl_by_segment:
        print(f"\n  {'Segment':<20} {'ECL':>12}")
        print(f"  {'-'*20} {'-'*12}")
        for seg, ecl in sorted(result.ecl_by_segment.items()):
            print(f"  {seg:<20} ${ecl:>11,.2f}")

    return result


def compare_horizons(model: CausalPDModel, dag: WorldDAG, data: list[dict]) -> None:
    """Compare fixed vs. dynamic horizon ECL."""
    engine = CECLEngine(model, dag)

    print(f"\n{'='*60}")
    print("HORIZON COMPARISON")
    print(f"{'='*60}")
    print(f"  {'Horizon':<20} {'ECL':>12} {'Band Low':>12} {'Band High':>12}")
    print(f"  {'-'*20} {'-'*12} {'-'*12} {'-'*12}")

    for quarters in [4, 8, 12]:
        r = engine.expected_credit_loss(data, horizon_quarters=quarters)
        print(
            f"  {quarters}Q (fixed){'':<11} ${r.total_ecl:>11,.2f} "
            f"${r.confidence_band[0]:>11,.2f} ${r.confidence_band[1]:>11,.2f}"
        )

    dynamic = engine.expected_credit_loss(data)
    print(
        f"  {dynamic.horizon_quarters}Q (soliton){'':<8} ${dynamic.total_ecl:>11,.2f} "
        f"${dynamic.confidence_band[0]:>11,.2f} ${dynamic.confidence_band[1]:>11,.2f}"
    )


def export_json(result: ECLResult) -> str:
    """Export ECL result as JSON."""
    output = {
        "total_ecl": round(result.total_ecl, 4),
        "pd_mean": round(result.pd_mean, 6),
        "lgd": result.lgd,
        "ead_total": round(result.ead_total, 2),
        "horizon_quarters": result.horizon_quarters,
        "horizon_method": result.horizon_method,
        "confidence_band": [round(x, 4) for x in result.confidence_band],
        "ecl_by_segment": {k: round(v, 4) for k, v in result.ecl_by_segment.items()},
    }
    return json.dumps(output, indent=2)


def main() -> None:
    """Run the full CECL pipeline."""
    # 1. Build ontology
    dag = build_ontology()

    # 2. Generate synthetic portfolio
    data = generate_portfolio(dag, n=500, seed=42)

    # 3. Fit causal PD model
    model = fit_pd_model(dag, data)

    # 4. Compute ECL
    result = compute_ecl(model, dag, data)

    # 5. Compare horizons
    compare_horizons(model, dag, data)

    # 6. Export
    print(f"\n{'='*60}")
    print("JSON OUTPUT")
    print(f"{'='*60}")
    print(export_json(result))


if __name__ == "__main__":
    main()
