from uuid import UUID
from decimal import Decimal
import logging

from app.core.module_framework.cqrs import ICommand, ICommandHandler
from app.core.module_framework.decorators import transactional, log_execution
from app.core.postgres.base_pkg.base_client import BasePostgresClient
from app.modules.inventory.domain.entities import InventoryItem
from app.modules.inventory.infrastructure.repositories import InventoryRepository

logger = logging.getLogger(__name__)

class ReserveStockCommand(ICommand):
    material_code: str
    warehouse_id: str
    quantity: Decimal

class ReserveStockCommandHandler(ICommandHandler[ReserveStockCommand, None]):
    def __init__(self, postgres_client: BasePostgresClient):
        self._postgres_client = postgres_client

    @log_execution
    @transactional
    def handle(self, command: ReserveStockCommand) -> None:
        repo = InventoryRepository(self._conn)
        from app.core.module_framework.event_bus import event_bus
        
        try:
            # 1. Truy vấn bằng SQL RAW có FOR UPDATE để Lock row (Tránh Race Condition kết hợp Optimistic Locking)
            with self._conn.cursor() as cur:
                cur.execute("""
                    SELECT id, material_code, warehouse_id, quantity_on_hand, reserved_quantity, version
                    FROM inventory.items
                    WHERE material_code = %s AND warehouse_id = %s
                    FOR UPDATE
                """, (command.material_code, command.warehouse_id))
                
                row = cur.fetchone()
                if not row:
                    raise ValueError(f"Không tìm thấy vật tư {command.material_code} trong kho {command.warehouse_id}")
                    
                item = InventoryItem(
                    id=row['id'],
                    material_code=row['material_code'],
                    warehouse_id=row['warehouse_id'],
                    quantity_on_hand=row['quantity_on_hand'],
                    reserved_quantity=row['reserved_quantity']
                )
                
                # Domain Entity validation (kiểm tra số âm / lock conflict)
                item.reserve_stock(command.quantity)
                
                # Lưu lại
                repo.save(item)
                
            logger.info(f"[Inventory] Đã giữ chỗ {command.quantity} cho mã {command.material_code}")
            
        except Exception as e:
            logger.warning(f"[Inventory] Thất bại khi giữ chỗ (Race Condition / Không đủ hàng): {e}")
            # [D6] Cứu vãn bằng cách phát Event cho Takeoff biết để rollback
            event_payload = {
                "material_code": command.material_code,
                "requested_quantity": float(command.quantity),
                "error_reason": str(e)
            }
            with self._conn.cursor() as cur:
                event_bus.publish_outbox(cur, "StockNotEnoughEvent", event_payload)
            # Reraise để transaction bị abort
            raise e
