"""
Date Normalizer

Handles normalization and validation of invoice dates.
"""

import re
from datetime import datetime

from shared.utils.validators import FieldValidators
from shared.utils.ocr_corrections import OCRCorrections

from .base import BaseNormalizer, NormalizationResult


class DateNormalizer(BaseNormalizer):
    """
    Normalizes dates from Swedish invoices.

    Handles various date formats:
    - 2025-08-29 (ISO format)
    - 2025.08.29 (dot separator)
    - 29/08/2025 (European format)
    - 29.08.2025 (European with dots)
    - 20250829 (compact format)

    Output format: YYYY-MM-DD (ISO 8601)
    """

    # Date patterns with their parsing logic
    PATTERNS = [
        # ISO format: 2025-08-29
        (
            r"(\d{4})-(\d{1,2})-(\d{1,2})",
            lambda m: (int(m.group(1)), int(m.group(2)), int(m.group(3))),
        ),
        # Dot format: 2025.08.29 (common in Swedish)
        (
            r"(\d{4})\.(\d{1,2})\.(\d{1,2})",
            lambda m: (int(m.group(1)), int(m.group(2)), int(m.group(3))),
        ),
        # European slash format: 29/08/2025
        (
            r"(\d{1,2})/(\d{1,2})/(\d{4})",
            lambda m: (int(m.group(3)), int(m.group(2)), int(m.group(1))),
        ),
        # European dot format: 29.08.2025
        (
            r"(\d{1,2})\.(\d{1,2})\.(\d{4})",
            lambda m: (int(m.group(3)), int(m.group(2)), int(m.group(1))),
        ),
        # Compact format: 20250829
        (
            r"(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)",
            lambda m: (int(m.group(1)), int(m.group(2)), int(m.group(3))),
        ),
    ]

    @property
    def field_name(self) -> str:
        return "Date"

    def normalize(self, text: str) -> NormalizationResult:
        text = text.strip()
        if not text:
            return NormalizationResult.failure("Empty text")

        # First, try using shared validator
        iso_date = FieldValidators.format_date_iso(text)
        if iso_date and FieldValidators.is_valid_date(iso_date):
            return NormalizationResult.success(iso_date)

        # Fallback: try original patterns for edge cases
        for pattern, extractor in self.PATTERNS:
            match = re.search(pattern, text)
            if match:
                try:
                    year, month, day = extractor(match)
                    # Validate date
                    parsed_date = datetime(year, month, day)
                    # Sanity check: year should be reasonable (2000-2100)
                    if 2000 <= parsed_date.year <= 2100:
                        return NormalizationResult.success(
                            parsed_date.strftime("%Y-%m-%d")
                        )
                except ValueError:
                    continue

        return NormalizationResult.failure(f"Cannot parse date: {text}")


class EnhancedDateNormalizer(DateNormalizer):
    """
    Enhanced date parsing with comprehensive format support.

    Additional support for:
    - Swedish text: "29 december 2024", "29 dec 2024"
    - OCR error correction: 2O24-12-29 -> 2024-12-29
    """

    # Swedish month names
    SWEDISH_MONTHS = {
        "januari": 1,
        "jan": 1,
        "februari": 2,
        "feb": 2,
        "mars": 3,
        "mar": 3,
        "april": 4,
        "apr": 4,
        "maj": 5,
        "juni": 6,
        "jun": 6,
        "juli": 7,
        "jul": 7,
        "augusti": 8,
        "aug": 8,
        "september": 9,
        "sep": 9,
        "sept": 9,
        "oktober": 10,
        "okt": 10,
        "november": 11,
        "nov": 11,
        "december": 12,
        "dec": 12,
    }

    # Extended patterns
    EXTENDED_PATTERNS = [
        # ISO format: 2025-08-29, 2025/08/29
        ("ymd", r"(\d{4})[-/](\d{1,2})[-/](\d{1,2})"),
        # Dot format: 2025.08.29
        ("ymd", r"(\d{4})\.(\d{1,2})\.(\d{1,2})"),
        # European slash: 29/08/2025
        ("dmy", r"(\d{1,2})/(\d{1,2})/(\d{4})"),
        # European dot: 29.08.2025
        ("dmy", r"(\d{1,2})\.(\d{1,2})\.(\d{4})"),
        # European dash: 29-08-2025
        ("dmy", r"(\d{1,2})-(\d{1,2})-(\d{4})"),
        # Compact: 20250829
        ("ymd_compact", r"(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)"),
    ]

    def normalize(self, text: str) -> NormalizationResult:
        text = text.strip()
        if not text:
            return NormalizationResult.failure("Empty text")

        # Apply OCR corrections
        corrected_text = OCRCorrections.correct_digits(text, aggressive=False).corrected

        # Try shared validator first
        iso_date = FieldValidators.format_date_iso(corrected_text)
        if iso_date and FieldValidators.is_valid_date(iso_date):
            return NormalizationResult.success(iso_date)

        # Try Swedish text date pattern: "29 december 2024" or "29 dec 2024"
        swedish_pattern = r"(\d{1,2})\s+([a-z\u00e5\u00e4\u00f6]+)\s+(\d{4})"
        match = re.search(swedish_pattern, corrected_text.lower())
        if match:
            day = int(match.group(1))
            month_name = match.group(2)
            year = int(match.group(3))
            if month_name in self.SWEDISH_MONTHS:
                month = self.SWEDISH_MONTHS[month_name]
                try:
                    dt = datetime(year, month, day)
                    if 2000 <= dt.year <= 2100:
                        return NormalizationResult.success(dt.strftime("%Y-%m-%d"))
                except ValueError:
                    pass

        # Extended patterns
        for fmt, pattern in self.EXTENDED_PATTERNS:
            match = re.search(pattern, corrected_text)
            if match:
                try:
                    if fmt == "ymd":
                        year = int(match.group(1))
                        month = int(match.group(2))
                        day = int(match.group(3))
                    elif fmt == "dmy":
                        day = int(match.group(1))
                        month = int(match.group(2))
                        year = int(match.group(3))
                    elif fmt == "ymd_compact":
                        year = int(match.group(1))
                        month = int(match.group(2))
                        day = int(match.group(3))
                    else:
                        continue

                    dt = datetime(year, month, day)
                    if 2000 <= dt.year <= 2100:
                        return NormalizationResult.success(dt.strftime("%Y-%m-%d"))
                except ValueError:
                    continue

        return NormalizationResult.failure(f"Cannot parse date: {text[:50]}")
