from __future__ import annotations

import unittest
from datetime import date, datetime
from unittest.mock import patch

from sqlalchemy import BigInteger, create_engine, select
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.orm import Session, sessionmaker


@compiles(BigInteger, "sqlite")
def _compile_big_integer_for_sqlite(type_, compiler, **kw) -> str:
    _ = type_, compiler, kw
    return "INTEGER"


import app.models.document_archive  # noqa: E402,F401
import app.models.master_data  # noqa: E402,F401
import app.models.miniapp  # noqa: E402,F401
import app.models.operations  # noqa: E402,F401
import app.models.org  # noqa: E402,F401
import app.models.planning  # noqa: E402,F401
import app.models.sales  # noqa: E402,F401
from app.api.routes.quality import inspect_receipt_item  # noqa: E402
from app.models.base import Base  # noqa: E402
from app.models.document_archive import DocumentArchive  # noqa: E402
from app.models.master_data import Item, StockBalance, Warehouse  # noqa: E402
from app.models.operations import PurchaseOrder, PurchaseOrderItem, PurchaseReceipt, PurchaseReceiptItem, StockLot, Supplier  # noqa: E402
from app.models.org import Department, Employee, User  # noqa: E402
from app.schemas.document_archives import DocumentArchiveGenerateResult  # noqa: E402
from app.schemas.operations import QualityInspectionAction  # noqa: E402
from app.services.auth import AuthContext  # noqa: E402


class QualityInspectionDocumentArchiveTest(unittest.TestCase):
    def setUp(self) -> None:
        engine = create_engine("sqlite+pysqlite:///:memory:", future=True)
        Base.metadata.create_all(engine)
        self.SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True)
        self.db: Session = self.SessionLocal()
        self._seed_inspection_data()

    def tearDown(self) -> None:
        self.db.close()

    def _seed_inspection_data(self) -> None:
        now = datetime(2026, 6, 11, 9, 0, 0)
        self.department = Department(
            id=1,
            dept_code="D-QC-001",
            dept_name="质检部",
            dept_type="QUALITY",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.employee = Employee(
            id=1,
            employee_code="EMP-QC-001",
            employee_name="质检员王工",
            dept_id=1,
            mobile="13800000001",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.user = User(
            id=1,
            username="quality_tester",
            password_hash="x",
            employee_id=1,
            dept_id=1,
            nickname="质检测试员",
            is_super_admin=1,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.supplier = Supplier(
            id=10,
            supplier_code="SUP-QC-001",
            supplier_name="质检供应商",
            short_name="质检供应商",
            lead_time_days=0,
            default_tax_rate=0,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.material_item = Item(
            id=20,
            item_code="RM-QC-001",
            item_name="质检原材料",
            item_type="RAW_MATERIAL",
            specification="D60",
            material_grade="45#",
            unit_weight_kg=1,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.warehouse = Warehouse(
            id=30,
            warehouse_code="WH-RAW",
            warehouse_name="原材料库",
            warehouse_type="RAW",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.purchase_order = PurchaseOrder(
            id=40,
            po_no="采购2026-00001",
            supplier_id=self.supplier.id,
            order_date=date(2026, 6, 11),
            expected_date=date(2026, 6, 20),
            warning_lead_days=0,
            purchaser_employee_id=None,
            target_warehouse_type="RAW",
            tax_rate=0.13,
            total_amount=660,
            status="PENDING_QC",
            remark=None,
            created_at=now,
            updated_at=now,
        )
        self.purchase_order_item = PurchaseOrderItem(
            id=50,
            purchase_order_id=self.purchase_order.id,
            line_no=1,
            material_item_id=self.material_item.id,
            source_demand_id=None,
            order_qty=0,
            order_weight_kg=100,
            received_qty=0,
            received_weight_kg=0,
            unit_price=6.6,
            line_amount=660,
            status="OPEN",
            remark=None,
            created_at=now,
            updated_at=now,
        )
        self.receipt = PurchaseReceipt(
            id=60,
            receipt_no="入库2026-00001",
            purchase_order_id=self.purchase_order.id,
            warehouse_id=self.warehouse.id,
            receipt_date=datetime(2026, 6, 12, 10, 0, 0),
            receiver_employee_id=None,
            supplier_delivery_no=None,
            logistics_waybill_no="YD-QC-001",
            logistics_freight_amount=50,
            logistics_photo_url=None,
            status="PENDING_QC",
            remark=None,
            created_at=now,
            updated_at=now,
        )
        self.receipt_item = PurchaseReceiptItem(
            id=70,
            receipt_id=self.receipt.id,
            line_no=1,
            purchase_order_item_id=self.purchase_order_item.id,
            material_item_id=self.material_item.id,
            location_id=None,
            lot_no="YL0001",
            merge_to_lot_id=None,
            received_qty=0,
            received_weight_kg=100,
            accepted_qty=0,
            accepted_weight_kg=0,
            unit_cost=6.6,
            status="PENDING_QC",
            remark=None,
            created_at=now,
            updated_at=now,
        )
        self.stock_lot = StockLot(
            id=80,
            lot_no=self.receipt_item.lot_no,
            parent_lot_id=None,
            lot_role="INBOUND_RAW",
            material_sub_batch_no=None,
            item_id=self.material_item.id,
            warehouse_id=self.warehouse.id,
            location_id=None,
            source_doc_type="PURCHASE_RECEIPT",
            source_doc_id=self.receipt.id,
            source_line_id=self.receipt_item.id,
            source_material_lot_id=None,
            source_material_sub_batch_no=None,
            source_material_summary=None,
            inbound_qty=0,
            inbound_weight_kg=100,
            remaining_qty=0,
            remaining_weight_kg=100,
            locked_qty=0,
            locked_weight_kg=100,
            unit_cost=6.6,
            production_date=None,
            expire_date=None,
            quality_status="PENDING_QC",
            logistics_waybill_no="YD-QC-001",
            logistics_freight_amount=50,
            logistics_photo_url=None,
            status="LOCKED",
            remark="到货待检",
            created_at=now,
            updated_at=now,
        )
        self.balance = StockBalance(
            id=90,
            item_id=self.material_item.id,
            warehouse_id=self.warehouse.id,
            location_id=None,
            qty_on_hand=0,
            weight_on_hand_kg=100,
            qty_available=0,
            weight_available_kg=0,
            qty_allocated=0,
            weight_allocated_kg=0,
            avg_unit_cost=6.6,
            created_at=now,
            updated_at=now,
        )
        self.db.add_all([
            self.department,
            self.employee,
            self.user,
            self.supplier,
            self.material_item,
            self.warehouse,
            self.purchase_order,
            self.purchase_order_item,
            self.receipt,
            self.receipt_item,
            self.stock_lot,
            self.balance,
        ])
        self.db.commit()

    def _auth_context(self) -> AuthContext:
        return AuthContext(
            user=self.user,
            employee=self.employee,
            department=self.department,
            role_codes=["ADMIN"],
            role_names=["管理员"],
            permission_codes=[],
        )

    def _archive_result(self, *, business_id: int) -> DocumentArchiveGenerateResult:
        return DocumentArchiveGenerateResult(
            business_id=business_id,
            document_type="质量校验单",
            document_no="质检-入库2026-00001-1",
            archive_status="已归档",
            archive_version=1,
            archive_error_message=None,
        )

    def test_inspect_receipt_item_generates_quality_archive_and_returns_metadata(self) -> None:
        def fake_generate_archive(
            db: Session,
            document_type: str,
            business_id: int,
            created_by: int | None = None,
        ) -> DocumentArchiveGenerateResult:
            db.add(
                DocumentArchive(
                    document_type=document_type,
                    business_id=business_id,
                    document_no="质检-入库2026-00001-1",
                    archive_version=1,
                    template_version="单据纸面V1",
                    file_format="PDF",
                    file_name="质检-入库2026-00001-1.pdf",
                    file_path="/tmp/质检-入库2026-00001-1.pdf",
                    file_hash="quality123",
                    status="已归档",
                    error_message=None,
                    created_by=created_by,
                    created_at=datetime(2026, 6, 12, 10, 30, 0),
                    updated_at=datetime(2026, 6, 12, 10, 30, 0),
                )
            )
            db.commit()
            return self._archive_result(business_id=business_id)

        with patch("app.api.routes.quality.generate_document_archive", side_effect=fake_generate_archive) as archive_mock:
            result = inspect_receipt_item(
                self.receipt_item.id,
                QualityInspectionAction(result="PASS", remark="表面无划伤"),
                context=self._auth_context(),
                db=self.db,
            )

        archive_mock.assert_called_once_with(self.db, "质量校验单", int(self.receipt_item.id), created_by=1)
        self.assertEqual(result.status, "PASSED")
        self.assertEqual(result.quality_status, "PASS")
        self.assertEqual(result.archive_status, "已归档")
        self.assertEqual(result.archive_version, 1)
        self.assertIsNone(result.archive_error_message)

        saved_archive = self.db.scalar(
            select(DocumentArchive).where(
                DocumentArchive.document_type == "质量校验单",
                DocumentArchive.business_id == self.receipt_item.id,
            )
        )
        self.assertIsNotNone(saved_archive)

    def test_inspect_receipt_item_returns_failed_archive_metadata_when_archive_generation_raises(self) -> None:
        with patch("app.api.routes.quality.generate_document_archive", side_effect=RuntimeError("renderer exploded")):
            result = inspect_receipt_item(
                self.receipt_item.id,
                QualityInspectionAction(result="REJECT", remark="硬度不合格"),
                context=self._auth_context(),
                db=self.db,
            )

        self.assertEqual(result.status, "REJECTED")
        self.assertEqual(result.archive_status, "归档失败")
        self.assertIsNone(result.archive_version)
        self.assertTrue(result.archive_error_message and result.archive_error_message.startswith("归档失败："))


if __name__ == "__main__":
    unittest.main()
