from __future__ import annotations

import re
import unicodedata
from dataclasses import dataclass

from ocr_pipeline.utils.logging import get_logger

logger = get_logger(__name__)


@dataclass
class CleanResult:
    text: str
    original_length: int
    cleaned_length: int
    operations: list[str]


def fix_encoding(text: str) -> str:
    try:
        return text.encode("latin-1").decode("utf-8")
    except UnicodeError:
        return text


def normalize_units(text: str) -> str:
    number = r"(\d+(?:\.\d+)?)"
    replacements = [
        (rf"{number}\s*[uµ]M\b", r"\1 µM"),
        (rf"{number}\s*[uµ]m\b", r"\1 µm"),
        (rf"{number}\s*[uµ]g\b", r"\1 µg"),
        (rf"{number}\s*ng\b", r"\1 ng"),
        (rf"{number}\s*mg\b", r"\1 mg"),
        (rf"{number}\s*[uµ]L\b", r"\1 µL"),
        (rf"{number}\s*mL\b", r"\1 mL"),
        (rf"{number}\s*pM\b", r"\1 pM"),
        (rf"{number}\s*nM\b", r"\1 nM"),
        (rf"{number}\s*mM\b", r"\1 mM"),
        (rf"{number}\s*[kK]b\b", r"\1 kb"),
        (rf"{number}\s*[mM]b\b", r"\1 Mb"),
        (rf"{number}\s*[gG]b\b", r"\1 Gb"),
        (rf"{number}\s*°\s*[cC]\b", r"\1 °C"),
    ]
    for pattern, replacement in replacements:
        text = re.sub(pattern, replacement, text)
    return text


def clean_text(text: str) -> CleanResult:
    original_length = len(text)
    operations: list[str] = []
    try:
        text = fix_encoding(text)
    finally:
        operations.append("fix_encoding")
    text = unicodedata.normalize("NFC", text)
    operations.append("normalize_unicode")
    for source, target in {"ﬁ": "fi", "ﬂ": "fl", "ﬀ": "ff", "ﬃ": "ffi", "ﬄ": "ffl"}.items():
        text = text.replace(source, target)
    operations.append("fix_ligatures")
    text = "".join(char for char in text if unicodedata.category(char)[0] != "C" or char in "\n\t")
    operations.append("remove_control_chars")
    text = re.sub(r"(\w+)-\n(\w+)", r"\1\2", text)
    operations.append("fix_hyphenation")
    text = re.sub(r"(?m)^\s*[|┃║─━┌┐└┘├┤┬┴┼]+\s*$", "", text)
    text = re.sub(r"(.)\1{10,}", "", text)
    operations.append("remove_ocr_artifacts")
    text = re.sub(r"(\d)\s*[×xX]\s*10\s*\^?\s*([+-]?\d+)", r"\1×10^\2", text)
    operations.append("fix_scientific_notation")
    text = normalize_units(text)
    operations.append("normalize_units")
    text = re.sub(r"[ \t]+", " ", text)
    text = re.sub(r"\n{3,}", "\n\n", text).strip()
    operations.append("normalize_whitespace")
    logger.debug("text_cleaned", original=original_length, cleaned=len(text), ops=operations)
    return CleanResult(
        text=text, original_length=original_length, cleaned_length=len(text), operations=operations
    )
