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_unicode(text: str) -> str:
    return unicodedata.normalize("NFC", text)


def fix_ligatures(text: str) -> str:
    ligatures = {
        "ﬁ": "fi",
        "ﬂ": "fl",
        "ﬀ": "ff",
        "ﬃ": "ffi",
        "ﬄ": "ffl",
        "ﬅ": "ft",
        "ﬆ": "st",
        "Ꜳ": "AA",
        "ꜳ": "aa",
        "Ꜵ": "AO",
        "ꜵ": "ao",
        "Ꜷ": "AU",
        "ꜷ": "au",
        "Ꜹ": "AV",
        "ꜹ": "av",
        "Ꜻ": "AV",
        "ꜻ": "av",
        "Ꜽ": "AY",
        "ꜽ": "ay",
    }
    for lig, repl in ligatures.items():
        text = text.replace(lig, repl)
    return text


def remove_control_chars(text: str) -> str:
    return "".join(ch for ch in text if unicodedata.category(ch)[0] != "C" or ch in "\n\t")


def fix_hyphenation(text: str) -> str:
    return re.sub(r"(\w+)-\n(\w+)", r"\1\2", text)


def normalize_whitespace(text: str) -> str:
    text = re.sub(r"[ \t]+", " ", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text.strip()


def remove_ocr_artifacts(text: str) -> str:
    patterns = [
        r"^\s*[\|┃║]\s*$",
        r"^\s*[─━]\s*$",
        r"^\s*[┌┐└┘├┤┬┴┼]\s*$",
        r"(.)\1{10,}",
        r"^\s*[•·▪▫◦‣⁃]\s*$",
    ]
    lines = text.split("\n")
    cleaned = []
    for line in lines:
        if any(re.match(p, line.strip()) for p in patterns):
            continue
        cleaned.append(line)
    return "\n".join(cleaned)


def fix_scientific_notation(text: str) -> str:
    text = re.sub(r"(\d)\s*[×xX]\s*10\s*[\^]?\s*([+-]?\d+)", r"\1×10^\2", text)
    text = re.sub(r"(\d)\s*[×xX]\s*10\s*([+-]?\d+)", r"\1×10^\2", text)
    return text


def normalize_units(text: str) -> str:
    unit_fixes = {
        r"(\d)\s*u[mM]\b": r"\1 µm",
        r"(\d)\s*[µu]g\b": r"\1 µg",
        r"(\d)\s*[mn]g\b": r"\1 mg",
        r"(\d)\s*[kK]b\b": r"\1 kb",
        r"(\d)\s*[mM]b\b": r"\1 Mb",
        r"(\d)\s*[gG]b\b": r"\1 Gb",
        r"(\d)\s*[pP]?[mM]\b": r"\1 pm",
        r"(\d)\s*[nN][mM]\b": r"\1 nm",
        r"(\d)\s*[cC][mM]\b": r"\1 cm",
        r"(\d)\s*[mM][lL]\b": r"\1 mL",
        r"(\d)\s*[uU][lL]\b": r"\1 µL",
        r"(\d)\s*°\s*[cC]\b": r"\1 °C",
    }
    for pattern, repl in unit_fixes.items():
        text = re.sub(pattern, repl, text)
    return text


def fix_line_breaks(text: str) -> str:
    text = re.sub(r"(?<!\n)\n(?!\n)", " ", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text


def clean_text(text: str) -> CleanResult:
    original_len = len(text)
    operations = []

    text = fix_encoding(text)
    operations.append("fix_encoding")

    text = normalize_unicode(text)
    operations.append("normalize_unicode")

    text = fix_ligatures(text)
    operations.append("fix_ligatures")

    text = remove_control_chars(text)
    operations.append("remove_control_chars")

    text = fix_hyphenation(text)
    operations.append("fix_hyphenation")

    text = normalize_whitespace(text)
    operations.append("normalize_whitespace")

    text = remove_ocr_artifacts(text)
    operations.append("remove_ocr_artifacts")

    text = fix_scientific_notation(text)
    operations.append("fix_scientific_notation")

    text = normalize_units(text)
    operations.append("normalize_units")

    text = fix_line_breaks(text)
    operations.append("fix_line_breaks")

    cleaned_len = len(text)
    logger.debug("text_cleaned", original=original_len, cleaned=cleaned_len, ops=operations)

    return CleanResult(
        text=text,
        original_length=original_len,
        cleaned_length=cleaned_len,
        operations=operations,
    )
