from uuid import UUID
from datetime import datetime
import logging
from typing import Dict, Any

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.core.module_framework.event_bus import event_bus
from app.modules.agents.application.llm_client import LLMClient
from app.modules.takeoff.application.pdf_classifier import DrawingPdfClassifierMixin
from app.modules.takeoff.infrastructure.repositories import TakeoffRepository

logger = logging.getLogger(__name__)

class ApproveTakeoffCommand(ICommand):
    takeoff_id: UUID

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

    @log_execution
    @transactional
    def handle(self, command: ApproveTakeoffCommand) -> None:
        # Note: Bỏ qua bước lookup Entity vì đang giả lập (Trong thực tế cần query qua repo)
        logger.info(f"[Takeoff Module] Phê duyệt bóc tách {command.takeoff_id}")
        
        # Publish Event cho Inventory lắng nghe
        event_payload = {
            "takeoff_id": str(command.takeoff_id),
            "material_code": "STEEL_D10", # Mock data for demonstration
            "quantity": 1500.50, # Mock data for demonstration
            "timestamp": datetime.utcnow().isoformat()
        }
        
        with self._conn.cursor() as cur:
            event_bus.publish_outbox(cur, "TakeoffApprovedIntegrationEvent", event_payload)
            
        logger.info(f"[Takeoff Module] Đã phát sự kiện TakeoffApprovedIntegrationEvent")

# ==============================================================================
# D6 PDF BOQ EXTRACTION CQRS COMMAND
# ==============================================================================

class ExtractBoQFromPdfCommand(ICommand):
    """CQRS Command to extract Bill of Quantities (BoQ) from a PDF file."""
    takeoff_id: UUID
    file_path: str

class ExtractBoQFromPdfCommandHandler(ICommandHandler[ExtractBoQFromPdfCommand, Dict[str, Any]]):
    def __init__(self, postgres_client: BasePostgresClient):
        self._postgres_client = postgres_client
        self._llm_client = LLMClient()
        
    @log_execution
    @transactional
    def handle(self, command: ExtractBoQFromPdfCommand) -> Dict[str, Any]:
        """Thực thi logic bóc tách BoQ từ file PDF (Deep Matrix D4, D5, D6)."""
        logger.info(f"[Takeoff Module] Đang trích xuất BoQ từ file {command.file_path}")
        
        # 1. Tự động giám định loại PDF (Phân biệt HSMT và Bản vẽ CAD pdf)
        classification = DrawingPdfClassifierMixin.classify_pdf_content_type(command.file_path)
        
        is_hsmt = classification.get("is_hsmt", False)
        
        # 2. Xử lý Logic Nghiệp Vụ [D4]
        # (Giả lập OCR & Parsing table ở đây)
        extracted_boq_data = {
            "items": [
                {"stt": 1, "material_name": "Bê tông thương phẩm mác 250", "quantity": 105.5, "unit": "m3", "unit_price": 1200000},
                {"stt": 2, "material_name": "Thép cuộn D10", "quantity": 1500.5, "unit": "kg", "unit_price": 15000}
            ],
            "state": "DRAFT_BOQ",
            "is_blinded": False
        }
        
        # Lọc bỏ cột "Đơn giá" nếu là Hồ sơ Mời Thầu (Blinded BoQ)
        if is_hsmt:
            logger.info("[Takeoff Module] Nhận diện là Hồ sơ Mời thầu (HSMT). Đang kích hoạt cơ chế Blinded BoQ (Lọc bỏ đơn giá)...")
            extracted_boq_data["is_blinded"] = True
            for item in extracted_boq_data["items"]:
                if "unit_price" in item:
                    del item["unit_price"]
        
        # 3. Giao tiếp Module Core: Validate tên vật tư [D5]
        # Gọi LLM (Gemini) để mapping tên vật tư thô sang mã vật tư chuẩn của CTY
        for item in extracted_boq_data["items"]:
            raw_name = item["material_name"]
            # Gọi LLM Client (Ví dụ)
            prompt = f"Phân tích tên vật liệu '{raw_name}' và chuẩn hóa theo danh mục ERP xây dựng."
            # response = self._llm_client.generate(prompt) # (Comment lại để tránh call API thực tế khi test)
            logger.info(f"[Takeoff Module] [LLM Validation] Đã chuẩn hóa: '{raw_name}'")
            item["validated"] = True
            
        # 4. Lưu trạng thái DRAFT vào Database
        with self._conn.cursor() as cur:
            # Trong thực tế sẽ gọi repo.save(TakeoffEntity(state='DRAFT_BOQ', ...))
            cur.execute("""
                UPDATE takeoff.takeoffs
                SET status = 'DRAFT_BOQ'
                WHERE id = %s
            """, (str(command.takeoff_id),))
            
        logger.info(f"[Takeoff Module] Đã trích xuất BoQ thành công cho Takeoff {command.takeoff_id}")
        return extracted_boq_data

# ==============================================================================
# D6 CAD GEOMETRIC PARSING CQRS COMMAND
# ==============================================================================

class ParseCadGeometryCommand(ICommand):
    """CQRS Command to parse geometric bounding boxes from CAD DWG/DXF files."""
    takeoff_id: UUID
    file_path: str
    
class ParseCadGeometryCommandHandler(ICommandHandler[ParseCadGeometryCommand, Dict[str, Any]]):
    def __init__(self, postgres_client: BasePostgresClient):
        self._postgres_client = postgres_client

    @log_execution
    @transactional
    def handle(self, command: ParseCadGeometryCommand) -> Dict[str, Any]:
        """Thực thi phân tích hình học CAD (Deep Matrix D4, D5, D6)."""
        logger.info(f"[Takeoff Module] Đang phân tích hình học CAD từ {command.file_path}")
        
        # [D4] Phân tích tọa độ XY, giao điểm (Giả lập Logic từ cad_geometric_engine.py)
        # Trong thực tế sẽ dùng ezdxf để đọc Line/Polyline
        lines = [
            {"type": "Line", "thickness": 0.5, "volume": 0},
            {"type": "Polyline", "thickness": 2.0, "volume": 12500.0}, # Khối xây lớn
            {"type": "Line", "thickness": 1.5, "volume": 450.0}
        ]
        
        valid_nodes = []
        total_volume = 0.0
        
        # [D4] Bỏ qua các nét vẽ rác (độ dày < 1px)
        for line in lines:
            if line["thickness"] < 1.0:
                logger.debug("Bỏ qua nét vẽ rác < 1px")
                continue
                
            valid_nodes.append(line)
            total_volume += line.get("volume", 0)
            
        logger.info(f"[Takeoff Module] Tìm thấy {len(valid_nodes)} nodes hình học hợp lệ. Tổng khối lượng: {total_volume}")
        
        # [D5] Bắn Event cảnh báo nếu khối lượng vượt 10,000
        if total_volume > 10000:
            logger.warning("[Takeoff Module] Khối lượng > 10,000. Đang phát LargeVolumeDetectedEvent...")
            event_payload = {
                "takeoff_id": str(command.takeoff_id),
                "total_volume": total_volume,
                "timestamp": datetime.utcnow().isoformat()
            }
            with self._conn.cursor() as cur:
                event_bus.publish_outbox(cur, "LargeVolumeDetectedEvent", event_payload)
                
        # [D4] Cập nhật State thành GEOMETRIC_PARSED
        with self._conn.cursor() as cur:
            cur.execute("""
                UPDATE takeoff.takeoffs
                SET status = 'GEOMETRIC_PARSED',
                    parsed_volume = %s
                WHERE id = %s
            """, (total_volume, str(command.takeoff_id)))
            
        return {
            "status": "success",
            "nodes_count": len(valid_nodes),
            "total_volume": total_volume
        }


