"""
arabic.py — the Arabic/multilingual text logic for cheque processing
(Knowledge Module 05 §6). All pure-Python and deterministic.

Covers the concrete things "support Arabic" actually means on a cheque:
  - Eastern Arabic numerals (٠١٢٣) -> Western (0123) before any math.
  - amount-in-WORDS -> number, for Arabic and English, to cross-check the
    amount-in-FIGURES (the classic cheque fraud/error control).
  - RTL/bidi note: store logical order; reverse only for display.
"""
from __future__ import annotations

import re

# --- numeral normalization --------------------------------------------------
_EASTERN = {"٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4",
            "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9",
            "،": ",", "٬": ",",          # Arabic comma + thousands separator (U+066C)
            "٫": "."}                     # Arabic decimal separator (U+066B)


def normalize_digits(s: str) -> str:
    """Eastern Arabic digits/separators -> Western. Always run before parsing a
    money figure: '١٢٥٠' -> '1250'."""
    return "".join(_EASTERN.get(ch, ch) for ch in s)


def parse_figure(s: str) -> float:
    """Parse an amount written in figures (Eastern or Western), e.g.
    '١٬٢٥٠٫٠٠' or '1,250.00' -> 1250.0."""
    s = normalize_digits(s).replace(",", "")
    m = re.search(r"-?\d+(?:\.\d+)?", s)
    if not m:
        raise ValueError(f"no number in {s!r}")
    return float(m.group(0))


# --- English amount-in-words -> number --------------------------------------
_EN = {"zero": 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5,
       "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10, "eleven": 11,
       "twelve": 12, "thirteen": 13, "fourteen": 14, "fifteen": 15,
       "sixteen": 16, "seventeen": 17, "eighteen": 18, "nineteen": 19,
       "twenty": 20, "thirty": 30, "forty": 40, "fifty": 50, "sixty": 60,
       "seventy": 70, "eighty": 80, "ninety": 90}
_EN_MULT = {"hundred": 100, "thousand": 1000, "million": 1_000_000}


def english_words_to_number(text: str) -> int:
    total, current = 0, 0
    for tok in re.findall(r"[a-z]+", text.lower()):
        if tok in ("and", "dirhams", "dirham", "aed", "only"):
            continue
        if tok in _EN:
            current += _EN[tok]
        elif tok in _EN_MULT:
            mult = _EN_MULT[tok]
            current = (current or 1) * mult
            if mult >= 1000:
                total += current
                current = 0
        # unknown tokens are ignored (robust to OCR noise)
    return total + current


# --- Arabic amount-in-words -> number ---------------------------------------
# MSA forms used on cheques. Compound hundreds and thousands are single tokens.
_AR = {
    "صفر": 0, "واحد": 1, "اثنان": 2, "اثنين": 2, "ثلاثة": 3, "أربعة": 4,
    "اربعة": 4, "خمسة": 5, "ستة": 6, "سبعة": 7, "ثمانية": 8, "تسعة": 9,
    "عشرة": 10, "عشرون": 20, "عشرين": 20, "ثلاثون": 30, "ثلاثين": 30,
    "أربعون": 40, "اربعون": 40, "أربعين": 40, "خمسون": 50, "خمسين": 50,
    "ستون": 60, "ستين": 60, "سبعون": 70, "سبعين": 70, "ثمانون": 80,
    "ثمانين": 80, "تسعون": 90, "تسعين": 90,
    "مئة": 100, "مائة": 100, "مئتان": 200, "مئتين": 200,
    "ثلاثمئة": 300, "أربعمئة": 400, "خمسمئة": 500, "ستمئة": 600,
    "سبعمئة": 700, "ثمانمئة": 800, "تسعمئة": 900,
    "ألفان": 2000, "ألفين": 2000,
}
_AR_MULT = {"مئة": 100, "مائة": 100, "ألف": 1000, "الف": 1000,
            "آلاف": 1000, "الاف": 1000}


def arabic_words_to_number(text: str) -> int:
    """Parse Arabic number words (additive, with 'و' = and). Handles compounds
    like مئتان=200 and multipliers like 'ثلاثة آلاف'=3000, 'ألف ومئتان وخمسون'=1250."""
    total, current = 0, 0
    # Tokens are separated by spaces; the connector 'و' (and) is a prefix we strip.
    raw = text.replace("درهم", " ").replace("فقط", " ")
    tokens = [t[1:] if t.startswith("و") and len(t) > 1 and t not in _AR and t not in _AR_MULT
              else t for t in raw.split()]
    for tok in tokens:
        tok = tok.strip("ًٌٍَُِّْ")  # strip diacritics
        if not tok or tok == "و":
            continue
        if tok in _AR_MULT and tok not in _AR:        # pure multiplier (ألف/آلاف)
            mult = _AR_MULT[tok]
            current = (current or 1) * mult
            if mult >= 1000:
                total += current
                current = 0
        elif tok in _AR:
            current += _AR[tok]
        # unknown tokens ignored (OCR noise robustness)
    return total + current


# --- the cheque control: words vs figures -----------------------------------
def words_match_figures(words: str, figure: str, lang: str = "ar",
                        tol: float = 0.01) -> bool:
    """The amount-in-words (legal amount) must equal the amount-in-figures
    (courtesy amount). A mismatch is a fraud/error signal -> human review."""
    parsed = arabic_words_to_number(words) if lang == "ar" else english_words_to_number(words)
    return abs(parsed - parse_figure(figure)) <= tol


# --- RTL / bidi note --------------------------------------------------------
def to_display_order(text: str) -> str:
    """Demonstration helper: logical (storage) order is preserved; only DISPLAY
    needs RTL handling. Production UIs use the Unicode Bidirectional Algorithm
    (UAX #9) — never hand-reverse strings for storage."""
    return text  # store logical order; rendering layer applies bidi
