"""
Text Cleaning Module

Provides text normalization and OCR error correction utilities.
Used by both inference (field_extractor) and matching (normalizer) stages.
"""

import re
from typing import Optional


class TextCleaner:
    """
    Unified text cleaning utilities for invoice processing.

    Handles:
    - Unicode normalization (zero-width chars, dash variants)
    - OCR error correction (O/0, l/1, etc.)
    - Whitespace normalization
    - Swedish-specific character handling
    """

    # OCR常见错误修正映射 (用于数字字段)
    # 当我们期望数字时，这些字符常被误识别
    OCR_DIGIT_CORRECTIONS = {
        'O': '0', 'o': '0',      # 字母O -> 数字0
        'Q': '0',                 # Q 有时像 0
        'l': '1', 'I': '1',      # 小写L/大写I -> 数字1
        '|': '1',                 # 竖线 -> 1
        'i': '1',                 # 小写i -> 1
        'S': '5', 's': '5',      # S -> 5
        'B': '8',                 # B -> 8
        'Z': '2', 'z': '2',      # Z -> 2
        'G': '6', 'g': '6',      # G -> 6 (在某些字体中)
        'A': '4',                 # A -> 4 (在某些字体中)
        'T': '7',                 # T -> 7 (在某些字体中)
        'q': '9',                 # q -> 9
        'D': '0',                 # D -> 0
    }

    # 反向映射：数字被误识别为字母的情况 (用于字母数字混合字段)
    OCR_LETTER_CORRECTIONS = {
        '0': 'O',
        '1': 'I',
        '5': 'S',
        '8': 'B',
        '2': 'Z',
    }

    # Unicode 特殊字符归一化
    UNICODE_NORMALIZATIONS = {
        # 各种横线/破折号 -> 标准连字符
        '\u2013': '-',  # en-dash –
        '\u2014': '-',  # em-dash —
        '\u2212': '-',  # minus sign −
        '\u00b7': '-',  # middle dot ·
        '\u2010': '-',  # hyphen ‐
        '\u2011': '-',  # non-breaking hyphen ‑
        '\u2012': '-',  # figure dash ‒
        '\u2015': '-',  # horizontal bar ―

        # 各种空格 -> 标准空格
        '\u00a0': ' ',  # non-breaking space
        '\u2002': ' ',  # en space
        '\u2003': ' ',  # em space
        '\u2009': ' ',  # thin space
        '\u200a': ' ',  # hair space

        # 零宽字符 -> 删除
        '\u200b': '',   # zero-width space
        '\u200c': '',   # zero-width non-joiner
        '\u200d': '',   # zero-width joiner
        '\ufeff': '',   # BOM / zero-width no-break space

        # 各种引号 -> 标准引号
        '\u2018': "'",  # left single quote '
        '\u2019': "'",  # right single quote '
        '\u201c': '"',  # left double quote "
        '\u201d': '"',  # right double quote "
    }

    @classmethod
    def clean_unicode(cls, text: str) -> str:
        """
        Normalize Unicode characters to ASCII equivalents.

        Handles:
        - Various dash types -> standard hyphen (-)
        - Various spaces -> standard space
        - Zero-width characters -> removed
        - Various quotes -> standard quotes
        """
        for unicode_char, replacement in cls.UNICODE_NORMALIZATIONS.items():
            text = text.replace(unicode_char, replacement)
        return text

    @classmethod
    def normalize_whitespace(cls, text: str) -> str:
        """Collapse multiple whitespace to single space and strip."""
        return ' '.join(text.split())

    @classmethod
    def clean_text(cls, text: str) -> str:
        """
        Full text cleaning pipeline.

        1. Normalize Unicode
        2. Normalize whitespace
        3. Strip

        This is safe for all field types.
        """
        text = cls.clean_unicode(text)
        text = cls.normalize_whitespace(text)
        return text.strip()

    @classmethod
    def apply_ocr_digit_corrections(cls, text: str) -> str:
        """
        Apply OCR error corrections for digit-only fields.

        Use this when the field is expected to contain only digits
        (e.g., OCR number, organization number digits, etc.)

        Example:
            "556l23-4S67" -> "556123-4567"
        """
        result = []
        for char in text:
            if char in cls.OCR_DIGIT_CORRECTIONS:
                result.append(cls.OCR_DIGIT_CORRECTIONS[char])
            else:
                result.append(char)
        return ''.join(result)

    @classmethod
    def extract_digits(cls, text: str, apply_ocr_correction: bool = True) -> str:
        """
        Extract only digits from text.

        Args:
            text: Input text
            apply_ocr_correction: If True, apply OCR corrections ONLY to characters
                                  that are adjacent to digits (not standalone letters)

        Returns:
            String containing only digits
        """
        if apply_ocr_correction:
            # 只对看起来像数字序列中的字符应用 OCR 修正
            # 例如 "556O23" 中的 O 应该修正，但 "ABC 123" 中的 ABC 不应该
            result = []
            for i, char in enumerate(text):
                if char.isdigit():
                    result.append(char)
                elif char in cls.OCR_DIGIT_CORRECTIONS:
                    # 检查前后是否有数字
                    prev_is_digit = i > 0 and (text[i - 1].isdigit() or text[i - 1] in cls.OCR_DIGIT_CORRECTIONS)
                    next_is_digit = i < len(text) - 1 and (text[i + 1].isdigit() or text[i + 1] in cls.OCR_DIGIT_CORRECTIONS)
                    if prev_is_digit or next_is_digit:
                        result.append(cls.OCR_DIGIT_CORRECTIONS[char])
                # 其他字符跳过
            return ''.join(result)
        else:
            return re.sub(r'\D', '', text)

    @classmethod
    def clean_for_digits(cls, text: str) -> str:
        """
        Clean text that should primarily contain digits.

        Pipeline:
        1. Clean Unicode
        2. Apply OCR digit corrections
        3. Normalize whitespace

        Preserves separators (-, /) for formatted numbers like "556123-4567"
        """
        text = cls.clean_unicode(text)
        text = cls.apply_ocr_digit_corrections(text)
        text = cls.normalize_whitespace(text)
        return text.strip()

    @classmethod
    def generate_ocr_variants(cls, text: str) -> list[str]:
        """
        Generate possible OCR error variants of the input text.

        This is useful for matching: if we have a CSV value,
        we generate variants that might appear in OCR output.

        Example:
            "5561234567" -> ["5561234567", "556I234567", "5561234S67", ...]
        """
        variants = {text}

        # 只对数字生成字母变体
        for digit, letter in cls.OCR_LETTER_CORRECTIONS.items():
            if digit in text:
                variants.add(text.replace(digit, letter))

        # 对字母生成数字变体
        for letter, digit in cls.OCR_DIGIT_CORRECTIONS.items():
            if letter in text:
                variants.add(text.replace(letter, digit))

        return list(variants)

    @classmethod
    def normalize_amount_text(cls, text: str) -> str:
        """
        Normalize amount text for parsing.

        - Removes currency symbols and labels
        - Normalizes separators
        - Handles Swedish format (space as thousand separator)
        """
        text = cls.clean_text(text)

        # 移除货币符号和标签 (使用单词边界确保完整匹配)
        text = re.sub(r'(?i)\b(kr|sek|kronor|öre)\b', '', text)

        # 移除千位分隔空格 (Swedish: "1 234,56" -> "1234,56")
        # 但保留小数点前的数字
        text = re.sub(r'(\d)\s+(\d)', r'\1\2', text)

        return text.strip()

    @classmethod
    def normalize_for_comparison(cls, text: str) -> str:
        """
        Normalize text for loose comparison.

        - Lowercase
        - Remove all non-alphanumeric
        - Apply OCR corrections

        This is the most aggressive normalization, used for fuzzy matching.
        """
        text = cls.clean_text(text)
        text = text.lower()
        text = cls.apply_ocr_digit_corrections(text)
        text = re.sub(r'[^a-z0-9]', '', text)
        return text
