import logging
from typing import Any, Dict, List, Optional

from fastapi import HTTPException
from sqlalchemy import or_
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

# Importamos el modelo PRINCIPAL
from .models import Part

# Importamos TODOS los DTOs necesarios
from .dto import (
    PartCreateDTO,
    PartUpdateDTO,
    PartResponseDTO,
    PartBasicDTO # ¡Importante tener este!
)

logger = logging.getLogger(__name__)

class PartService:
    """Servicio para gestión de Partes (Anexo 76 + Anexo 24)"""

    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
    ) -> tuple[List[Part], int]:
        
        query = db.query(Part).filter(
            Part.tenant_id == tenant_id, 
            Part.company_id == company_id
        )

        if filters:
            if filters.get("q"):
                search = f"%{filters['q']}%"
                query = query.filter(
                    or_(
                        Part.part_number.ilike(search),
                        Part.description_spanish.ilike(search),
                        Part.commercial_part_number.ilike(search)
                    )
                )
            # Otros filtros...

        total = query.count()
        items = query.offset(skip).limit(limit).all()
        return items, total

    @staticmethod
    def get_by_id(db: Session, part_id: int, tenant_id: int, company_id: int) -> Optional[Part]:
        return db.query(Part).filter(
            Part.id == part_id,
            Part.tenant_id == tenant_id,
            Part.company_id == company_id
        ).first()

    @staticmethod
    def create(db: Session, part_data: PartCreateDTO, tenant_id: int, company_id: int) -> Part:
        # 1. Preparar datos
        data = part_data.model_dump()
        
        # Separar datos anidados
        fa_dict = data.pop('fa_data', None)
        inv_dict = data.pop('inv_data', None)
        
        # Inyectar IDs de contexto (Seguridad Multi-tenant)
        data['company_id'] = company_id
        data['tenant_id'] = tenant_id

        # 2. Verificar duplicados (Usando la UniqueConstraint del modelo)
        existing = db.query(Part).filter(
            Part.tenant_id == tenant_id,
            Part.company_id == company_id,
            Part.part_number == data['part_number']
        ).first()

        if existing:
            raise HTTPException(
                status_code=400, 
                detail=f"El número de parte '{data['part_number']}' ya existe."
            )

        # 3. Crear objeto Part
        db_part = Part(**data)

        # 4. Crear relaciones (Anexo 24)
        # Importamos aquí para evitar ciclos, usando las rutas de tu modelo
        if fa_dict:
            from api.v1.modules.a24.fa.fa_parts.models import FaPart
            # Importante: Pasar tenant/company también al hijo
            db_part.fa_data = FaPart(**fa_dict, tenant_id=tenant_id, company_id=company_id)
        
        if inv_dict:
            from api.v1.modules.a24.inv.inv_parts.models import InvPart
            db_part.inv_data = InvPart(**inv_dict, tenant_id=tenant_id, company_id=company_id)

        try:
            db.add(db_part)
            db.commit()
            db.refresh(db_part)
            return db_part

        except IntegrityError as e:
            db.rollback()
            err_msg = str(e.orig)
            logger.error(f"Error DB creando parte: {err_msg}")
            
            if "foreign key" in err_msg:
                if "unit_of_measure" in err_msg:
                    raise HTTPException(400, "La Unidad de Medida no existe en el catálogo.")
                if "currency_key" in err_msg:
                    raise HTTPException(400, "La Moneda no existe en el catálogo.")
                if "part_class" in err_msg:
                    raise HTTPException(400, "La Clase no existe para este cliente.")
                
                # El error genérico si falla Company/Client
                raise HTTPException(400, "Error de referencia: Verifique Cliente, Compañía o Catálogos.")
            
            raise HTTPException(400, "Error al guardar la parte.")

    @staticmethod
    def update(db: Session, part_id: int, tenant_id: int, part_data: PartUpdateDTO, company_id: int) -> Optional[Part]:
        db_part = PartService.get_by_id(db, part_id, tenant_id, company_id)
        if not db_part:
            return None

        data = part_data.model_dump(exclude_unset=True)
        fa_dict = data.pop('fa_data', None)
        inv_dict = data.pop('inv_data', None)

        # Actualizar campos directos
        for key, value in data.items():
            setattr(db_part, key, value)

        # Actualizar FA Data
        if fa_dict is not None:
            if db_part.fa_data:
                for k, v in fa_dict.items():
                    setattr(db_part.fa_data, k, v)
            else:
                from api.v1.modules.a24.fa.fa_parts.models import FaPart
                db_part.fa_data = FaPart(**fa_dict, tenant_id=tenant_id, company_id=company_id)

        # Actualizar INV Data
        if inv_dict is not None:
            if db_part.inv_data:
                for k, v in inv_dict.items():
                    setattr(db_part.inv_data, k, v)
            else:
                from api.v1.modules.a24.inv.inv_parts.models import InvPart
                db_part.inv_data = InvPart(**inv_dict, tenant_id=tenant_id, company_id=company_id)

        try:
            db.commit()
            db.refresh(db_part)
            return db_part
        except IntegrityError as e:
            db.rollback()
            raise HTTPException(400, f"Error actualizando: {str(e.orig)}")

    @staticmethod
    def delete(db: Session, part_id: int, tenant_id: int, company_id: int) -> bool:
        db_part = PartService.get_by_id(db, part_id, tenant_id, company_id)
        if not db_part: return False
        
        try:
            db.delete(db_part)
            db.commit()
            return True
        except Exception:
            db.rollback()
            raise HTTPException(500, "Error eliminando parte")