"""Pure Domain Rules for Bank Transaction and Invoice Clearing."""

from __future__ import annotations

import re
import unicodedata
from decimal import Decimal
from typing import Any


def strip_accents(text: str) -> str:
    """Loại bỏ dấu tiếng Việt và chuẩn hóa khoảng trắng để so khớp chuỗi tin cậy."""
    if not text:
        return ""
    text = text.replace("\xa0", " ").replace("\t", " ")
    text = unicodedata.normalize("NFD", text)
    text = "".join(ch for ch in text if unicodedata.category(ch) != "Mn")
    return unicodedata.normalize("NFC", text).upper()


def match_by_invoice_number(description: str, invoice_number: str) -> bool:
    """Kiểm tra số hóa đơn có xuất hiện trong nội dung chuyển khoản hay không."""
    if not description or not invoice_number:
        return False

    desc_clean = strip_accents(description)
    inv_num_clean = str(invoice_number).strip().lstrip("0")
    if not inv_num_clean:
        return False

    # Tìm các mẫu số hóa đơn thông dụng: HD 123, SO 123, HOA DON 123, HDON 123, INV 123, HDMB 123, hoặc số đầy đủ
    patterns = [
        rf"\bHD\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bSO\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bINV\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bHDO\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bHDON\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bHOA\s*DON\s*0*{re.escape(inv_num_clean)}\b",
        rf"\bHDMB\s*0*{re.escape(inv_num_clean)}\b",
        rf"\b{re.escape(str(invoice_number).strip())}\b",
    ]

    for p in patterns:
        if re.search(p, desc_clean):
            return True
    return False


def match_by_exact_amount(
    tx_amount: Decimal,
    invoice_remaining_amount: Decimal,
    tolerance: Decimal = Decimal("0"),
) -> bool:
    """So khớp số tiền giao dịch chính xác tuyệt đối với số dư nợ hóa đơn."""
    if tx_amount <= Decimal("0") or invoice_remaining_amount <= Decimal("0"):
        return False
    return abs(tx_amount - invoice_remaining_amount) <= tolerance


def match_partner_keywords(
    description: str,
    partner_name: str,
    tax_code: str | None = None,
) -> tuple[bool, float]:
    """So khớp tên đối tác hoặc MST trong diễn giải ngân hàng."""
    if not description:
        return False, 0.0

    desc_clean = strip_accents(description)

    # 1. Khớp theo Mã số thuế (nếu có trong nội dung)
    if tax_code and tax_code.strip():
        clean_mst = tax_code.strip()
        if clean_mst in desc_clean or clean_mst.replace("-", "") in desc_clean:
            return True, 1.0

    # 2. Khớp theo Tên đối tác
    if not partner_name:
        return False, 0.0

    name_clean = strip_accents(partner_name)

    # Loại bỏ tiền tố pháp nhân
    name_core = re.sub(
        r"\b(CONG TY|CO PHAN|TNHH|DOANH NGHIEP|DNTN|TMDV|THUONG MAI|DICH VU|XAY DUNG|DAU TU|SAN XUAT|MTV|CP|TM|XD|VA)\b",
        " ",
        name_clean,
    )
    tokens = [w for w in re.findall(r"\w+", name_core) if len(w) > 1]
    if not tokens:
        tokens = [w for w in re.findall(r"\w+", name_clean) if len(w) > 1]

    if not tokens:
        return False, 0.0

    # Cụm từ cốt lõi
    key_phrase = " ".join(tokens)
    if key_phrase in desc_clean:
        return True, 1.0

    matched_tokens = [t for t in tokens if t in desc_clean]
    ratio = len(matched_tokens) / len(tokens)

    if ratio >= 0.6:
        return True, min(1.0, ratio)

    return False, ratio


class NonInvoiceClassification:
    """Kết quả phân loại nghiệp vụ phi hóa đơn."""

    def __init__(
        self,
        matched: bool,
        account_code: str,
        partner_name: str,
        clearing_method: str,
        confidence_score: Decimal,
        notes: str,
    ) -> None:
        self.matched = matched
        self.account_code = account_code
        self.partner_name = partner_name
        self.clearing_method = clearing_method
        self.confidence_score = confidence_score
        self.notes = notes


def classify_non_invoice_transaction(
    description: str,
    direction: str = "outflow",
    amount: Decimal = Decimal("0"),
) -> NonInvoiceClassification:
    """Nhận diện và định khoản tự động các dòng tiền phi hóa đơn theo chuẩn kế toán VAS."""
    if not description:
        return NonInvoiceClassification(False, "", "", "", Decimal("0"), "")

    desc_clean = strip_accents(description)

    # 1. Phí ngân hàng (Outflow -> TK 642)
    bank_fee_keywords = [
        "THU PHI",
        "PHI NTDT",
        "BSMS",
        "SMS",
        "FEE",
        "PHI DUY TRI",
        "PHI CHUYEN TIEN",
        "PHI QLTK",
        "PHI THUONG NIEN",
        "THU PHI DICH VU",
        "PHI GD",
    ]
    if direction == "outflow" and any(k in desc_clean for k in bank_fee_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="642",
            partner_name="Ngân hàng phục vụ",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("1.0000"),
            notes=f"Phí dịch vụ ngân hàng: {description[:100]}",
        )

    # 2. Lãi tiền gửi nhập vốn (Inflow -> TK 515)
    interest_keywords = [
        "TRA LAI SO DU",
        "LAI NHAP VON",
        "NHAN LAI TU TK",
        "LAI TIEN GUI",
        "LAI KHONG KY HAN",
        "TIEN LAI PHAT SINH",
    ]
    if direction == "inflow" and any(k in desc_clean for k in interest_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="515",
            partner_name="Ngân hàng phục vụ",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("1.0000"),
            notes=f"Lãi tiền gửi không kỳ hạn nhập vốn: {description[:100]}",
        )

    # 3. Nộp thuế Kho bạc Nhà nước (Outflow -> TK 333)
    tax_keywords = [
        "NTDT+KB",
        "KBNN",
        "NOP THUE",
        "KHO BAC",
        "THUE GTGT",
        "THUE TNDN",
        "LE PHI MON BAI",
        "THUE TNCN",
        "NOP NSNN",
        "NGAN SACH NHA NUOC",
    ]
    if direction == "outflow" and any(k in desc_clean for k in tax_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="333",
            partner_name="Kho bạc Nhà nước (Cơ quan Thuế)",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("0.9800"),
            notes=f"Nộp thuế vào Ngân sách Nhà nước / KBNN: {description[:100]}",
        )

    # 4. Đóng Bảo hiểm Xã hội (Outflow -> TK 3383)
    insurance_keywords = [
        "DONG BHXH",
        "BHXH+",
        "BHXH",
        "BHYT",
        "BHTN",
        "BAO HIEM XA HOI",
    ]
    if direction == "outflow" and any(k in desc_clean for k in insurance_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="3383",
            partner_name="Bảo hiểm Xã hội TP Hải Phòng",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("0.9800"),
            notes=f"Đóng Bảo hiểm xã hội, BHYT, BHTN: {description[:100]}",
        )

    # 5. Chi trả Lương & Tiền ăn ca công nhân (Outflow -> TK 334)
    payroll_keywords = [
        "TT LUONG",
        "TRA LUONG",
        "CHI LUONG",
        "TAM UNG TIEN AN",
        "CONG NHAN",
        "THO LAI MAY",
        "UNG LUONG",
        "TIEN AN CA",
        "PHU CAP",
        "LUONG THANG",
    ]
    if direction == "outflow":
        is_payroll = any(k in desc_clean for k in payroll_keywords)
        if not is_payroll and re.search(r"\bLUONG\b", desc_clean):
            is_payroll = True

        if is_payroll:
            return NonInvoiceClassification(
                matched=True,
                account_code="334",
                partner_name="Cán bộ công nhân viên DSCons",
                clearing_method="NON_INVOICE_VAS_JOURNAL",
                confidence_score=Decimal("0.9500"),
                notes=f"Chi trả lương, phụ cấp & tiền ăn công nhân hiện trường: {description[:100]}",
            )

    # 6. Thu tiền tạm ứng / giải ngân Hợp đồng Chủ đầu tư (Inflow -> TK 131)
    investor_keywords = [
        "THUY LOI DA DO",
        "BAN QLDA",
        "KIEN MINH",
        "VIMC",
        "RANG DONG",
        "MINATO",
        "GIAI NGAN",
        "TAM UNG HD",
        "THANH TOAN DOT",
        "NGAN HANG PHAT TRIEN",
        "QLDA",
        "BQL",
        "DA DO",
    ]
    if direction == "inflow" and any(k in desc_clean for k in investor_keywords):
        c_name = "Chủ đầu tư / Ban QLDA"
        if "DA DO" in desc_clean or "THUY LOI" in desc_clean:
            c_name = "Công ty TNHH MTV Khai thác CTTL Đa Độ"
        elif "KIEN MINH" in desc_clean:
            c_name = "Công ty Cổ phần Kiến Minh"
        elif "VIMC" in desc_clean:
            c_name = "Tổng công ty Hàng hải Việt Nam (VIMC)"
        elif "RANG DONG" in desc_clean:
            c_name = "Công ty Cổ phần Xây dựng Rạng Đông"
        elif "MINATO" in desc_clean:
            c_name = "Công ty TNHH Minato Việt Nam"

        return NonInvoiceClassification(
            matched=True,
            account_code="131",
            partner_name=c_name,
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("0.9200"),
            notes=f"Thu giải ngân / tạm ứng hợp đồng từ CĐT {c_name}: {description[:100]}",
        )

    # 7. Tạm ứng tiền hàng, Đặt cọc vật tư & Thanh toán hàng chưa hóa đơn (Outflow -> TK 331 với ADVANCE_DEPOSIT)
    advance_keywords = [
        "DAT COC",
        "TAM UNG TIEN HANG",
        "CUOC THIET BI",
        "TAM UNG MUA",
        "TIEN COC",
        "COC THUE",
        "TAM UNG HOP DONG",
        "TT TIEN HANG",
        "TRA TIEN HANG",
        "TT BE TONG",
        "TT KINH",
        "HDMB",
        "DON HANG",
        "TIEN HANG LAN",
    ]
    if direction == "outflow" and any(k in desc_clean for k in advance_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="331",
            partner_name="Nhà cung cấp (Tạm ứng / Hợp đồng kinh tế)",
            clearing_method="ADVANCE_DEPOSIT",
            confidence_score=Decimal("0.9000"),
            notes=f"Tạm ứng / Đặt cọc / Thanh toán HĐMB chưa xuất hóa đơn: {description[:100]}",
        )

    # 8. Rút tiền mặt nhập quỹ công ty (Outflow -> TK 1111)
    cash_withdraw_keywords = ["RUT TIEN", "RUT TIEN MAT", "NHAP QUY"]
    if direction == "outflow" and any(k in desc_clean for k in cash_withdraw_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="111",
            partner_name="Thủ quỹ DSCons (Nhập quỹ tiền mặt)",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("1.0000"),
            notes=f"Rút tiền gửi ngân hàng nhập quỹ tiền mặt (Nợ 1111 / Có 1121): {description[:100]}",
        )

    # 9. Chủ sở hữu nộp tiền vào tài khoản / mượn vốn lưu động (Inflow -> TK 3388)
    capital_inflow_keywords = ["NOP TK", "NOP TIEN", "NOP TIEN MAT", "NOP VAO TK", "NGUYEN SI SON"]
    if direction == "inflow" and any(k in desc_clean for k in capital_inflow_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="3388",
            partner_name="Nguyễn Sĩ Sơn (Chủ sở hữu)",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("0.9800"),
            notes=f"Chủ sở hữu nộp tiền vào tài khoản công ty (Nợ 1121 / Có 3388): {description[:100]}",
        )

    # 10. Hoàn trả tiền chuyển nhầm (Outflow -> TK 3388)
    transfer_mistake_keywords = ["CHUYEN NHAM", "TRA LAI TIEN CHUYEN NHAM"]
    if direction == "outflow" and any(k in desc_clean for k in transfer_mistake_keywords):
        return NonInvoiceClassification(
            matched=True,
            account_code="3388",
            partner_name="Đối tác chuyển nhầm",
            clearing_method="NON_INVOICE_VAS_JOURNAL",
            confidence_score=Decimal("1.0000"),
            notes=f"Hoàn trả tiền chuyển nhầm (Nợ 3388 / Có 1121): {description[:100]}",
        )

    return NonInvoiceClassification(False, "", "", "", Decimal("0"), "")


def find_subset_sum_invoices(
    target_amount: Decimal,
    candidate_invoices: list[dict[str, Any]],
    max_combo: int = 4,
    partner_filter: str | None = None,
) -> list[dict[str, Any]] | None:
    """Tìm tổ hợp gồm k hóa đơn (2 <= k <= max_combo) có tổng số dư nợ đúng bằng target_amount.

    Chỉ ghép các hóa đơn có cùng Mã số thuế đối tác (hoặc tên đối tác) để đảm bảo
    1 UNC chỉ thanh toán gộp cho cùng 1 Nhà cung cấp / Khách hàng.
    """
    from itertools import combinations

    if target_amount <= Decimal("0") or not candidate_invoices:
        return None

    # Lọc các hóa đơn có remaining_amount_vnd > 0 và <= target_amount
    valid_invs: list[dict[str, Any]] = []
    for inv in candidate_invoices:
        rem = Decimal(str(inv.get("remaining_amount_vnd") or 0))
        if Decimal("0") < rem <= target_amount:
            valid_invs.append(inv)

    if len(valid_invs) < 2:
        return None

    # Nhóm theo MST hoặc tên đối tác
    partner_groups: dict[str, list[dict[str, Any]]] = {}
    for inv in valid_invs:
        p_tax = (inv.get("seller_tax_code") or inv.get("buyer_tax_code") or "").strip()
        p_name = (inv.get("seller_name") or inv.get("buyer_name") or "").strip().upper()
        key = p_tax if p_tax else p_name
        if not key:
            continue
        if partner_filter and (partner_filter.upper() not in key and key not in partner_filter.upper()):
            continue
        partner_groups.setdefault(key, []).append(inv)

    for partner_key, group_invs in partner_groups.items():
        if len(group_invs) < 2:
            continue

        # Giới hạn tối đa 12 hóa đơn gần nhất để tối ưu hiệu năng
        limited_invs = group_invs[:12]
        for k in range(2, min(len(limited_invs) + 1, max_combo + 1)):
            for combo in combinations(limited_invs, k):
                total_combo = sum(
                    Decimal(str(item.get("remaining_amount_vnd") or 0))
                    for item in combo
                )
                if total_combo == target_amount:
                    return list(combo)

    return None



def calculate_settlement_allocation(
    available_payment_amount: Decimal,
    invoice_remaining_amount: Decimal,
) -> tuple[Decimal, Decimal, str]:
    """Tính toán số tiền gạch nợ và trạng thái mới của hóa đơn.

    Returns: (settled_amount, new_remaining_amount, new_payment_status)
    """
    if available_payment_amount <= Decimal("0"):
        return Decimal("0"), invoice_remaining_amount, "unpaid"

    settled_amount = min(available_payment_amount, invoice_remaining_amount)
    new_remaining = invoice_remaining_amount - settled_amount

    if new_remaining <= Decimal("0"):
        new_status = "settled"
        new_remaining = Decimal("0")
    elif settled_amount > Decimal("0"):
        new_status = "partially_paid"
    else:
        new_status = "unpaid"

    return settled_amount, new_remaining, new_status

