"""Integration facade — composes Rings 1-3 into high-level operations.

OrxaqEngine is the primary Python API entry point and the backend
for the CLI. All ring imports are lazy (inside methods) for fast startup.
Stdlib-only (ADR-021).
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from orxaq.kernel import AuditLog, WorldDAG

if TYPE_CHECKING:
    from orxaq.fabric.catalog import DataCatalog


class OrxaqEngine:
    """Main entry point for Orxaq operations.

    Composes kernel, credit, fabric, and intelligence rings
    into high-level operations for the CLI and Python API.
    """

    def __init__(self) -> None:
        self.audit_log = AuditLog()
        self.world_dag = WorldDAG()
        self._catalog: DataCatalog | None = None

    @property
    def catalog(self):
        """Lazy-init DataCatalog."""
        if self._catalog is None:
            from orxaq.fabric.catalog import DataCatalog

            self._catalog = DataCatalog()
        return self._catalog

    # ── Credit ──────────────────────────────────────────────

    def load_credit_ontology(self) -> WorldDAG:
        """Build the 19-variable credit DAG."""
        from orxaq.credit.ontology import build_credit_ontology

        dag = build_credit_ontology()
        self.world_dag = dag
        self.audit_log.append(
            action="ontology_loaded",
            actor="integration",
            details="19-variable credit ontology loaded",
        )
        return dag

    def generate_synthetic(self, n: int = 1000, seed: int = 42) -> list[dict]:
        """Generate synthetic credit data with known causal structure."""
        from orxaq.credit.synthetic import CreditDataGenerator

        gen = CreditDataGenerator(self.world_dag, seed=seed)
        data = gen.generate(n)
        self.audit_log.append(
            action="synthetic_generated",
            actor="integration",
            details=f"Generated {n} synthetic rows",
        )
        return data

    def fit_pd_model(self, data: list[dict]):
        """Fit causal PD model on data."""
        from orxaq.credit.causal_pd import CausalPDModel

        model = CausalPDModel(self.world_dag)
        model.fit(data)
        self.audit_log.append(
            action="pd_model_fit",
            actor="integration",
            details=f"PD model fitted on {len(data)} rows",
        )
        return model

    def compute_ecl(self, data: list[dict], horizon: int | None = None):
        """Compute CECL expected credit loss."""
        from orxaq.credit.cecl import CECLEngine

        model = self.fit_pd_model(data)
        engine = CECLEngine(model, self.world_dag)
        if horizon is None:
            horizon = engine.dynamic_horizon()
        result = engine.expected_credit_loss(data, horizon)
        self.audit_log.append(
            action="ecl_computed",
            actor="integration",
            details=f"ECL={result.total_ecl:.4f} horizon={horizon}Q",
        )
        return result

    def stress_test(self, data: list[dict], scenario_name: str = "severely_adverse") -> dict:
        """Run CCAR stress test."""
        from orxaq.credit.scenarios import ADVERSE, BASELINE, SEVERELY_ADVERSE, ScenarioEngine

        scenarios = {
            "baseline": BASELINE,
            "adverse": ADVERSE,
            "severely_adverse": SEVERELY_ADVERSE,
        }
        scenario = scenarios.get(scenario_name, SEVERELY_ADVERSE)
        engine = ScenarioEngine(self.world_dag)
        model = self.fit_pd_model(data)
        base_pd = engine.stress_pd(model, BASELINE, data)
        stress_pd = engine.stress_pd(model, scenario, data)
        self.audit_log.append(
            action="stress_test",
            actor="integration",
            details=f"{scenario_name}: base_pd={base_pd:.4f} stress_pd={stress_pd:.4f}",
        )
        return {
            "scenario": scenario_name,
            "base_pd": round(base_pd, 6),
            "stress_pd": round(stress_pd, 6),
            "delta": round(stress_pd - base_pd, 6),
        }

    def fairness_audit(self, protected: str = "age"):
        """Run fair lending analysis."""
        from orxaq.credit.fair_lending import FairLendingAnalyzer

        analyzer = FairLendingAnalyzer(self.world_dag)
        decomp = analyzer.decompose_effect(protected, "default_flag")
        self.audit_log.append(
            action="fairness_audit",
            actor="integration",
            details=f"protected={protected} direct_effect={decomp.direct_effect:.4f}",
        )
        return decomp

    # ── Discovery ───────────────────────────────────────────

    def discover_structure(
        self,
        data: list[dict],
        variables: list[str] | None = None,
        alpha: float = 0.05,
        use_consensus: bool = True,
    ):
        """Discover causal structure from data."""
        if variables is None:
            variables = sorted(data[0].keys()) if data else []

        if use_consensus:
            from orxaq.intelligence.consensus import ConsensusEngine

            engine = ConsensusEngine(alpha=alpha)
            result = engine.discover(data, variables)
        else:
            from orxaq.intelligence.independence import PartialCorrelationTest
            from orxaq.intelligence.pc_algorithm import PCAlgorithm

            ci = PartialCorrelationTest()
            pc = PCAlgorithm(ci, alpha=alpha)
            result = pc.fit(data, variables)  # type: ignore[assignment]

        method = "consensus" if use_consensus else "pc"
        self.audit_log.append(
            action="discovery_run",
            actor="integration",
            details=f"method={method} variables={len(variables)}",
        )
        return result

    def validate_dag(self, data: list[dict], alpha: float = 0.05):
        """Validate current WorldDAG against data using d-separation."""
        from orxaq.intelligence.independence import PartialCorrelationTest
        from orxaq.intelligence.validation import DSeparationTester

        ci = PartialCorrelationTest()
        tester = DSeparationTester(self.world_dag, ci)
        variables = sorted(self.world_dag._entities.keys())
        report = tester.test_all_implications(data, variables=variables, alpha=alpha)
        self.audit_log.append(
            action="dag_validated",
            actor="integration",
            details=f"violations={report.violations}/{report.total_implications}",
        )
        return report

    # ── Fabric ──────────────────────────────────────────────

    def ingest_csv(self, filepath: str, schema_name: str | None = None):
        """Ingest a CSV file."""
        from orxaq.fabric.connectors import CSVConnector
        from orxaq.fabric.schema import credit_schema

        schema = credit_schema() if schema_name == "credit" else None
        conn = CSVConnector()
        dataset = conn.read(filepath, schema=schema)
        ds_id = self.catalog.register(dataset, tags=[schema_name or "raw"])
        self.audit_log.append(
            action="data_ingested",
            actor="integration",
            details=f"source={filepath} rows={dataset.row_count} id={ds_id}",
        )
        return dataset

    def profile_data(self, data: list[dict]):
        """Profile data for statistics."""
        from orxaq.fabric.profiler import DataProfiler

        profiler = DataProfiler()
        return profiler.profile(data)

    def run_quality_gates(self, data: list[dict], gate_names: list[str] | None = None):
        """Run quality gates on data."""
        from orxaq.fabric.quality import BoundsGate, CompletenessGate, QualityGate, QualityPipeline

        if not data:
            from orxaq.fabric.quality import PipelineResult

            return PipelineResult(all_passed=True, gate_results=(), overall_score=1.0)

        gates: list[QualityGate] = []
        columns = list(data[0].keys())
        names = gate_names or ["completeness"]

        if "completeness" in names:
            gates.append(CompletenessGate(columns))
        if "bounds" in names:
            for col in columns:
                gates.append(BoundsGate(col, min_val=0.0))

        if not gates:
            gates.append(CompletenessGate(columns))

        pipeline = QualityPipeline(gates, self.audit_log)
        return pipeline.run(data)

    # ── Status ──────────────────────────────────────────────

    def status(self) -> dict:
        """Return engine status."""
        return {
            "dag": self.world_dag.summary(),
            "audit_entries": len(self.audit_log),
            "catalog": self.catalog.summary(),
        }
