"""Identify anti-patterns in existing reports and suggest improvements."""
from __future__ import annotations

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

from .contracts import ReportLineItem, ReportSpec

logger = logging.getLogger(__name__)


class ImprovementAdvisor:
    """Analyze reports for anti-patterns and suggest improvements."""

    def analyze(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Analyze a report spec for anti-patterns and improvement opportunities.

        Returns list of suggestions with severity (info/warning/critical).
        """
        suggestions: List[Dict[str, Any]] = []
        suggestions.extend(self._check_hardcoded_filters(spec))
        suggestions.extend(self._check_fiscal_alignment(spec))
        suggestions.extend(self._check_rounding_consistency(spec))
        suggestions.extend(self._check_redundant_calculations(spec))
        suggestions.extend(self._check_missing_subtotals(spec))
        suggestions.extend(self._check_ungrouped_items(spec))
        suggestions.extend(self._check_missing_expected_values(spec))
        return suggestions

    def _check_hardcoded_filters(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Detect hardcoded filter values that should be parameters."""
        issues = []
        for item in spec.line_items:
            for f in item.filters:
                val = f.get("value", "")
                if re.match(r"^\d{4}(-\d{2})?(-\d{2})?$", val):
                    issues.append({
                        "severity": "warning",
                        "category": "hardcoded_filter",
                        "line_item": item.label,
                        "detail": f"Hardcoded date value '{val}' in filter — should be parameterized",
                        "suggestion": "Replace with a parameter or relative date expression",
                    })
                if val.lower() in ("true", "false", "1", "0"):
                    issues.append({
                        "severity": "info",
                        "category": "hardcoded_filter",
                        "line_item": item.label,
                        "detail": f"Hardcoded boolean filter '{val}' — consider making configurable",
                        "suggestion": "Use a report parameter instead of hardcoded value",
                    })
        return issues

    def _check_fiscal_alignment(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for missing fiscal year alignment."""
        if not spec.temporal_column:
            return [{
                "severity": "warning",
                "category": "fiscal_alignment",
                "detail": "No temporal column defined — cannot validate fiscal alignment",
                "suggestion": "Define a temporal_column in the report spec",
            }]

        if spec.temporal_grain == "monthly":
            has_fiscal = any(
                "fiscal" in str(f.get("column", "")).lower() or "fy" in str(f.get("column", "")).lower()
                for f in spec.filters
            )
            if not has_fiscal:
                return [{
                    "severity": "info",
                    "category": "fiscal_alignment",
                    "detail": "Monthly report without fiscal year filter",
                    "suggestion": "Consider adding fiscal year alignment if reporting crosses calendar years",
                }]
        return []

    def _check_rounding_consistency(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for inconsistent precision across line items."""
        precisions = set()
        for item in spec.line_items:
            for val in item.expected_values.values():
                if val != 0:
                    decimal_places = len(str(val).rstrip("0").split(".")[-1]) if "." in str(val) else 0
                    precisions.add(decimal_places)

        if len(precisions) > 2:
            return [{
                "severity": "warning",
                "category": "rounding_consistency",
                "detail": f"Inconsistent decimal precision across line items: {sorted(precisions)}",
                "suggestion": "Standardize rounding to a consistent number of decimal places",
            }]
        return []

    def _check_redundant_calculations(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for redundant calculations (same measures used in multiple line items)."""
        measure_usage: Dict[str, List[str]] = {}
        for item in spec.line_items:
            for m in item.source_measures:
                measure_usage.setdefault(m, []).append(item.label)

        issues = []
        for measure, labels in measure_usage.items():
            if len(labels) > 3:
                issues.append({
                    "severity": "info",
                    "category": "redundant_calculation",
                    "detail": f"Measure '{measure}' used in {len(labels)} line items: {labels[:3]}...",
                    "suggestion": "Consider pre-calculating this measure in a CTE or intermediate table",
                })
        return issues

    def _check_missing_subtotals(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for groups without subtotal line items."""
        if not spec.groups:
            return []

        issues = []
        for group in spec.groups:
            group_items = [li for li in spec.line_items if li.group == group]
            has_subtotal = any(
                "total" in li.label.lower() or "subtotal" in li.label.lower()
                for li in group_items
            )
            if len(group_items) > 2 and not has_subtotal:
                issues.append({
                    "severity": "info",
                    "category": "missing_subtotals",
                    "detail": f"Group '{group}' has {len(group_items)} items but no subtotal",
                    "suggestion": f"Add a subtotal line item for group '{group}' as a cross-check",
                })
        return issues

    def _check_ungrouped_items(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for line items not assigned to any group."""
        if not spec.groups:
            return []
        ungrouped = [li for li in spec.line_items if not li.group]
        if ungrouped:
            return [{
                "severity": "info",
                "category": "ungrouped_items",
                "detail": f"{len(ungrouped)} line items not assigned to any group",
                "suggestion": "Assign all line items to report sections for better organization",
            }]
        return []

    def _check_missing_expected_values(self, spec: ReportSpec) -> List[Dict[str, Any]]:
        """Check for line items without expected values."""
        missing = [li for li in spec.line_items if not li.expected_values]
        if missing:
            return [{
                "severity": "critical",
                "category": "missing_expected_values",
                "detail": f"{len(missing)} line items have no expected values for comparison",
                "suggestion": "Provide expected values for all line items to enable parity testing",
            }]
        return []
