from __future__ import annotations

import unittest
from datetime import UTC, datetime
from decimal import Decimal

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.miniapp  # noqa: E402,F401
from app.api.routes import master_data  # noqa: E402
from app.models.base import Base  # noqa: E402
from app.models.master_data import Item  # noqa: E402
from app.models.miniapp import MiniAppProduct  # noqa: E402
from app.schemas.database import ProductCreate  # noqa: E402


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

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

    def _payload(self) -> ProductCreate:
        return ProductCreate(
            item_name="测试支架",
            specification=None,
            drawing_no=None,
            product_family=None,
            version_no="V1.0",
            unit_weight_kg=1.2,
            allowed_scrap_rate=0,
            standard_output_qty=0,
            quality_standard=None,
            safety_stock_weight_kg=0,
            status="ACTIVE",
        )

    def _add_item(self, code: str, item_type: str = "FINISHED_GOOD") -> None:
        now = datetime.now(UTC)
        self.db.add(
            Item(
                item_code=code,
                item_name=f"测试产品{code}",
                item_type=item_type,
                specification=None,
                material_grade=None,
                unit_weight_kg=Decimal("1.000000"),
                safety_stock_weight_kg=Decimal("0"),
                scrap_sale_price=Decimal("0"),
                status="ACTIVE",
                created_at=now,
                updated_at=now,
            )
        )
        self.db.commit()

    def test_product_spec_code_starts_from_named_five_digit_sequence(self) -> None:
        self.assertEqual(master_data._build_product_code(self.db, self._payload()), "产品需规00001")

    def test_product_spec_code_increments_matching_codes_only(self) -> None:
        self._add_item("产品需规00001")
        self._add_item("FG-TEST-001")
        self._add_item("PRJ-001")

        self.assertEqual(master_data._build_product_code(self.db, self._payload()), "产品需规00002")

    def test_product_spec_code_ignores_legacy_non_matching_codes(self) -> None:
        self._add_item("FG-LAC-51012170-001")
        self._add_item("LAC-51012170")

        self.assertEqual(master_data._build_product_code(self.db, self._payload()), "产品需规00001")

    def test_product_spec_code_skips_same_code_used_by_other_item_types(self) -> None:
        self._add_item("产品需规00001")
        self._add_item("产品需规00002", item_type="RAW_MATERIAL")

        self.assertEqual(master_data._build_product_code(self.db, self._payload()), "产品需规00003")

    def test_miniapp_created_product_uses_product_spec_code_rule(self) -> None:
        row = MiniAppProduct(
            attendance_point_name="测试考勤点",
            project_no="XCX-PRJ-001",
            product_name="小程序同步支架",
            device_no="",
            process_name="1",
            profile_no="PF-001",
            product_net_weight_kg=Decimal("1.2000"),
            scrap_loss_rate=Decimal("0.0100"),
        )

        product, item = master_data._create_product_from_miniapp_group(self.db, [row])

        self.assertIsNotNone(product)
        self.assertIsNotNone(item)
        self.assertEqual(item.item_code, "产品需规00001")


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