"""Domain Service for Automated 3-Way Matching (PO - GRN - Invoice)."""

from __future__ import annotations

from datetime import date
from decimal import Decimal
from typing import Any

from app.models.matching_schemas import (
    GoodsReceiptSummary,
    InvoiceMatchSummary,
    MatchingDashboardResponse,
    MatchStatusEnum,
    PurchaseOrderSummary,
    ThreeWayMatchResult,
)


class ThreeWayMatchingService:
    """Performs 3-Way Reconciliation between Purchase Orders, Site Goods Receipts, and Invoices."""

    def __init__(self, postgres_client: Any | None = None) -> None:
        self.postgres_client = postgres_client

    def match_documents(
        self,
        po: PurchaseOrderSummary | None,
        grn: GoodsReceiptSummary | None,
        invoice: InvoiceMatchSummary,
    ) -> ThreeWayMatchResult:
        seller_tax_id = invoice.seller_tax_id
        seller_name = invoice.seller_name
        item_name = invoice.item_name
        project_code = (
            po.project_code if po else (grn.project_code if grn else "CHUA_XAC_DINH")
        )

        if not po:
            return ThreeWayMatchResult(
                match_id=f"MATCH-{invoice.invoice_number}-NOPO",
                match_status=MatchStatusEnum.MISSING_PO,
                seller_tax_id=seller_tax_id,
                seller_name=seller_name,
                project_code=project_code,
                item_name=item_name,
                invoice_ref=invoice,
                discrepancy_explanation="Hóa đơn xuất không có Đơn đặt hàng (PO) tương ứng đã phê duyệt.",
                recommended_action="HOLD_FOR_REVIEW",
            )

        if not grn:
            return ThreeWayMatchResult(
                match_id=f"MATCH-{invoice.invoice_number}-NOGRN",
                match_status=MatchStatusEnum.MISSING_GRN,
                seller_tax_id=seller_tax_id,
                seller_name=seller_name,
                project_code=project_code,
                item_name=item_name,
                po_ref=po,
                invoice_ref=invoice,
                discrepancy_explanation="Chưa có Phiếu nhập kho hoặc Vé cân tại công trường chứng minh đã nhận vật tư.",
                recommended_action="HOLD_FOR_REVIEW",
            )

        qty_var = invoice.billed_quantity - grn.received_quantity
        price_var = invoice.unit_price - po.unit_price
        expected_amt = grn.received_quantity * po.unit_price
        amt_var = invoice.total_pre_tax - expected_amt

        if abs(qty_var) < Decimal("0.0010") and abs(price_var) < Decimal("0.0010"):
            status = MatchStatusEnum.PERFECT_MATCH
            explanation = "Khớp nối hoàn hảo 100% giữa Đơn đặt hàng, Vé cân hiện trường và Hóa đơn VAT."
            action = "AUTO_APPROVE"
        elif abs(price_var) > Decimal("0.0010"):
            status = MatchStatusEnum.PRICE_MISMATCH
            explanation = f"Lệch đơn giá: Đơn giá PO = {po.unit_price:,.0f} đ, Đơn giá HĐ = {invoice.unit_price:,.0f} đ"
            action = "HOLD_FOR_REVIEW"
        elif qty_var > Decimal("0.0000"):
            status = MatchStatusEnum.QUANTITY_MISMATCH
            explanation = f"Khối lượng trên HĐ ({invoice.billed_quantity} {invoice.item_name}) vượt khối lượng thực nhận ({grn.received_quantity})"
            action = "REJECT_INVOICE"
        else:
            status = MatchStatusEnum.PARTIAL_RECEIPT
            explanation = f"Hóa đơn xuất cho đợt nhận một phần ({invoice.billed_quantity} / {po.ordered_quantity})"
            action = "AUTO_APPROVE"

        return ThreeWayMatchResult(
            match_id=f"MATCH-{invoice.invoice_number}",
            match_status=status,
            seller_tax_id=seller_tax_id,
            seller_name=seller_name,
            project_code=project_code,
            item_name=item_name,
            po_ref=po,
            grn_ref=grn,
            invoice_ref=invoice,
            qty_variance=qty_var,
            price_variance=price_var,
            amount_variance=amt_var,
            discrepancy_explanation=explanation,
            recommended_action=action,
        )

    def get_dashboard_summary(
        self, as_of_date: date | None = None
    ) -> MatchingDashboardResponse:
        target_date = as_of_date or date.today()

        # Match 1: Cát san lấp Trung Kiên (Perfect Match)
        m1 = self.match_documents(
            po=PurchaseOrderSummary(
                po_code="PO-2026-KM-01",
                supplier_tax_id="0201805660",
                supplier_name="Cty CP XD TM & DV Trung Kiên",
                project_code="DA-KM-BENKEM-2025-2026",
                item_name="Cát vàng đổ bê tông",
                ordered_quantity=Decimal("150.0000"),
                unit_price=Decimal("320000.0000"),
                total_amount=Decimal("48000000.0000"),
                unit="m³",
            ),
            grn=GoodsReceiptSummary(
                grn_code="GRN-2026-KM-014",
                weighbridge_ticket_number="TC-KM-9821",
                supplier_tax_id="0201805660",
                project_code="DA-KM-BENKEM-2025-2026",
                item_name="Cát vàng đổ bê tông",
                received_quantity=Decimal("150.0000"),
                received_date=date(2026, 8, 20),
                receiver_name="Kỹ sư Phạm Văn Tùng",
                unit="m³",
            ),
            invoice=InvoiceMatchSummary(
                invoice_number="0012458",
                invoice_date=date(2026, 8, 21),
                seller_tax_id="0201805660",
                seller_name="Cty CP XD TM & DV Trung Kiên",
                item_name="Cát vàng đổ bê tông",
                billed_quantity=Decimal("150.0000"),
                unit_price=Decimal("320000.0000"),
                total_pre_tax=Decimal("48000000.0000"),
                tax_amount=Decimal("3840000.0000"),
            ),
        )

        # Match 2: Thuê máy Loan Khải (Price Mismatch)
        m2 = self.match_documents(
            po=PurchaseOrderSummary(
                po_code="PO-2026-DADO-05",
                supplier_tax_id="0201889988",
                supplier_name="Cty TNHH Vận Tải & TM Loan Khải",
                project_code="DA-DADO-THUYLDT-2022-2026",
                item_name="Ca máy đào bánh xích 0.8m3",
                ordered_quantity=Decimal("20.0000"),
                unit_price=Decimal("4500000.0000"),
                total_amount=Decimal("90000000.0000"),
                unit="Ca",
            ),
            grn=GoodsReceiptSummary(
                grn_code="GRN-2026-DADO-088",
                weighbridge_ticket_number=None,
                supplier_tax_id="0201889988",
                project_code="DA-DADO-THUYLDT-2022-2026",
                item_name="Ca máy đào bánh xích 0.8m3",
                received_quantity=Decimal("20.0000"),
                received_date=date(2026, 8, 19),
                receiver_name="Kỹ sư Nguyễn Văn Nam",
                unit="Ca",
            ),
            invoice=InvoiceMatchSummary(
                invoice_number="0008742",
                invoice_date=date(2026, 8, 20),
                seller_tax_id="0201889988",
                seller_name="Cty TNHH Vận Tải & TM Loan Khải",
                item_name="Ca máy đào bánh xích 0.8m3",
                billed_quantity=Decimal("20.0000"),
                unit_price=Decimal("4800000.0000"),  # Higher than PO
                total_pre_tax=Decimal("96000000.0000"),
                tax_amount=Decimal("7680000.0000"),
            ),
        )

        matches = [m1, m2]
        perfect_matches = sum(
            1 for m in matches if m.match_status == MatchStatusEnum.PERFECT_MATCH
        )
        mismatches = sum(
            1 for m in matches if m.match_status != MatchStatusEnum.PERFECT_MATCH
        )
        total_discrepancy = sum(
            (abs(m.amount_variance) for m in matches), Decimal("0.0000")
        )

        return MatchingDashboardResponse(
            as_of_date=target_date,
            total_invoices_checked=len(matches),
            perfect_matches_count=perfect_matches,
            mismatches_count=mismatches,
            pending_approval_count=mismatches,
            total_discrepancy_amount=total_discrepancy,
            matches=matches,
        )
