from __future__ import annotations

import unittest
from decimal import Decimal

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


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


import app.models.master_data  # noqa: E402,F401
import app.models.operations  # noqa: E402,F401
from app.api.routes.inventory import create_customer_supplied_inbound, create_warehouse_inbound  # noqa: E402
from app.models.base import Base  # noqa: E402
from app.models.master_data import Item, StockBalance, Warehouse  # noqa: E402
from app.models.operations import StockLot  # noqa: E402
from app.schemas.operations import CustomerSuppliedInboundCreate, WarehouseInboundCreate  # noqa: E402


class User:
    id = 1


class Context:
    user = User()


class CustomerSuppliedInboundLotNoTest(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.material = Item(
            id=1,
            item_code="110501000185",
            item_name="左右后纵梁后段毛坯",
            item_type="RAW_MATERIAL",
            unit_weight_kg=Decimal("1"),
            status="ACTIVE",
        )
        self.warehouse = Warehouse(
            id=1,
            warehouse_code="WH-RAW",
            warehouse_name="原材料库",
            warehouse_type="RAW",
            status="ACTIVE",
        )
        self.db.add_all([self.material, self.warehouse])
        self.db.commit()

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

    def _seed_existing_lot(self, lot_no: str) -> None:
        self.db.add(
            StockLot(
                lot_no=lot_no,
                lot_role="CUSTOMER_SUPPLIED_RAW",
                item_id=self.material.id,
                warehouse_id=self.warehouse.id,
                source_doc_type="CUSTOMER_SUPPLIED_IN",
                source_doc_id=1,
                inbound_qty=Decimal("0"),
                inbound_weight_kg=Decimal("100"),
                remaining_qty=Decimal("0"),
                remaining_weight_kg=Decimal("100"),
                locked_qty=Decimal("0"),
                locked_weight_kg=Decimal("0"),
                unit_cost=Decimal("0"),
                quality_status="PASS",
                status="AVAILABLE",
            )
        )
        self.db.commit()

    def test_generic_customer_supplied_inbound_ignores_manual_lot_no_and_uses_global_raw_sequence(self) -> None:
        self._seed_existing_lot("JH_左右后纵梁后段毛坯00185_0001")

        first_row = create_warehouse_inbound(
            WarehouseInboundCreate(
                biz_type="CUSTOMER_SUPPLIED",
                item_id=self.material.id,
                warehouse_id=self.warehouse.id,
                inbound_weight_kg=50,
                unit_cost=0,
                lot_no="用户手填批次号",
                source_material_sub_batch_no="用户手填来源批次",
                provider_name="客户A",
                waybill_no="YD-KL-001",
                freight_amount=0,
            ),
            context=Context(),
            db=self.db,
        )
        second_row = create_warehouse_inbound(
            WarehouseInboundCreate(
                biz_type="CUSTOMER_SUPPLIED",
                item_id=self.material.id,
                warehouse_id=self.warehouse.id,
                inbound_weight_kg=25,
                unit_cost=0,
                lot_no="第二个用户手填批次号",
                source_material_sub_batch_no="第二个用户手填来源批次",
                provider_name="客户A",
                waybill_no="YD-KL-002",
                freight_amount=0,
            ),
            context=Context(),
            db=self.db,
        )

        lot = self.db.scalar(select(StockLot).where(StockLot.id == first_row.lot_id))
        second_lot = self.db.scalar(select(StockLot).where(StockLot.id == second_row.lot_id))
        balance = self.db.scalar(
            select(StockBalance).where(
                StockBalance.item_id == self.material.id,
                StockBalance.warehouse_id == self.warehouse.id,
            )
        )
        self.assertEqual(lot.lot_no, "YL0001")
        self.assertEqual(second_lot.lot_no, "YL0002")
        self.assertIsNone(lot.source_material_sub_batch_no)
        self.assertEqual(lot.source_material_summary, "客户A")
        self.assertIsNotNone(balance)
        self.assertEqual(balance.weight_on_hand_kg, Decimal("75.000000"))

    def test_dedicated_customer_supplied_inbound_ignores_manual_lot_no_and_starts_from_one(self) -> None:
        row = create_customer_supplied_inbound(
            CustomerSuppliedInboundCreate(
                material_item_id=self.material.id,
                warehouse_id=self.warehouse.id,
                provider_name="客户B",
                inbound_weight_kg=80,
                lot_no="旧接口手填批次号",
            ),
            context=Context(),
            db=self.db,
        )

        lot = self.db.scalar(select(StockLot).where(StockLot.id == row.lot_id))
        self.assertEqual(lot.lot_no, "YL0001")
        self.assertEqual(lot.source_material_summary, "客户B")


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