"""Cross-system enterprise bus matrix with conformance scoring."""
from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional, Set

from .contracts import (
    ConformedDimension,
    EnterpriseBusMatrix,
    EnterpriseBusMatrixEntry,
    FactAlignment,
    GrainLevel,
    _new_id,
)

logger = logging.getLogger(__name__)


class EnterpriseBusMatrixGenerator:
    """Generate an enterprise bus matrix spanning all source systems.

    Extends src/data_modeling/bus_matrix.py:BusMatrixGenerator with
    cross-system conformance scoring and enterprise-level aggregation.
    """

    def __init__(self):
        self._facts: List[Dict[str, Any]] = []
        self._conformed_dims: List[ConformedDimension] = []
        self._alignments: List[FactAlignment] = []

    def add_fact(
        self,
        table_name: str,
        source_systems: List[str],
        grain: GrainLevel,
        dimension_columns: List[str],
    ) -> None:
        """Register a fact table for the bus matrix."""
        self._facts.append({
            "table_name": table_name,
            "source_systems": source_systems,
            "grain": grain,
            "dimension_columns": [d.upper() for d in dimension_columns],
        })

    def add_conformed_dimension(self, dim: ConformedDimension) -> None:
        """Register a conformed dimension."""
        self._conformed_dims.append(dim)

    def add_alignment(self, alignment: FactAlignment) -> None:
        """Register a fact alignment."""
        self._alignments.append(alignment)

    def build(self) -> EnterpriseBusMatrix:
        """Build the enterprise bus matrix."""
        all_dim_names = sorted(set(d.dimension_name for d in self._conformed_dims))
        all_systems: Set[str] = set()

        entries: List[EnterpriseBusMatrixEntry] = []
        for fact in self._facts:
            all_systems.update(fact["source_systems"])

            dims_used: Dict[str, bool] = {}
            for dim in self._conformed_dims:
                # Check if fact references this dimension
                is_conformed = self._fact_uses_dimension(fact, dim)
                dims_used[dim.dimension_name] = is_conformed

            conformance = self._calc_conformance(dims_used)
            entries.append(EnterpriseBusMatrixEntry(
                fact_table=fact["table_name"],
                source_systems=fact["source_systems"],
                grain=fact["grain"],
                dimensions=dims_used,
                conformance_score=conformance,
            ))

        overall = (
            sum(e.conformance_score for e in entries) / len(entries)
            if entries else 0.0
        )

        return EnterpriseBusMatrix(
            entries=entries,
            all_dimensions=all_dim_names,
            all_systems=sorted(all_systems),
            overall_conformance=round(overall, 4),
        )

    def conformance_report(self) -> Dict[str, Any]:
        """Generate a conformance scoring report."""
        matrix = self.build()
        dim_coverage: Dict[str, Dict[str, Any]] = {}

        for dim_name in matrix.all_dimensions:
            used_by = []
            for entry in matrix.entries:
                if entry.dimensions.get(dim_name, False):
                    used_by.append(entry.fact_table)
            coverage = len(used_by) / len(matrix.entries) if matrix.entries else 0.0
            dim_coverage[dim_name] = {
                "used_by_facts": used_by,
                "coverage": round(coverage, 4),
                "is_fully_conformed": coverage == 1.0,
            }

        fact_scores: List[Dict[str, Any]] = []
        for entry in matrix.entries:
            conformed_count = sum(1 for v in entry.dimensions.values() if v)
            total_dims = len(entry.dimensions)
            fact_scores.append({
                "fact_table": entry.fact_table,
                "source_systems": entry.source_systems,
                "grain": entry.grain.value,
                "conformed_dimensions": conformed_count,
                "total_dimensions": total_dims,
                "conformance_score": round(entry.conformance_score, 4),
            })

        return {
            "matrix_id": matrix.matrix_id,
            "overall_conformance": matrix.overall_conformance,
            "total_facts": len(matrix.entries),
            "total_dimensions": len(matrix.all_dimensions),
            "total_systems": len(matrix.all_systems),
            "systems": matrix.all_systems,
            "fact_scores": fact_scores,
            "dimension_coverage": dim_coverage,
        }

    def to_markdown(self) -> str:
        """Render the bus matrix as markdown."""
        matrix = self.build()
        if not matrix.entries:
            return "*(No fact tables registered)*"

        dim_names = matrix.all_dimensions
        header = "| Fact Table | Systems | Grain | " + " | ".join(dim_names) + " | Score |"
        sep = "|" + "|".join(["---"] * (4 + len(dim_names))) + "|"

        rows = []
        for entry in matrix.entries:
            cells = [
                entry.fact_table,
                ", ".join(entry.source_systems),
                entry.grain.value,
            ]
            for d in dim_names:
                cells.append("X" if entry.dimensions.get(d, False) else "")
            cells.append(f"{entry.conformance_score:.0%}")
            rows.append("| " + " | ".join(cells) + " |")

        return "\n".join([header, sep] + rows)

    # ------------------------------------------------------------------
    # Internal
    # ------------------------------------------------------------------

    def _fact_uses_dimension(
        self, fact: Dict[str, Any], dim: ConformedDimension,
    ) -> bool:
        """Check if a fact table references a conformed dimension."""
        fact_cols = set(fact["dimension_columns"])

        # Direct match on enterprise key
        if dim.enterprise_key_column.upper() in fact_cols:
            return True

        # Match on any source dimension key
        for src in dim.source_dims:
            key = src.get("key_column", "").upper()
            if key and key in fact_cols:
                return True

        # Match on natural key columns
        for nk in dim.natural_key_mapping.values():
            if nk.upper() in fact_cols:
                return True

        # Fuzzy: stripped dimension name appears in fact columns
        dim_base = dim.dimension_name.replace("ENT_", "").replace("DIM_", "")
        for fc in fact_cols:
            if dim_base in fc:
                return True

        return False

    @staticmethod
    def _calc_conformance(dims_used: Dict[str, bool]) -> float:
        """Calculate conformance score (fraction of dimensions that are conformed)."""
        if not dims_used:
            return 0.0
        conformed = sum(1 for v in dims_used.values() if v)
        return round(conformed / len(dims_used), 4)
