from __future__ import annotations

import unittest
from datetime import UTC, datetime

from fastapi import HTTPException
from sqlalchemy import BigInteger, create_engine
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.models.base import Base  # noqa: E402
from app.models.master_data import Warehouse  # noqa: E402
from app.models.operations import Stocktake, StocktakeWarehouse  # noqa: E402
from app.services.stocktake import ensure_warehouses_unlocked, get_locked_warehouse_ids  # noqa: E402


class StocktakeLockingTest(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()

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

    def _seed(self) -> None:
        now = datetime.now(UTC)
        self.raw = Warehouse(
            id=1,
            warehouse_code="WH-RAW-01",
            warehouse_name="原材料库",
            warehouse_type="RAW",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.finished = Warehouse(
            id=2,
            warehouse_code="WH-FG-01",
            warehouse_name="成品库",
            warehouse_type="FINISHED",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.db.add_all([self.raw, self.finished])
        self.db.flush()
        stocktake = Stocktake(
            stocktake_no="PK-20260530-001",
            status="LOCKED",
            started_by_user_id=1,
            started_at=now,
            remark="测试锁库",
        )
        self.db.add(stocktake)
        self.db.flush()
        self.db.add(
            StocktakeWarehouse(
                stocktake_id=stocktake.id,
                warehouse_id=self.raw.id,
                warehouse_type="RAW",
                warehouse_name="原材料库",
                status="LOCKED",
            )
        )
        self.db.commit()

    def test_get_locked_warehouse_ids_returns_only_active_stocktakes(self) -> None:
        locked = get_locked_warehouse_ids(self.db, [self.raw.id, self.finished.id])

        self.assertEqual(locked, {self.raw.id})

    def test_ensure_warehouses_unlocked_raises_for_locked_warehouse(self) -> None:
        with self.assertRaises(HTTPException) as exc:
            ensure_warehouses_unlocked(self.db, [self.raw.id], "入库")

        self.assertEqual(exc.exception.status_code, 423)
        self.assertIn("原材料库正在盘库", exc.exception.detail)

    def test_ensure_warehouses_unlocked_allows_unlocked_warehouse(self) -> None:
        ensure_warehouses_unlocked(self.db, [self.finished.id], "销售出库")

    def test_inventory_inbound_is_blocked_when_warehouse_is_locked(self) -> None:
        from decimal import Decimal

        from app.api.routes.inventory import create_warehouse_inbound
        from app.models.master_data import Item
        from app.schemas.operations import WarehouseInboundCreate

        now = datetime.now(UTC)
        item = Item(
            id=1,
            item_code="原材料00001",
            item_name="冷轧钢板",
            item_type="RAW_MATERIAL",
            unit_weight_kg=Decimal("1"),
            safety_stock_weight_kg=Decimal("0"),
            scrap_sale_price=Decimal("0"),
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        self.db.add(item)
        self.db.commit()

        class User:
            id = 1

        class Context:
            user = User()

        with self.assertRaises(HTTPException) as exc:
            create_warehouse_inbound(
                WarehouseInboundCreate(
                    biz_type="OPENING",
                    item_id=item.id,
                    warehouse_id=self.raw.id,
                    inbound_weight_kg=10,
                    unit_cost=1,
                ),
                context=Context(),
                db=self.db,
            )

        self.assertEqual(exc.exception.status_code, 423)
        self.assertIn("正在盘库", exc.exception.detail)

    def test_lock_guard_message_names_action(self) -> None:
        with self.assertRaises(HTTPException) as exc:
            ensure_warehouses_unlocked(self.db, [self.raw.id], "到货入库")

        self.assertEqual(exc.exception.status_code, 423)
        self.assertIn("到货入库", exc.exception.detail)


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