"""Vietnamese CAD Font Normalizer and Multi-Encoding Transcoder.
Supports TCVN3 (ABC), VNI-Windows, AutoCAD SHX/MText Escape Sequences, and Unicode Normalization.
Specialists: Quynh (QS & BoQ Takeoff), Hung (Site Engineering & Drawings), Minh (System Standards).
"""

from __future__ import annotations

import re
import unicodedata

# ==============================================================================
# 1. TCVN3 (ABC / .VnTime) TO UNICODE MAPPING
# ==============================================================================
# Composite multi-character TCVN3 sequences (ươ, ướ, ườ, ưở, ưỡ, ượ)
TCVN3_COMPOSITES: list[tuple[str, str]] = [
    ("\xa6\xed", "ướ"),
    ("\xa6\xea", "ườ"),
    ("\xa6\xeb", "ưở"),
    ("\xa6\xec", "ưỡ"),
    ("\xa6\xee", "ượ"),
    ("\xa6\xac", "ươ"),
    ("\xad\xed", "ướ"),
    ("\xad\xea", "ườ"),
    ("\xad\xeb", "ưở"),
    ("\xad\xec", "ưỡ"),
    ("\xad\xee", "ượ"),
    ("\xad\xac", "ươ"),
    ("\xaa\xd5", "ế"),
    ("\xaa\xd2", "ề"),
    ("\xaa\xd3", "ể"),
    ("\xaa\xd4", "ễ"),
    ("\xaa\xd6", "ệ"),
    ("\xab\xe8", "ố"),
    ("\xab\xe5", "ồ"),
    ("\xab\xe6", "ổ"),
    ("\xab\xe7", "ỗ"),
    ("\xab\xe9", "ộ"),
]

TCVN3_DICT: dict[str, str] = {
    # a, ă, â
    "\xb8": "á",
    "\xb5": "à",
    "\xb6": "ả",
    "\xb7": "ã",
    "\xb9": "ạ",
    "\xa8": "ă",
    "\xbe": "ắ",
    "\xbb": "ằ",
    "\xbc": "ẳ",
    "\xbd": "ẵ",
    "\xc6": "ặ",
    "\xa9": "â",
    "\xca": "ấ",
    "\xc7": "ầ",
    "\xc8": "ẩ",
    "\xc9": "ẫ",
    "\xcb": "ậ",
    # e, ê
    "\xd0": "é",
    "\xcc": "è",
    "\xce": "ẻ",
    "\xcf": "ẽ",
    "\xd1": "ẹ",
    "\xaa": "ê",
    "\xd5": "ế",
    "\xd2": "ề",
    "\xd3": "ể",
    "\xd4": "ễ",
    "\xd6": "ệ",
    # i
    "\xdd": "í",
    "\xd7": "ì",
    "\xd8": "ỉ",
    "\xdc": "ĩ",
    "\xde": "ị",
    # o, ô, ơ
    "\xe3": "ó",
    "\xdf": "ò",
    "\xe1": "ỏ",
    "\xe2": "õ",
    "\xe4": "ọ",
    "\xab": "ô",
    "\xe8": "ố",
    "\xe5": "ồ",
    "\xe6": "ổ",
    "\xe7": "ỗ",
    "\xe9": "ộ",
    "\xac": "ơ",
    "\xed": "ớ",
    "\xea": "ờ",
    "\xeb": "ở",
    "\xec": "ỡ",
    "\xee": "ợ",
    # u, ư
    "\xf3": "ú",
    "\xef": "ù",
    "\xf1": "ủ",
    "\xf2": "ũ",
    "\xf4": "ụ",
    "\xad": "ư",
    "\xa6": "ư",
    "\xf8": "ứ",
    "\xf5": "ừ",
    "\xf6": "ử",
    "\xf7": "ữ",
    "\xf9": "ự",
    # y
    "\xfd": "ý",
    "\xfa": "ỳ",
    "\xfb": "ỷ",
    "\xfc": "ỹ",
    "\xfe": "ỵ",
    # d
    "\xae": "đ",
    "\xa7": "Đ",
    # Uppercase
    "\xa1": "Ă",
    "\xa2": "Â",
    "\xa3": "Đ",
    "\xa4": "Ê",
    "\xa5": "Ô",
}

_TCVN3_REGEX = re.compile(
    "|".join(re.escape(k) for k in sorted(TCVN3_DICT.keys(), key=len, reverse=True))
)

_TCVN3_DISCRIMINATIVE_MARKERS = [
    "\xb5",
    "\xb8",
    "\xbe",
    "\xbc",
    "\xc6",
    "\xca",
    "\xc7",
    "\xd0",
    "\xd5",
    "\xd6",
    "\xd2",
    "\xd3",
    "\xd4",
    "\xdd",
    "\xae",
    "\xa7",
    "\xa1",
    "\xa2",
    "\xa3",
    "\xa4",
    "\xa5",
    "\xa6",
    "\xa8",
    "\xa9",
    "\xab",
    "\xac",
    "\xad",
    "\xbb",
    "\xbd",
    "\xcb",
    "\xcc",
    "\xce",
    "\xcf",
    "\xd1",
    "\xd7",
    "\xd8",
    "\xdc",
    "\xde",
    "\xe3",
    "\xe5",
    "\xe6",
    "\xe7",
    "\xe8",
    "\xe9",
    "\xea",
    "\xeb",
    "\xec",
    "\xed",
    "\xee",
    "\xf1",
    "\xf2",
    "\xf4",
    "\xf5",
    "\xf6",
    "\xf7",
    "\xf8",
]


# ==============================================================================
# 2. VNI-WINDOWS TO UNICODE MAPPING
# ==============================================================================
VNI_PATTERNS: list[tuple[str, str]] = [
    # 3-char sequences
    (r"oâù|oá", "ố"),
    (r"oâø|oà", "ồ"),
    (r"oâû|oå", "ổ"),
    (r"oâõ|oã", "ỗ"),
    (r"oâï|oä", "ộ"),
    (r"oâ", "ô"),
    (r"OÂÙ|OÁ", "Ố"),
    (r"OÂØ|OÀ", "Ồ"),
    (r"OÂÛ|OÅ", "Ổ"),
    (r"OÂÕ|OÃ", "Ỗ"),
    (r"OÂÄ|OÄ", "Ộ"),
    (r"OÂ", "Ô"),
    (r"aâù|aá", "ấ"),
    (r"aâø|aà", "ầ"),
    (r"aâû|aå", "ẩ"),
    (r"aâõ|aã", "ẫ"),
    (r"aâï|aä", "ậ"),
    (r"aâ", "â"),
    (r"AÂÙ|AÁ", "Ấ"),
    (r"AÂØ|AÀ", "Ầ"),
    (r"AÂÛ|AÅ", "Ẩ"),
    (r"AÂÕ|AÃ", "Ẫ"),
    (r"AÂÄ|AÄ", "Ậ"),
    (r"AÂ", "Â"),
    (r"aêù|aé", "ắ"),
    (r"aêø|aè", "ằ"),
    (r"aêû|aú", "ẳ"),
    (r"aêõ|aü", "ẵ"),
    (r"aêï|aë", "ặ"),
    (r"aê", "ă"),
    (r"AÊÙ|AÉ", "Ắ"),
    (r"AÊØ|AÈ", "Ằ"),
    (r"AÊÛ|AÚ", "Ẳ"),
    (r"AÊÕ|AÜ", "Ẵ"),
    (r"AÊÄ|AË", "Ặ"),
    (r"AÊ", "Ă"),
    (r"eâù|eá", "ế"),
    (r"eâø|eà", "ề"),
    (r"eâû|eå", "ể"),
    (r"eâõ|eã", "ễ"),
    (r"eâï|eä", "ệ"),
    (r"eâ", "ê"),
    (r"EÂÙ|EÁ", "Ế"),
    (r"EÂØ|EÀ", "Ề"),
    (r"EÂÛ|EÅ", "Ể"),
    (r"EÂÕ|EÃ", "Ễ"),
    (r"EÂÄ|EÄ", "Ệ"),
    (r"EÂ", "Ê"),
    (r"ôù", "ớ"),
    (r"ôø", "ờ"),
    (r"ôû", "ở"),
    (r"ôõ", "ỡ"),
    (r"ôï", "ợ"),
    (r"ô", "ơ"),
    (r"ÔÙ", "Ớ"),
    (r"ÔØ", "Ờ"),
    (r"ÔÛ", "Ở"),
    (r"ÔÕ", "Ỡ"),
    (r"ÔÏ", "Ợ"),
    (r"Ô", "Ơ"),
    (r"öù", "ứ"),
    (r"öø", "ừ"),
    (r"öû", "ử"),
    (r"öõ", "ữ"),
    (r"öï", "ự"),
    (r"ö", "ư"),
    (r"ÖÙ", "Ứ"),
    (r"ÖØ", "Ừ"),
    (r"ÖÛ", "Ử"),
    (r"ÖÕ", "Ữ"),
    (r"ÖÏ", "Ự"),
    (r"Ö", "Ư"),
    # 2-char sequences
    (r"aù", "á"),
    (r"aø", "à"),
    (r"aû", "ả"),
    (r"aõ", "ã"),
    (r"aï", "ạ"),
    (r"AÙ", "Á"),
    (r"AØ", "À"),
    (r"AÛ", "Ả"),
    (r"AÕ", "Ã"),
    (r"AÏ", "Ạ"),
    (r"eù", "é"),
    (r"eø", "è"),
    (r"eû", "ẻ"),
    (r"eõ", "ẽ"),
    (r"eï", "ẹ"),
    (r"EÙ", "É"),
    (r"EØ", "È"),
    (r"EÛ", "Ẻ"),
    (r"EÕ", "Ẽ"),
    (r"EÏ", "Ẹ"),
    (r"où", "ó"),
    (r"oø", "ò"),
    (r"oû", "ỏ"),
    (r"oõ", "õ"),
    (r"oï", "ọ"),
    (r"OÙ", "Ó"),
    (r"OØ", "Ò"),
    (r"OÛ", "Ỏ"),
    (r"OÕ", "Õ"),
    (r"OÏ", "Ọ"),
    (r"uù", "ú"),
    (r"uø", "ù"),
    (r"uû", "ủ"),
    (r"uõ", "ũ"),
    (r"uï", "ụ"),
    (r"UÙ", "Ú"),
    (r"UØ", "Ù"),
    (r"UÛ", "Ủ"),
    (r"UÕ", "Ũ"),
    (r"UÏ", "Ụ"),
    (r"yù", "ý"),
    (r"yø", "ỳ"),
    (r"yû", "ỷ"),
    (r"yõ", "ỹ"),
    (r"YÙ", "Ý"),
    (r"YØ", "Ỳ"),
    (r"YÛ", "Ỷ"),
    (r"YÕ", "Ỹ"),
    # Single characters
    (r"ñ", "đ"),
    (r"Ñ", "Đ"),
    (r"í", "í"),
    (r"ì", "ì"),
    (r"æ", "ỉ"),
    (r"ó", "ĩ"),
    (r"ò", "ị"),
    (r"Í", "Í"),
    (r"Ì", "Ì"),
    (r"Æ", "Ỉ"),
    (r"Ó", "Ĩ"),
    (r"Ò", "Ị"),
]

_VNI_DICT: dict[str, str] = {}
for p_group, repl in VNI_PATTERNS:
    for sub_p in p_group.split("|"):
        _VNI_DICT[sub_p] = repl

_VNI_REGEX = re.compile(
    "|".join(re.escape(k) for k in sorted(_VNI_DICT.keys(), key=len, reverse=True))
)

_VNI_PAIRS = [
    "aù",
    "aø",
    "aû",
    "aõ",
    "aï",
    "aê",
    "eù",
    "eø",
    "eû",
    "eõ",
    "eï",
    "eâ",
    "où",
    "oø",
    "oû",
    "oõ",
    "oï",
    "oâ",
    "ôù",
    "ôø",
    "uù",
    "uø",
    "uû",
    "uõ",
    "uï",
    "öù",
    "öø",
    "yù",
    "yø",
    "ñ",
    "Ñ",
]


class VietnameseCadFontTranscoder:
    """Specialized transcoder to decode all legacy Vietnamese CAD fonts (TCVN3, VNI, SHX, MText) to UTF-8 Unicode."""

    @staticmethod
    def clean_autocad_mtext(text: str) -> str:
        """Strip AutoCAD MText formatting codes, escape sequences, and symbol codes."""
        if not text:
            return ""

        # 1. Decode Unicode Hex escapes: \U+0110 -> Đ, \U+00E0 -> à
        def _replace_u_hex(match: re.Match) -> str:
            hex_val = match.group(1)
            try:
                return chr(int(hex_val, 16))
            except Exception:
                return match.group(0)

        res = re.sub(r"(?i)\\U\+([0-9a-f]{4})", _replace_u_hex, text)

        # 2. AutoCAD Special Symbol Codes
        res = re.sub(r"(?i)%%c", "Ø", res)  # Diameter symbol (Phi)
        res = re.sub(r"(?i)%%d", "°", res)  # Degree symbol
        res = re.sub(r"(?i)%%p", "±", res)  # Plus/minus tolerance
        res = re.sub(r"(?i)%%u", "", res)  # Underline toggle
        res = re.sub(r"(?i)%%o", "", res)  # Overscore toggle
        res = re.sub(r"(?i)%%%", "%", res)  # Literal percent

        # 3. Newline & Paragraph codes
        res = re.sub(r"(?i)\\P", "\n", res)
        res = re.sub(r"\\~", " ", res)  # Non-breaking space

        # 4. Remove MText formatting blocks: \f.VnTime|b0|i0; \H1.5x; \W0.8; \Q15; \A1; \C1;
        res = re.sub(r"(?i)\\[fFHhWwQqAaCc]\w*[^;]*;", "", res)
        # Remove stacked fractions: \S1/2; or \S1^2;
        res = re.sub(r"(?i)\\S([^;]*);", r"\1", res)
        # Remove curly braces used for formatting blocks: { ... }
        res = res.replace("{", "").replace("}", "")

        return res.strip()

    @classmethod
    def transcode_tcvn3_to_unicode(cls, text: str) -> str:
        """Convert a TCVN3 (ABC / .VnTime) encoded string to UTF-8 Unicode."""
        if not text:
            return ""
        for comp, repl in TCVN3_COMPOSITES:
            text = text.replace(comp, repl)
        res = _TCVN3_REGEX.sub(lambda m: TCVN3_DICT.get(m.group(0), m.group(0)), text)
        return unicodedata.normalize("NFC", res)

    @classmethod
    def transcode_vni_to_unicode(cls, text: str) -> str:
        """Convert a VNI-Windows encoded string to UTF-8 Unicode."""
        if not text:
            return ""
        res = _VNI_REGEX.sub(lambda m: _VNI_DICT[m.group(0)], text)
        return unicodedata.normalize("NFC", res)

    @classmethod
    def has_vietnamese_extended_unicode(cls, text: str) -> bool:
        """Check if string already contains modern Vietnamese UTF-8 multi-byte characters."""
        for c in text:
            code = ord(c)
            if 0x1EA0 <= code <= 0x1EF9 or code in (
                0x0110,
                0x0111,
                0x0102,
                0x0103,
                0x01A0,
                0x01A1,
                0x01AF,
                0x01B0,
            ):
                return True
        return False

    @classmethod
    def auto_transcode_cad_text(cls, text: str) -> str:
        """Auto-detect CAD font encoding (TCVN3, VNI, MText, Unicode) and transcode to pristine UTF-8 Unicode."""
        if not text:
            return ""

        # Step 1: Clean AutoCAD MText formatting and escape codes
        cleaned = cls.clean_autocad_mtext(text)

        # Step 2: If already modern Vietnamese Unicode, do not corrupt with legacy transcoders
        if cls.has_vietnamese_extended_unicode(cleaned):
            return unicodedata.normalize("NFC", cleaned)

        # Step 3: Scoring heuristics
        vni_score = sum(cleaned.count(p) for p in _VNI_PAIRS)
        tcvn3_score = sum(cleaned.count(m) for m in _TCVN3_DISCRIMINATIVE_MARKERS)

        if vni_score > 0 and vni_score > tcvn3_score:
            return cls.transcode_vni_to_unicode(cleaned)
        elif tcvn3_score > 0:
            return cls.transcode_tcvn3_to_unicode(cleaned)
        elif vni_score > 0:
            return cls.transcode_vni_to_unicode(cleaned)

        # Step 4: Default Unicode NFC normalization
        return unicodedata.normalize("NFC", cleaned)

    def decode_cad_text(self, text: str) -> str:
        """Instance alias for auto_transcode_cad_text."""
        return self.auto_transcode_cad_text(text)


_global_transcoder = VietnameseCadFontTranscoder()


def get_cad_font_transcoder() -> VietnameseCadFontTranscoder:
    return _global_transcoder


get_vietnamese_cad_font_transcoder = get_cad_font_transcoder


# ==============================================================================
# 3. CQRS QUERY INTEGRATION (Deep Matrix Migration D6)
# ==============================================================================
from app.core.module_framework.cqrs import IQuery, IQueryHandler

class DecodeCadTextQuery(IQuery):
    """CQRS Query to decode AutoCAD text with legacy Vietnamese fonts (TCVN3, VNI) to UTF-8 Unicode."""
    raw_text: str

class DecodeCadTextQueryHandler(IQueryHandler[DecodeCadTextQuery, str]):
    def handle(self, query: DecodeCadTextQuery) -> str:
        transcoder = get_cad_font_transcoder()
        return transcoder.auto_transcode_cad_text(query.raw_text)

