from __future__ import annotations

"""Unit & Integration tests cho Động cơ Phân bổ Vật tư theo Dự toán (BoQ) và Hạch toán xuất thẳng chân công trường."""

from decimal import Decimal
import pytest
from fastapi.testclient import TestClient

from app.main import app
from app.modules.auth.presentation.auth import get_current_user
from app.modules.invoices.application.invoice_processing.boq_material_allocation_service import (
    BoQMaterialAllocationService,
)

app.dependency_overrides[get_current_user] = lambda: {
    "id": "admin-id",
    "email": "admin@dscons.vn",
    "role": "superadmin",
    "full_name": "Ban Quản Trị Hệ Thống DSCons",
}

client = TestClient(app)
service = BoQMaterialAllocationService()


def test_spec_matching_civil_vs_hydraulic():
    """Kiểm tra nhận diện quy cách kỹ thuật đặc thù: Dân dụng vs Thủy lợi."""
    active_boqs = service.get_project_boq_materials()
    assert len(active_boqs) > 0

    # 1. Tôn mạ màu -> Chỉ khớp với dự án Dân dụng (Đại Thắng)
    cand_ton = service.determine_candidate_projects(
        item_name="Tôn mạ màu dày 0.45 mm",
        material_group="ROOFING",
        active_boq_items=active_boqs,
    )
    assert len(cand_ton) >= 1
    for c in cand_ton:
        assert c["project_code"] == "DA-2608281122"

    # 2. Gạch xây không nung -> Chỉ khớp với dự án Dân dụng (Đại Thắng)
    cand_gach = service.determine_candidate_projects(
        item_name="Gạch xây không nung",
        material_group="BRICK",
        active_boq_items=active_boqs,
    )
    assert len(cand_gach) >= 1
    for c in cand_gach:
        assert c["project_code"] == "DA-2608281122"

    # 3. Rọ đá mạ kẽm / Đá hộc kè mái -> Chỉ khớp với dự án Thủy lợi (Đa Độ)
    cand_ro_da = service.determine_candidate_projects(
        item_name="Rọ đá mạ kẽm bọc nhựa PVC",
        material_group="WIRE_MESH",
        active_boq_items=active_boqs,
    )
    assert len(cand_ro_da) >= 1
    for c in cand_ro_da:
        assert c["project_code"] == "DA-2026-41" or "DADO" in c["project_code"] or "đa độ" in str(c.get("client_name", "")).lower()


def test_proportional_quota_split():
    """Kiểm tra thuật toán phân bổ tỷ trọng khi 1 dòng hóa đơn giao cho nhiều công trình."""
    mock_candidates = [
        {
            "id": "boq-1",
            "project_id": "proj-1",
            "project_code": "DA-DAITHANG",
            "boq_quantity": Decimal("100.0000"),
            "invoiced_quantity": Decimal("70.0000"),
            "remaining_quantity": Decimal("30.0000"),  # Còn thiếu 30
        },
        {
            "id": "boq-2",
            "project_id": "proj-2",
            "project_code": "DA-BENKEM",
            "boq_quantity": Decimal("200.0000"),
            "invoiced_quantity": Decimal("130.0000"),
            "remaining_quantity": Decimal("70.0000"),  # Còn thiếu 70
        },
    ]

    item = {
        "quantity": Decimal("100.0000"),
        "amount_before_vat_vnd": Decimal("150000000.0000"),
        "vat_amount_vnd": Decimal("12000000.0000"),
        "total_item_amount_vnd": Decimal("162000000.0000"),
    }

    allocations = service.calculate_item_allocations(item, mock_candidates)
    assert len(allocations) == 2

    # Tỷ lệ 30 / 100 = 30% và 70 / 100 = 70%
    alloc_1 = next(a for a in allocations if a["project_code"] == "DA-DAITHANG")
    alloc_2 = next(a for a in allocations if a["project_code"] == "DA-BENKEM")

    assert alloc_1["allocated_quantity"] == Decimal("30.0000")
    assert alloc_2["allocated_quantity"] == Decimal("70.0000")
    assert alloc_1["allocated_total_amount_vnd"] == Decimal("48600000.0000")  # 30% of 162M
    assert alloc_2["allocated_total_amount_vnd"] == Decimal("113400000.0000")  # 70% of 162M
    assert alloc_1["debit_account"] == "621_DA-DAITHANG"
    assert alloc_2["debit_account"] == "621_DA-BENKEM"


def test_boq_overrun_guardrail():
    """Kiểm tra hàng rào cảnh báo vượt định mức dự toán (> 105%)."""
    mock_candidates = [
        {
            "id": "boq-1",
            "project_id": "proj-1",
            "project_code": "DA-DAITHANG",
            "boq_quantity": Decimal("100.0000"),
            "invoiced_quantity": Decimal("90.0000"),
            "remaining_quantity": Decimal("10.0000"),
            "allowed_loss_percent": Decimal("3.00"),
        }
    ]

    # Hóa đơn mới thêm 20 cái -> tổng 110 > 103 (103% allowed)
    item = {
        "quantity": Decimal("20.0000"),
        "amount_before_vat_vnd": Decimal("20000000.0000"),
        "vat_amount_vnd": Decimal("1600000.0000"),
        "total_item_amount_vnd": Decimal("21600000.0000"),
    }

    allocations = service.calculate_item_allocations(item, mock_candidates)
    assert len(allocations) == 1
    assert allocations[0]["is_overrun"] is True
    assert allocations[0]["overrun_ratio"] == 1.1


def test_api_boq_allocation_endpoints():
    """Kiểm tra các REST API endpoints quản lý và phân bổ BoQ."""
    # 1. Lấy danh sách dự án
    active_boqs = service.get_project_boq_materials()
    assert len(active_boqs) > 0
    test_proj_id = str(active_boqs[0]["project_id"])

    # 2. Test GET materials of project
    res_mat = client.get(f"/v1/erp/boq-allocations/projects/{test_proj_id}/materials")
    assert res_mat.status_code == 200
    mat_data = res_mat.json()
    assert mat_data["status"] == "success"
    assert "materials" in mat_data
    assert len(mat_data["materials"]) > 0

    # 3. Test GET summary
    res_sum = client.get("/v1/erp/boq-allocations/summary")
    assert res_sum.status_code == 200
    sum_data = res_sum.json()
    assert sum_data["status"] == "success"
    assert "projects_summary" in sum_data
