# ============================================================
# core/ocr.py
# OCR hóa đơn bằng PaddleOCR + gợi ý bút toán tự động
# ============================================================

import os
import re
import sys
from typing import Optional

_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _check_paddle():
    try:
        from paddleocr import PaddleOCR
        return PaddleOCR
    except ImportError:
        print("❌ Cần cài PaddleOCR:")
        print("   pip install paddlepaddle paddleocr pillow")
        sys.exit(1)


# ── Regex patterns cho hóa đơn Việt Nam ─────────────────────
_PATTERNS = {
    "invoice_no": [
        r"[Ss][Ốố]\s*[Hh][Ðđ]?\s*[:\-]?\s*([A-Z0-9\-/]+)",
        r"Invoice\s+No\.?\s*[:\-]?\s*([A-Z0-9\-/]+)",
        r"Số\s+hóa\s+đơn\s*[:\-]?\s*([A-Z0-9\-/]+)",
    ],
    "invoice_date": [
        r"[Nn]gày\s+(\d{1,2})\s*[/\-]\s*(\d{1,2})\s*[/\-]\s*(\d{4})",
        r"[Dd]ate\s*[:\-]?\s*(\d{1,2})[/\-](\d{1,2})[/\-](\d{4})",
        r"(\d{1,2})/(\d{1,2})/(\d{4})",
    ],
    "seller_tax":  [
        r"[Mm]ã\s+[Ss]ố\s+[Tt]huế\s*[:\-]?\s*(\d{10,13})",
        r"MST\s*[:\-]?\s*(\d{10,13})",
        r"Tax\s*[Cc]ode\s*[:\-]?\s*(\d{10,13})",
    ],
    "subtotal": [
        r"[Tt]iền\s+hàng\s+(?:chưa\s+[Tt]huế)?\s*[:\-]?\s*([\d,\.]+)",
        r"[Cc]ộng\s+tiền\s+hàng\s*[:\-]?\s*([\d,\.]+)",
        r"Amount\s*[:\-]?\s*([\d,\.]+)",
    ],
    "vat_amount": [
        r"[Tt]iền\s+thuế\s+(?:GTGT)?\s*[:\-]?\s*([\d,\.]+)",
        r"VAT\s*(?:10%|5%|8%)?\s*[:\-]?\s*([\d,\.]+)",
        r"Tax\s+[Aa]mount\s*[:\-]?\s*([\d,\.]+)",
    ],
    "total": [
        r"[Tt]ổng\s+(?:cộng\s+)?(?:tiền\s+)?(?:thanh\s+toán)?\s*[:\-]?\s*([\d,\.]+)",
        r"[Tt]otal\s+[Aa]mount\s*[:\-]?\s*([\d,\.]+)",
        r"[Ss]ố\s+tiền\s+[Tt]hanh\s+[Tt]oán\s*[:\-]?\s*([\d,\.]+)",
    ],
    "vat_rate": [
        r"[Tt]huế\s+[Ss]uất\s+GTGT\s*[:\-]?\s*(\d+)%",
        r"VAT\s*[:\-]?\s*(\d+)%",
    ],
}


def _parse_number(s: str) -> float:
    """Chuyển '10,000,000' hoặc '10.000.000' → float."""
    if not s:
        return 0.0
    s = s.strip().replace(",", "").replace(".", "")
    try:
        return float(s)
    except ValueError:
        return 0.0


def _extract_fields(text: str) -> dict:
    """Extract các trường quan trọng từ raw OCR text."""
    result = {}

    # Invoice number
    for pat in _PATTERNS["invoice_no"]:
        m = re.search(pat, text)
        if m:
            result["invoice_no"] = m.group(1).strip()
            break

    # Date
    for pat in _PATTERNS["invoice_date"]:
        m = re.search(pat, text)
        if m:
            day, mon, yr = m.group(1), m.group(2), m.group(3)
            result["invoice_date"] = f"{yr}-{int(mon):02d}-{int(day):02d}"
            break

    # MST
    for pat in _PATTERNS["seller_tax"]:
        m = re.search(pat, text)
        if m:
            result["seller_tax"] = m.group(1).strip()
            break

    # Amounts
    for field in ("subtotal", "vat_amount", "total"):
        for pat in _PATTERNS[field]:
            m = re.search(pat, text)
            if m:
                result[field] = _parse_number(m.group(1))
                break

    # VAT rate
    for pat in _PATTERNS["vat_rate"]:
        m = re.search(pat, text)
        if m:
            result["vat_rate"] = int(m.group(1))
            break

    # Determine invoice type (rough heuristic)
    text_lower = text.lower()
    if any(k in text_lower for k in ["bên bán", "người bán", "seller"]):
        result["invoice_side"] = "purchase"   # HĐ đầu vào (mua)
    else:
        result["invoice_side"] = "sale"       # HĐ đầu ra (bán) — default

    return result


def suggest_journal_entries(fields: dict) -> list:
    """
    Gợi ý bút toán dựa trên thông tin trích xuất.
    Returns list of {account_code, debit, credit, description}
    """
    side      = fields.get("invoice_side", "purchase")
    subtotal  = fields.get("subtotal", 0)
    vat       = fields.get("vat_amount", 0)
    total     = fields.get("total", subtotal + vat)
    inv_no    = fields.get("invoice_no", "HĐ")
    vat_rate  = fields.get("vat_rate", 10)

    if side == "purchase":
        # HĐ mua vào: Nợ 156/641 / Có 331 + Nợ 1331 / Có 331
        entries = [
            {"account_code": "156", "debit": subtotal, "credit": 0,
             "description": f"Mua hàng {inv_no}"},
            {"account_code": "1331", "debit": vat, "credit": 0,
             "description": f"Thuế GTGT đầu vào {inv_no}"},
            {"account_code": "331",  "debit": 0, "credit": total,
             "description": f"Phải trả NCC {inv_no}"},
        ]
    else:
        # HĐ bán ra: Nợ 131 / Có 511 + Có 33311
        entries = [
            {"account_code": "131",   "debit": total, "credit": 0,
             "description": f"Phải thu KH {inv_no}"},
            {"account_code": "511",   "debit": 0, "credit": subtotal,
             "description": f"Doanh thu bán hàng {inv_no}"},
            {"account_code": "33311", "debit": 0, "credit": vat,
             "description": f"Thuế GTGT đầu ra {inv_no}"},
        ]

    return entries


def ocr_invoice(image_path: str, lang: str = "vi",
                verbose: bool = True) -> dict:
    """
    Chạy PaddleOCR trên 1 file ảnh/PDF, trả về fields trích xuất.
    """
    PaddleOCR = _check_paddle()

    if verbose:
        print(f"  📄 OCR: {os.path.basename(image_path)} ...")

    ocr = PaddleOCR(use_angle_cls=True, lang=lang, show_log=False)
    result = ocr.ocr(image_path, cls=True)

    # Gộp toàn bộ text
    lines = []
    for page in result:
        if not page:
            continue
        for item in page:
            if item and len(item) >= 2:
                text_info = item[1]
                if isinstance(text_info, (list, tuple)) and text_info:
                    lines.append(str(text_info[0]))
                else:
                    lines.append(str(text_info))

    full_text = "\n".join(lines)
    if verbose:
        print(f"  📝 Trích xuất {len(lines)} dòng text")

    fields = _extract_fields(full_text)
    fields["raw_text"] = full_text
    fields["image_path"] = image_path
    fields["suggested_entries"] = suggest_journal_entries(fields)

    return fields


def ocr_batch(image_dir: str, lang: str = "vi") -> list:
    """Chạy OCR trên toàn bộ ảnh trong 1 thư mục."""
    exts = {".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".pdf"}
    files = [
        f for f in os.listdir(image_dir)
        if os.path.splitext(f)[1].lower() in exts
    ]
    files.sort()
    print(f"🔍 Tìm thấy {len(files)} file trong {image_dir}")

    results = []
    for i, fname in enumerate(files, 1):
        path = os.path.join(image_dir, fname)
        print(f"[{i}/{len(files)}] ", end="")
        try:
            r = ocr_invoice(path, lang=lang)
            results.append(r)
        except Exception as e:
            print(f"  ❌ Lỗi: {e}")
            results.append({"image_path": path, "error": str(e), "suggested_entries": []})
    return results


def print_ocr_result(fields: dict) -> None:
    """In kết quả OCR ra terminal."""
    print(f"\n{'─'*60}")
    print(f"  KẾT QUẢ OCR: {os.path.basename(fields.get('image_path',''))}")
    print(f"{'─'*60}")
    print(f"  Số HĐ     : {fields.get('invoice_no', '?')}")
    print(f"  Ngày HĐ   : {fields.get('invoice_date', '?')}")
    print(f"  MST       : {fields.get('seller_tax', '?')}")
    print(f"  Tiền hàng : {fields.get('subtotal', 0):>15,.0f}")
    print(f"  Thuế GTGT : {fields.get('vat_amount', 0):>15,.0f}")
    print(f"  Tổng TT   : {fields.get('total', 0):>15,.0f}")
    print(f"  Loại HĐ   : {'ĐẦU VÀO (mua)' if fields.get('invoice_side')=='purchase' else 'ĐẦU RA (bán)'}")
    print(f"\n  📋 Gợi ý bút toán:")
    for e in fields.get("suggested_entries", []):
        dr = f"  NỢ {e['account_code']} {e['debit']:,.0f}" if e['debit'] else ""
        cr = f"  CÓ {e['account_code']} {e['credit']:,.0f}" if e['credit'] else ""
        line = dr or cr
        print(f"    {line:<35}  {e['description']}")
