"""Generate DW SQL queries from ReportSpec to compare against report values."""
from __future__ import annotations

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

from .contracts import (
    ReportLineItem,
    ReportSpec,
    ValidationPeriod,
    ValidationScope,
    ValidationSlice,
)

logger = logging.getLogger(__name__)

# Period format patterns for SQL
_PERIOD_FORMATS = {
    ValidationPeriod.MONTH: ("YYYY-MM", "TO_CHAR({col}, 'YYYY-MM')"),
    ValidationPeriod.QUARTER: ("YYYY-QN", "TO_CHAR({col}, 'YYYY') || '-Q' || TO_CHAR({col}, 'Q')"),
    ValidationPeriod.YEAR: ("YYYY", "TO_CHAR({col}, 'YYYY')"),
}


class QueryGenerator:
    """Generate DW SQL queries from ReportSpec for parity comparison."""

    def __init__(self, schema_prefix: str = ""):
        self._schema_prefix = schema_prefix

    def generate_query(
        self,
        spec: ReportSpec,
        line_item: ReportLineItem,
        period: ValidationPeriod,
        period_value: str = "",
    ) -> str:
        """Generate a SQL query for a single line item + period.

        The query computes the line item's measure expression from the DW
        and optionally filters to a specific period value.
        """
        expression = line_item.measure_expression or self._build_default_expression(line_item)
        temporal_col = spec.temporal_column
        grain_cols = spec.grain_columns

        # SELECT clause
        select_parts = [f"{expression} AS measure_value"]
        if temporal_col:
            period_expr = self._period_expression(temporal_col, period)
            select_parts.insert(0, f"{period_expr} AS period_key")

        select_clause = ", ".join(select_parts)

        # FROM clause — assume fact table is derived from spec
        fact_table = self._infer_fact_table(spec)

        # WHERE clause
        where_parts: List[str] = []
        for f in spec.filters + line_item.filters:
            col = f.get("column", "")
            op = f.get("op", "=")
            val = f.get("value", "")
            if col:
                where_parts.append(f"{col} {op} '{val}'")

        if period_value and temporal_col:
            period_expr = self._period_expression(temporal_col, period)
            where_parts.append(f"{period_expr} = '{period_value}'")

        where_clause = f"\nWHERE {' AND '.join(where_parts)}" if where_parts else ""

        # GROUP BY clause
        group_parts: List[str] = []
        if temporal_col:
            group_parts.append(self._period_expression(temporal_col, period))

        group_clause = f"\nGROUP BY {', '.join(group_parts)}" if group_parts else ""

        return f"SELECT {select_clause}\nFROM {fact_table}{where_clause}{group_clause};"

    def generate_comparison_queries(
        self,
        spec: ReportSpec,
        validation_slice: ValidationSlice,
    ) -> Dict[str, str]:
        """Generate queries for all line items in a validation slice.

        Returns: {line_item_id: sql_query}
        """
        queries: Dict[str, str] = {}
        items = self._resolve_line_items(spec, validation_slice)

        for item in items:
            sql = self.generate_query(
                spec, item, validation_slice.period, validation_slice.period_value,
            )
            queries[item.line_id] = sql

        return queries

    def _resolve_line_items(
        self,
        spec: ReportSpec,
        validation_slice: ValidationSlice,
    ) -> List[ReportLineItem]:
        """Resolve which line items to include based on scope."""
        if validation_slice.line_item_ids:
            return [li for li in spec.line_items if li.line_id in validation_slice.line_item_ids]

        if validation_slice.scope == ValidationScope.LINE_ITEM:
            return spec.line_items[:1] if spec.line_items else []

        if validation_slice.scope == ValidationScope.LINE_GROUP:
            if spec.groups:
                target_group = spec.groups[0]
                return [li for li in spec.line_items if li.group == target_group]
            return spec.line_items

        return spec.line_items

    def _infer_fact_table(self, spec: ReportSpec) -> str:
        """Infer the fact table name from spec metadata."""
        if spec.metadata.get("fact_table"):
            table = spec.metadata["fact_table"]
        elif spec.source_system:
            table = f"FACT_{spec.source_system.upper()}"
        else:
            table = "FACT_TABLE"
        return f"{self._schema_prefix}{table}" if self._schema_prefix else table

    @staticmethod
    def _period_expression(temporal_col: str, period: ValidationPeriod) -> str:
        """Generate SQL expression for period aggregation."""
        _, template = _PERIOD_FORMATS.get(period, ("", "{col}"))
        return template.format(col=temporal_col)

    @staticmethod
    def _build_default_expression(line_item: ReportLineItem) -> str:
        """Build a default measure expression from source measures."""
        if line_item.source_measures:
            parts = [f"SUM({m})" for m in line_item.source_measures]
            return " + ".join(parts)
        return "0"
