"""BLCE E2E Handoff Loader — consume E2E pipeline outputs into BLCE.

Reads existing E2E workflow context dicts and ``run_summary.json`` files,
converting them into BLCE contracts (``E2EHandoff``, ``LogicArtifact``).
"""
from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any, Dict, List, Optional

from .constants import SourceType
from .contracts import (
    Dependency,
    E2EHandoff,
    GrainColumn,
    LogicArtifact,
    LogicObjects,
    Measure,
)

logger = logging.getLogger(__name__)


class E2EHandoffLoader:
    """Load E2E pipeline outputs and convert to BLCE artifacts.

    Parameters
    ----------
    run_dir : str | None
        Directory containing ``run_summary.json`` and related files.
    run_id : str | None
        E2E run identifier (for metadata).
    """

    def __init__(
        self,
        run_dir: Optional[str] = None,
        run_id: Optional[str] = None,
    ):
        self.run_dir = run_dir
        self.run_id = run_id or ""

    # -- load from context dict --------------------------------------------

    def load_from_context(self, context: Dict[str, Any]) -> E2EHandoff:
        """Map E2E workflow context keys into an ``E2EHandoff``.

        Expected context shape::

            context["ai_classify"]["table_profiles"] -> table_profiles
            context["ai_classify"]["classifications"] -> classifications
            context["graph_sync"]["relationships"] -> relationships
            context["load_metadata"]["tables"] -> table_summaries
        """
        ai_classify = context.get("ai_classify", {})
        graph_sync = context.get("graph_sync", {})
        load_meta = context.get("load_metadata", {})
        detect_dims = context.get("detect_dimensions", {})

        # Include enhanced table analyses in run_summary for downstream
        run_summary = context.get("run_summary", {})
        if isinstance(run_summary, dict) and detect_dims:
            run_summary.update({
                "table_analyses": detect_dims.get("table_analyses", {}),
                "natural_keys": detect_dims.get("natural_keys", {}),
                "purpose_summaries": detect_dims.get("purpose_summaries", {}),
                "domain_groups": detect_dims.get("domain_groups", {}),
            })

        return E2EHandoff(
            e2e_run_id=self.run_id or context.get("run_id", ""),
            source_dir=self.run_dir or "",
            table_profiles=ai_classify.get("table_profiles", []),
            classifications=ai_classify.get("classifications", []),
            relationships=graph_sync.get("relationships", []),
            table_summaries=load_meta.get("tables", []),
            run_summary=run_summary,
        )

    # -- load from directory -----------------------------------------------

    def load_from_directory(self, run_dir: Optional[str] = None) -> E2EHandoff:
        """Load from a directory containing ``run_summary.json``.

        Falls back gracefully if files are missing.
        """
        d = Path(run_dir or self.run_dir or "")
        if not d.is_dir():
            return E2EHandoff(
                e2e_run_id=self.run_id,
                source_dir=str(d),
                run_summary={"error": f"Directory not found: {d}"},
            )

        summary_path = d / "run_summary.json"
        run_summary: Dict[str, Any] = {}
        if summary_path.exists():
            try:
                run_summary = json.loads(summary_path.read_text(encoding="utf-8"))
            except Exception as exc:
                run_summary = {"error": str(exc)}

        # Attempt to load datashield project files
        table_profiles: List[Dict[str, Any]] = []
        classifications: List[Dict[str, Any]] = []
        table_summaries: List[Dict[str, Any]] = []

        # Look for typical E2E output files
        for json_file in sorted(d.glob("*.json")):
            if json_file.name == "run_summary.json":
                continue
            try:
                data = json.loads(json_file.read_text(encoding="utf-8"))
                if isinstance(data, list):
                    # Heuristic: check first item keys
                    if data and isinstance(data[0], dict):
                        keys = set(data[0].keys())
                        if "column_count" in keys or "row_count" in keys:
                            table_profiles.extend(data)
                        elif "classification" in keys:
                            classifications.extend(data)
                        elif "table_name" in keys:
                            table_summaries.extend(data)
            except Exception:
                pass

        return E2EHandoff(
            e2e_run_id=self.run_id or run_summary.get("run_id", ""),
            source_dir=str(d),
            table_profiles=table_profiles,
            classifications=classifications,
            table_summaries=table_summaries,
            run_summary=run_summary,
        )

    # -- convert to artifacts ----------------------------------------------

    def convert_to_artifacts(self, handoff: E2EHandoff) -> List[LogicArtifact]:
        """Convert each table profile into a ``LogicArtifact``.

        Confidence scoring:
            base 0.30
            + 0.20 if has numeric columns (measures)
            + 0.20 if has relationships
            + 0.15 if has classifications
            + 0.15 if has table summary
        """
        # Build lookup indexes
        class_by_table: Dict[str, List[Dict[str, Any]]] = {}
        for c in handoff.classifications:
            tbl = c.get("table_name", c.get("table", ""))
            class_by_table.setdefault(tbl, []).append(c)

        rel_by_table: Dict[str, List[Dict[str, Any]]] = {}
        for r in handoff.relationships:
            src = r.get("source_table", r.get("src_table", ""))
            rel_by_table.setdefault(src, []).append(r)

        summary_by_table: Dict[str, Dict[str, Any]] = {}
        for s in handoff.table_summaries:
            tbl = s.get("table_name", s.get("name", ""))
            summary_by_table[tbl] = s

        artifacts: List[LogicArtifact] = []
        for profile in handoff.table_profiles:
            table_name = profile.get("table_name", profile.get("name", "unknown"))
            columns = profile.get("columns", [])

            # Extract measures from numeric columns
            measures: List[Measure] = []
            grain_cols: List[GrainColumn] = []
            for col in columns:
                col_name = col if isinstance(col, str) else col.get("name", "")
                col_type = "" if isinstance(col, str) else col.get("type", "").lower()
                if col_type in ("number", "numeric", "float", "int", "decimal"):
                    measures.append(Measure(
                        name=col_name,
                        expression=f"SUM({col_name})",
                        source_table=table_name,
                        source_columns=[col_name],
                    ))
                elif "id" in col_name.lower() or "key" in col_name.lower():
                    grain_cols.append(GrainColumn(column=col_name, table=table_name))

            # Extract dependencies from relationships
            deps: List[Dependency] = []
            for r in rel_by_table.get(table_name, []):
                tgt = r.get("target_table", r.get("tgt_table", ""))
                if tgt:
                    deps.append(Dependency(name=tgt, dep_type="table"))

            # Confidence scoring
            has_measures = len(measures) > 0
            has_rels = table_name in rel_by_table
            has_class = table_name in class_by_table
            has_summary = table_name in summary_by_table

            confidence = 0.30
            if has_measures:
                confidence += 0.20
            if has_rels:
                confidence += 0.20
            if has_class:
                confidence += 0.15
            if has_summary:
                confidence += 0.15
            confidence = min(1.0, confidence)

            grain_desc = ", ".join(g.column for g in grain_cols) if grain_cols else ""
            summary_text = summary_by_table.get(table_name, {}).get("summary", "")

            art = LogicArtifact(
                source_type=SourceType.SQL,
                source_name=table_name,
                source_path=handoff.source_dir,
                grain=grain_desc,
                confidence=round(confidence, 2),
                explanation=summary_text,
                objects=LogicObjects(
                    measures=measures,
                    grain_columns=grain_cols,
                    dependencies=deps,
                ),
                metadata={
                    "e2e_handoff_id": handoff.handoff_id,
                    "e2e_run_id": handoff.e2e_run_id,
                    "classifications": class_by_table.get(table_name, []),
                },
            )
            artifacts.append(art)

        return artifacts

    # -- summary -----------------------------------------------------------

    def get_summary(self, handoff: E2EHandoff) -> Dict[str, Any]:
        """Return counts of profiles, classifications, relationships, summaries."""
        return {
            "handoff_id": handoff.handoff_id,
            "e2e_run_id": handoff.e2e_run_id,
            "table_profiles": len(handoff.table_profiles),
            "classifications": len(handoff.classifications),
            "relationships": len(handoff.relationships),
            "table_summaries": len(handoff.table_summaries),
            "has_run_summary": bool(handoff.run_summary),
        }
