"""
Format Variants Generator

Generates multiple format variants for invoice field values.
Used by both inference (to try different extractions) and matching (to match CSV values).
"""

import re
from datetime import datetime
from typing import Optional

from .text_cleaner import TextCleaner


class FormatVariants:
    """
    Generates format variants for different field types.

    The same logic is used for:
    - Inference: trying different formats to extract a value
    - Matching: generating variants of CSV values to match against OCR text
    """

    # Swedish month names for date parsing
    SWEDISH_MONTHS = {
        'januari': '01', 'jan': '01',
        'februari': '02', 'feb': '02',
        'mars': '03', 'mar': '03',
        'april': '04', 'apr': '04',
        'maj': '05',
        'juni': '06', 'jun': '06',
        'juli': '07', 'jul': '07',
        'augusti': '08', 'aug': '08',
        'september': '09', 'sep': '09', 'sept': '09',
        'oktober': '10', 'okt': '10',
        'november': '11', 'nov': '11',
        'december': '12', 'dec': '12',
    }

    # =========================================================================
    # Organization Number Variants
    # =========================================================================

    @classmethod
    def organisation_number_variants(cls, value: str) -> list[str]:
        """
        Generate all format variants for Swedish organization number.

        Input formats handled:
        - "556123-4567" (standard with hyphen)
        - "5561234567" (no hyphen)
        - "SE556123456701" (VAT format)
        - "SE 556123-4567 01" (VAT with spaces)

        Returns all possible variants for matching.
        """
        value = TextCleaner.clean_text(value)
        value_upper = value.upper()
        variants = set()

        # 提取纯数字
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=True)

        # 如果是 VAT 格式，提取中间的 org number
        # SE + 10 digits + 01 = "SE556123456701"
        if value_upper.startswith('SE') and len(digits) == 12 and digits.endswith('01'):
            # VAT format: SE + org_number + 01
            digits = digits[:10]
        elif digits.startswith('46') and len(digits) == 14:
            # SE prefix in numeric (46 is SE in phone code): 46 + 10 digits + 01
            digits = digits[2:12]

        if len(digits) == 12:
            # 12 位数字可能是带世纪前缀的: NNNNNNNN-NNNN (19556123-4567)
            variants.add(value)
            variants.add(digits)  # 195561234567
            # 带横线格式
            variants.add(f"{digits[:8]}-{digits[8:]}")  # 19556123-4567
            # 提取后 10 位作为标准 org number
            short_digits = digits[2:]  # 5561234567
            variants.add(short_digits)
            variants.add(f"{short_digits[:6]}-{short_digits[6:]}")  # 556123-4567
            # VAT 格式
            variants.add(f"SE{short_digits}01")  # SE556123456701
            return list(v for v in variants if v)

        if len(digits) != 10:
            # 如果不是标准 10 位，返回原始值和清洗后的变体
            variants.add(value)
            if digits:
                variants.add(digits)
            return list(variants)

        # 生成所有变体
        # 1. 纯数字
        variants.add(digits)  # 5561234567

        # 2. 标准格式 (NNNNNN-NNNN)
        with_hyphen = f"{digits[:6]}-{digits[6:]}"
        variants.add(with_hyphen)  # 556123-4567

        # 3. VAT 格式
        vat_compact = f"SE{digits}01"
        variants.add(vat_compact)  # SE556123456701
        variants.add(vat_compact.lower())  # se556123456701

        vat_spaced = f"SE {digits[:6]}-{digits[6:]} 01"
        variants.add(vat_spaced)  # SE 556123-4567 01

        vat_spaced_no_hyphen = f"SE {digits} 01"
        variants.add(vat_spaced_no_hyphen)  # SE 5561234567 01

        # 4. 有时带国家代码但无 01 后缀
        variants.add(f"SE{digits}")  # SE5561234567
        variants.add(f"SE {digits}")  # SE 5561234567
        variants.add(f"SE{digits[:6]}-{digits[6:]}")  # SE556123-4567

        # 5. OCR 可能的错误变体
        ocr_variants = TextCleaner.generate_ocr_variants(digits)
        for ocr_var in ocr_variants:
            if len(ocr_var) == 10:
                variants.add(ocr_var)
                variants.add(f"{ocr_var[:6]}-{ocr_var[6:]}")

        return list(v for v in variants if v)

    # =========================================================================
    # Bankgiro Variants
    # =========================================================================

    @classmethod
    def bankgiro_variants(cls, value: str) -> list[str]:
        """
        Generate variants for Bankgiro number.

        Formats:
        - 7 digits: XXX-XXXX (e.g., 123-4567)
        - 8 digits: XXXX-XXXX (e.g., 1234-5678)
        """
        value = TextCleaner.clean_text(value)
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=True)
        variants = set()

        variants.add(value)

        if not digits or len(digits) < 7 or len(digits) > 8:
            return list(v for v in variants if v)

        # 纯数字
        variants.add(digits)

        # 带横线格式
        if len(digits) == 7:
            variants.add(f"{digits[:3]}-{digits[3:]}")  # XXX-XXXX
        elif len(digits) == 8:
            variants.add(f"{digits[:4]}-{digits[4:]}")  # XXXX-XXXX
            # 有些 8 位也用 XXX-XXXXX 格式
            variants.add(f"{digits[:3]}-{digits[3:]}")

        # 带空格格式 (有时 OCR 会这样识别)
        if len(digits) == 7:
            variants.add(f"{digits[:3]} {digits[3:]}")
        elif len(digits) == 8:
            variants.add(f"{digits[:4]} {digits[4:]}")

        # OCR 错误变体
        for ocr_var in TextCleaner.generate_ocr_variants(digits):
            variants.add(ocr_var)

        return list(v for v in variants if v)

    # =========================================================================
    # Plusgiro Variants
    # =========================================================================

    @classmethod
    def plusgiro_variants(cls, value: str) -> list[str]:
        """
        Generate variants for Plusgiro number.

        Format: XXXXXXX-X (7 digits + check digit) or shorter
        Examples: 1234567-8, 12345-6, 1-8
        """
        value = TextCleaner.clean_text(value)
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=True)
        variants = set()

        variants.add(value)

        if not digits or len(digits) < 2 or len(digits) > 8:
            return list(v for v in variants if v)

        # 纯数字
        variants.add(digits)

        # Plusgiro 格式: 最后一位是校验位，用横线分隔
        main_part = digits[:-1]
        check_digit = digits[-1]
        variants.add(f"{main_part}-{check_digit}")

        # 有时带空格
        variants.add(f"{main_part} {check_digit}")

        # 分组格式 (常见于长号码): XX XX XX-X
        if len(digits) >= 6:
            # 尝试 XX XX XX-X 格式
            spaced = ' '.join([digits[i:i + 2] for i in range(0, len(digits) - 1, 2)])
            if len(digits) % 2 == 0:
                spaced = spaced[:-1] + '-' + digits[-1]
            else:
                spaced = spaced + '-' + digits[-1]
            variants.add(spaced.replace('- ', '-'))

        # OCR 错误变体
        for ocr_var in TextCleaner.generate_ocr_variants(digits):
            variants.add(ocr_var)

        return list(v for v in variants if v)

    # =========================================================================
    # Amount Variants
    # =========================================================================

    @classmethod
    def amount_variants(cls, value: str) -> list[str]:
        """
        Generate variants for monetary amounts.

        Handles:
        - Swedish: 1 234,56 (space thousand, comma decimal)
        - German: 1.234,56 (dot thousand, comma decimal)
        - US/UK: 1,234.56 (comma thousand, dot decimal)
        - Integer: 1234 -> 1234.00

        Returns variants with different separators and with/without decimals.
        """
        value = TextCleaner.clean_text(value)
        variants = set()
        variants.add(value)

        # 尝试解析为数值
        amount = cls._parse_amount(value)
        if amount is None:
            return list(v for v in variants if v)

        # 生成不同格式的变体
        int_part = int(amount)
        dec_part = round((amount - int_part) * 100)

        # 1. 基础格式
        if dec_part == 0:
            variants.add(str(int_part))  # 1234
            variants.add(f"{int_part}.00")  # 1234.00
            variants.add(f"{int_part},00")  # 1234,00
        else:
            variants.add(f"{int_part}.{dec_part:02d}")  # 1234.56
            variants.add(f"{int_part},{dec_part:02d}")  # 1234,56

        # 2. 带千位分隔符
        int_str = str(int_part)
        if len(int_str) > 3:
            # 从右往左每3位加分隔符
            parts = []
            while int_str:
                parts.append(int_str[-3:])
                int_str = int_str[:-3]
            parts.reverse()

            # 空格分隔 (Swedish)
            space_sep = ' '.join(parts)
            if dec_part == 0:
                variants.add(space_sep)
            else:
                variants.add(f"{space_sep},{dec_part:02d}")
                variants.add(f"{space_sep}.{dec_part:02d}")

            # 点分隔 (German)
            dot_sep = '.'.join(parts)
            if dec_part == 0:
                variants.add(dot_sep)
            else:
                variants.add(f"{dot_sep},{dec_part:02d}")

            # 逗号分隔 (US)
            comma_sep = ','.join(parts)
            if dec_part == 0:
                variants.add(comma_sep)
            else:
                variants.add(f"{comma_sep}.{dec_part:02d}")

        # 3. 带货币符号
        base_amounts = [f"{int_part}.{dec_part:02d}", f"{int_part},{dec_part:02d}"]
        if dec_part == 0:
            base_amounts.append(str(int_part))

        for base in base_amounts:
            variants.add(f"{base} kr")
            variants.add(f"{base} SEK")
            variants.add(f"{base}kr")
            variants.add(f"SEK {base}")

        return list(v for v in variants if v)

    @classmethod
    def _parse_amount(cls, text: str) -> Optional[float]:
        """Parse amount from various formats."""
        text = TextCleaner.normalize_amount_text(text)

        # 移除所有非数字和分隔符
        clean = re.sub(r'[^\d,.\s]', '', text)
        if not clean:
            return None

        # 检测格式
        # 瑞典格式: 1 234,56 或 1234,56
        if re.match(r'^[\d\s]+,\d{2}$', clean):
            clean = clean.replace(' ', '').replace(',', '.')
            try:
                return float(clean)
            except ValueError:
                pass

        # 德国格式: 1.234,56
        if re.match(r'^[\d.]+,\d{2}$', clean):
            clean = clean.replace('.', '').replace(',', '.')
            try:
                return float(clean)
            except ValueError:
                pass

        # 美国格式: 1,234.56
        if re.match(r'^[\d,]+\.\d{2}$', clean):
            clean = clean.replace(',', '')
            try:
                return float(clean)
            except ValueError:
                pass

        # 简单格式
        clean = clean.replace(' ', '').replace(',', '.')
        # 如果有多个点，只保留最后一个
        if clean.count('.') > 1:
            parts = clean.rsplit('.', 1)
            clean = parts[0].replace('.', '') + '.' + parts[1]

        try:
            return float(clean)
        except ValueError:
            return None

    # =========================================================================
    # Date Variants
    # =========================================================================

    @classmethod
    def date_variants(cls, value: str) -> list[str]:
        """
        Generate variants for dates.

        Input can be:
        - ISO: 2024-12-29
        - European: 29/12/2024, 29.12.2024
        - Swedish text: "29 december 2024"
        - Compact: 20241229

        Returns all format variants.
        """
        value = TextCleaner.clean_text(value)
        variants = set()
        variants.add(value)

        # 尝试解析日期
        parsed = cls._parse_date(value)
        if parsed is None:
            return list(v for v in variants if v)

        year, month, day = parsed

        # 生成所有格式变体
        # ISO
        variants.add(f"{year}-{month:02d}-{day:02d}")
        variants.add(f"{year}-{month}-{day}")  # 不补零

        # 点分隔 (Swedish common)
        variants.add(f"{year}.{month:02d}.{day:02d}")
        variants.add(f"{day:02d}.{month:02d}.{year}")

        # 斜杠分隔
        variants.add(f"{day:02d}/{month:02d}/{year}")
        variants.add(f"{month:02d}/{day:02d}/{year}")  # US format
        variants.add(f"{year}/{month:02d}/{day:02d}")

        # 紧凑格式
        variants.add(f"{year}{month:02d}{day:02d}")

        # 带月份名 (Swedish)
        for month_name, month_num in cls.SWEDISH_MONTHS.items():
            if month_num == f"{month:02d}":
                variants.add(f"{day} {month_name} {year}")
                variants.add(f"{day:02d} {month_name} {year}")
                # 首字母大写
                variants.add(f"{day} {month_name.capitalize()} {year}")

        # 短年份
        short_year = str(year)[2:]
        variants.add(f"{day:02d}.{month:02d}.{short_year}")
        variants.add(f"{day:02d}/{month:02d}/{short_year}")
        variants.add(f"{short_year}-{month:02d}-{day:02d}")

        return list(v for v in variants if v)

    @classmethod
    def _parse_date(cls, text: str) -> Optional[tuple[int, int, int]]:
        """
        Parse date from text, returns (year, month, day) or None.
        """
        text = TextCleaner.clean_text(text).lower()

        # ISO: 2024-12-29
        match = re.search(r'(\d{4})-(\d{1,2})-(\d{1,2})', text)
        if match:
            return int(match.group(1)), int(match.group(2)), int(match.group(3))

        # Dot format: 2024.12.29
        match = re.search(r'(\d{4})\.(\d{1,2})\.(\d{1,2})', text)
        if match:
            return int(match.group(1)), int(match.group(2)), int(match.group(3))

        # European: 29/12/2024 or 29.12.2024
        match = re.search(r'(\d{1,2})[/.](\d{1,2})[/.](\d{4})', text)
        if match:
            day, month, year = int(match.group(1)), int(match.group(2)), int(match.group(3))
            # 验证日期合理性
            if 1 <= day <= 31 and 1 <= month <= 12:
                return year, month, day

        # Compact: 20241229
        match = re.search(r'(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)', text)
        if match:
            year, month, day = int(match.group(1)), int(match.group(2)), int(match.group(3))
            if 2000 <= year <= 2100 and 1 <= month <= 12 and 1 <= day <= 31:
                return year, month, day

        # Swedish month name: "29 december 2024"
        for month_name, month_num in cls.SWEDISH_MONTHS.items():
            pattern = rf'(\d{{1,2}})\s*{month_name}\s*(\d{{4}})'
            match = re.search(pattern, text)
            if match:
                day, year = int(match.group(1)), int(match.group(2))
                return year, int(month_num), day

        return None

    # =========================================================================
    # Invoice Number Variants
    # =========================================================================

    @classmethod
    def invoice_number_variants(cls, value: str) -> list[str]:
        """
        Generate variants for invoice numbers.

        Invoice numbers are highly variable:
        - Pure digits: 12345678
        - Alphanumeric: A3861, INV-2024-001
        - With separators: 2024/001
        """
        value = TextCleaner.clean_text(value)
        variants = set()
        variants.add(value)

        # 提取数字部分
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=False)
        if digits:
            variants.add(digits)

        # 大小写变体
        variants.add(value.upper())
        variants.add(value.lower())

        # 移除分隔符
        no_sep = re.sub(r'[-/\s]', '', value)
        variants.add(no_sep)
        variants.add(no_sep.upper())

        # OCR 错误变体
        for ocr_var in TextCleaner.generate_ocr_variants(value):
            variants.add(ocr_var)

        return list(v for v in variants if v)

    # =========================================================================
    # OCR Number Variants
    # =========================================================================

    @classmethod
    def ocr_number_variants(cls, value: str) -> list[str]:
        """
        Generate variants for OCR reference numbers.

        OCR numbers are typically 10-25 digits.
        """
        value = TextCleaner.clean_text(value)
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=True)
        variants = set()

        variants.add(value)

        if digits:
            variants.add(digits)

            # 有些 OCR 号码带空格分组
            if len(digits) > 4:
                # 每 4 位分组
                spaced = ' '.join([digits[i:i + 4] for i in range(0, len(digits), 4)])
                variants.add(spaced)

            # OCR 错误变体
            for ocr_var in TextCleaner.generate_ocr_variants(digits):
                variants.add(ocr_var)

        return list(v for v in variants if v)

    # =========================================================================
    # Customer Number Variants
    # =========================================================================

    @classmethod
    def customer_number_variants(cls, value: str) -> list[str]:
        """
        Generate variants for customer numbers.

        Customer numbers can be very diverse:
        - Pure digits: 12345
        - Alphanumeric: ABC123, EMM 256-6
        - With separators: 123-456
        """
        value = TextCleaner.clean_text(value)
        variants = set()
        variants.add(value)

        # 大小写
        variants.add(value.upper())
        variants.add(value.lower())

        # 移除所有分隔符和空格
        compact = re.sub(r'[-/\s]', '', value)
        variants.add(compact)
        variants.add(compact.upper())
        variants.add(compact.lower())

        # 纯数字
        digits = TextCleaner.extract_digits(value, apply_ocr_correction=False)
        if digits:
            variants.add(digits)

        # 纯字母 + 数字 (分离)
        letters = re.sub(r'[^a-zA-Z]', '', value)
        if letters and digits:
            variants.add(f"{letters}{digits}")
            variants.add(f"{letters.upper()}{digits}")
            variants.add(f"{letters} {digits}")
            variants.add(f"{letters.upper()} {digits}")
            variants.add(f"{letters}-{digits}")
            variants.add(f"{letters.upper()}-{digits}")

        # OCR 错误变体
        for ocr_var in TextCleaner.generate_ocr_variants(value):
            variants.add(ocr_var)

        return list(v for v in variants if v)

    # =========================================================================
    # Generic Field Variants
    # =========================================================================

    @classmethod
    def get_variants(cls, field_name: str, value: str) -> list[str]:
        """
        Get variants for a field by name.

        This is the main entry point - dispatches to specific variant generators.
        """
        if not value:
            return []

        field_lower = field_name.lower()

        # 映射字段名到变体生成器
        if 'organisation' in field_lower or 'org' in field_lower:
            return cls.organisation_number_variants(value)
        elif 'bankgiro' in field_lower or field_lower == 'bg':
            return cls.bankgiro_variants(value)
        elif 'plusgiro' in field_lower or field_lower == 'pg':
            return cls.plusgiro_variants(value)
        elif 'amount' in field_lower or 'belopp' in field_lower:
            return cls.amount_variants(value)
        elif 'date' in field_lower or 'datum' in field_lower:
            return cls.date_variants(value)
        elif 'invoice' in field_lower and 'number' in field_lower:
            return cls.invoice_number_variants(value)
        elif field_lower == 'invoicenumber':
            return cls.invoice_number_variants(value)
        elif 'ocr' in field_lower:
            return cls.ocr_number_variants(value)
        elif 'customer' in field_lower:
            return cls.customer_number_variants(value)
        else:
            # 默认: 返回原值和基本清洗
            return [value, TextCleaner.clean_text(value)]
