"""Parse existing reports into structured ReportSpec objects."""
from __future__ import annotations

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

from .contracts import ReportLineItem, ReportSpec, _new_id

logger = logging.getLogger(__name__)


class ReportSpecCompiler:
    """Compile report definitions into structured ReportSpec objects.

    Input: BLCE ReportAnalysis output, expected values from CSV/Excel/manual.
    Output: ReportSpec with structured line items, measure expressions, and
            expected values per period.
    """

    def compile(
        self,
        report_name: str,
        source_system: str,
        line_items: List[Dict[str, Any]],
        grain_columns: Optional[List[str]] = None,
        temporal_column: str = "",
        temporal_grain: str = "monthly",
        global_filters: Optional[List[Dict[str, str]]] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> ReportSpec:
        """Compile a report definition into a ReportSpec.

        Args:
            report_name: Name of the report
            source_system: Source system identifier
            line_items: List of dicts with keys:
              - label: str (e.g. "Net Revenue")
              - expression: str (e.g. "SUM(revenue) - SUM(adjustments)")
              - source_measures: List[str] (e.g. ["revenue", "adjustments"])
              - filters: List[Dict] (per-line filters)
              - expected: Dict[str, float] (e.g. {"2024-01": 1234.56})
              - group: str (report section name)
            grain_columns: GROUP BY dimensions
            temporal_column: Date column
            temporal_grain: "monthly" | "quarterly" | "yearly"
            global_filters: Report-level filters
            metadata: Additional metadata
        """
        compiled_items: List[ReportLineItem] = []
        groups: set = set()

        for item in line_items:
            label = item.get("label", "")
            expression = item.get("expression", "")
            source_measures = item.get("source_measures", [])
            filters = item.get("filters", [])
            expected = item.get("expected", {})
            group = item.get("group", "")

            if not source_measures and expression:
                source_measures = self._extract_measures(expression)

            li = ReportLineItem(
                label=label,
                measure_expression=expression,
                source_measures=source_measures,
                filters=filters,
                expected_values=expected,
                group=group,
            )
            compiled_items.append(li)
            if group:
                groups.add(group)

        return ReportSpec(
            report_name=report_name,
            source_system=source_system,
            grain_columns=grain_columns or [],
            temporal_column=temporal_column,
            temporal_grain=temporal_grain,
            line_items=compiled_items,
            filters=global_filters or [],
            groups=sorted(groups),
            metadata=metadata or {},
        )

    def compile_from_blce(
        self,
        report_analysis: Dict[str, Any],
        expected_values: Dict[str, Dict[str, float]],
    ) -> ReportSpec:
        """Compile from BLCE blce_process_report output.

        Args:
            report_analysis: Output from blce_process_report
            expected_values: {line_item_label: {period: value}}
        """
        report_name = report_analysis.get("report_name", "")
        source_system = report_analysis.get("source_system", "")
        kpis = report_analysis.get("kpis", [])
        dimensions = report_analysis.get("dimensions", [])
        filters = report_analysis.get("filters", [])

        line_items = []
        for kpi in kpis:
            label = kpi.get("name", kpi.get("label", ""))
            expression = kpi.get("expression", kpi.get("formula", ""))
            measures = kpi.get("source_columns", kpi.get("source_measures", []))
            expected = expected_values.get(label, {})

            line_items.append({
                "label": label,
                "expression": expression,
                "source_measures": measures,
                "filters": kpi.get("filters", []),
                "expected": expected,
                "group": kpi.get("section", kpi.get("group", "")),
            })

        temporal_col = ""
        temporal_grain = "monthly"
        for dim in dimensions:
            dim_name = dim if isinstance(dim, str) else dim.get("name", "")
            if any(t in dim_name.upper() for t in ("DATE", "MONTH", "YEAR", "QUARTER")):
                temporal_col = dim_name
                if "MONTH" in dim_name.upper():
                    temporal_grain = "monthly"
                elif "QUARTER" in dim_name.upper():
                    temporal_grain = "quarterly"
                elif "YEAR" in dim_name.upper():
                    temporal_grain = "yearly"
                break

        grain_cols = [d if isinstance(d, str) else d.get("name", "") for d in dimensions]

        return self.compile(
            report_name=report_name,
            source_system=source_system,
            line_items=line_items,
            grain_columns=grain_cols,
            temporal_column=temporal_col,
            temporal_grain=temporal_grain,
            global_filters=filters,
        )

    @staticmethod
    def _extract_measures(expression: str) -> List[str]:
        """Extract measure column names from an expression string."""
        pattern = r"(?:SUM|AVG|MIN|MAX|COUNT)\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)"
        matches = re.findall(pattern, expression, re.IGNORECASE)
        if matches:
            return list(dict.fromkeys(matches))
        tokens = re.findall(r"[a-zA-Z_][a-zA-Z0-9_]*", expression)
        keywords = {"SUM", "AVG", "MIN", "MAX", "COUNT", "AS", "CASE", "WHEN", "THEN", "ELSE", "END"}
        return [t for t in tokens if t.upper() not in keywords]
