"""Measure normalizer — dedup and canonicalize measures across artifacts."""
from __future__ import annotations

import re
from collections import defaultdict
from typing import Dict, List, Tuple

from ..constants import CONFIDENCE_MEDIUM, MeasureAggregation
from ..contracts import LogicArtifact, Measure, NormalizedMeasure


def _canonicalize(name: str) -> str:
    """Convert a measure name to canonical snake_case form."""
    s = name.strip().lower()
    s = re.sub(r"[^a-z0-9_]", "_", s)
    s = re.sub(r"_+", "_", s).strip("_")
    return s


def _expression_signature(m: Measure) -> str:
    """Simplified expression signature for dedup comparison."""
    expr = m.expression.upper().strip()
    expr = re.sub(r"\s+", " ", expr)
    return expr


class MeasureNormalizer:
    """Deduplicate and normalize measures collected from multiple artifacts.

    Groups measures by canonical name, merges source information,
    detects conflicts where same name has different definitions.
    """

    def normalize(
        self, artifacts: List[LogicArtifact]
    ) -> List[NormalizedMeasure]:
        """Normalize measures across all provided artifacts.

        Returns a list of NormalizedMeasure with deduplication applied.
        """
        # Collect all measures with their artifact_id
        tagged: List[Tuple[str, Measure]] = []
        for art in artifacts:
            for m in art.objects.measures:
                tagged.append((art.artifact_id, m))

        if not tagged:
            return []

        # Group by canonical name
        groups: Dict[str, List[Tuple[str, Measure]]] = defaultdict(list)
        for aid, m in tagged:
            key = _canonicalize(m.alias or m.name or m.expression)
            if not key:
                key = _canonicalize(m.expression)
            if key:
                groups[key].append((aid, m))

        # Build normalized measures
        results: List[NormalizedMeasure] = []
        for canonical, items in groups.items():
            artifact_ids = list(dict.fromkeys(aid for aid, _ in items))
            expressions = list(dict.fromkeys(
                _expression_signature(m) for _, m in items if m.expression
            ))
            all_cols = []
            for _, m in items:
                all_cols.extend(m.source_columns)
            source_cols = list(dict.fromkeys(all_cols))

            # Detect conflicts — different expressions for same canonical name
            conflicts: List[str] = []
            if len(expressions) > 1:
                conflicts = [
                    f"Multiple definitions: {', '.join(expressions)}"
                ]

            # Average confidence across contributing measures
            confidences = [m.confidence for _, m in items if m.confidence > 0]
            avg_conf = sum(confidences) / len(confidences) if confidences else 0.0

            # Boost if seen in multiple artifacts
            if len(artifact_ids) > 1:
                avg_conf = min(avg_conf + 0.1, 1.0)

            # Pick best business name — prefer aliased names
            business_name = ""
            for _, m in items:
                if m.alias:
                    business_name = m.alias.replace("_", " ").title()
                    break
            if not business_name:
                business_name = canonical.replace("_", " ").title()

            # Build definition from the most complete expression
            definition = expressions[0] if expressions else canonical

            results.append(NormalizedMeasure(
                canonical_name=canonical,
                business_name=business_name,
                definition=definition,
                source_artifacts=artifact_ids,
                source_expressions=expressions,
                confidence=round(avg_conf, 2),
                conflicts=conflicts,
            ))

        # Sort by confidence descending
        results.sort(key=lambda nm: -nm.confidence)
        return results

    def find_low_confidence(
        self, artifacts: List[LogicArtifact], threshold: float = CONFIDENCE_MEDIUM
    ) -> List[Measure]:
        """Return measures below the confidence threshold (candidates for AI enrichment)."""
        low: List[Measure] = []
        for art in artifacts:
            for m in art.objects.measures:
                if m.confidence < threshold:
                    low.append(m)
        return low

    def compare_sources(
        self, artifacts: List[LogicArtifact]
    ) -> Dict[str, List[Dict]]:
        """Compare measures across different source types (SQL vs Python vs Excel).

        Returns a dict keyed by canonical measure name with entries per source type.
        """
        by_source: Dict[str, List[Dict]] = defaultdict(list)
        for art in artifacts:
            for m in art.objects.measures:
                key = _canonicalize(m.alias or m.name or m.expression)
                if key:
                    by_source[key].append({
                        "artifact_id": art.artifact_id,
                        "source_type": art.source_type.value,
                        "expression": m.expression,
                        "aggregation": m.aggregation.value,
                        "confidence": m.confidence,
                    })
        return dict(by_source)
