from typing import List, Optional, Tuple, Dict, Any, Type
from sqlalchemy import Sequence
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException
import logging

logger = logging.getLogger(__name__)

from .models import (
    UnitOfMeasureACE, UnitOfMeasureOMA, UnitOfMeasureAmerican, UnitOfMeasureCustoms,
    UnitOfMeasure, UnitOfMeasureGeneral
)
from .dto import (
    UnitOfMeasureACECreate, UnitOfMeasureACEUpdate,
    UnitOfMeasureOMACreate, UnitOfMeasureOMAUpdate,
    UnitOfMeasureAmericanCreate, UnitOfMeasureAmericanUpdate,
    UnitOfMeasureCustomsCreate, UnitOfMeasureCustomsUpdate,
    UnitOfMeasureCreate, UnitOfMeasureUpdate,
    UnitOfMeasureGeneralCreate, UnitOfMeasureGeneralUpdate
)


class BaseService:
    model = None

    @classmethod
    def get_all(
        cls,
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[Any], int]:
        query = db.query(cls.model)
        
        if hasattr(cls.model, "tenant_id"):
            query = query.filter(cls.model.tenant_id == tenant_id)
        
        if hasattr(cls.model, "company_id"):
            query = query.filter(cls.model.company_id == company_id)

        if filters:
            if filters.get("code"):
                query = query.filter(
                    cls.model.code.ilike(f"%{filters['code']}%"))
            if filters.get("description"):
                if hasattr(cls.model, "description"):
                    query = query.filter(cls.model.description.ilike(
                        f"%{filters['description']}%"))

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

    @classmethod
    def get_by_id(
        cls, db: Session, id: int, tenant_id: int, company_id: int
    ) -> Optional[Any]:
        query = db.query(cls.model).filter(cls.model.id == id)
        
        if hasattr(cls.model, "tenant_id"):
            query = query.filter(cls.model.tenant_id == tenant_id)
            
        if hasattr(cls.model, "company_id"):
            query = query.filter(cls.model.company_id == company_id)
            
        return query.first()

    @classmethod
    def create(
        cls,
        db: Session,
        data: Any,
        tenant_id: int,
        company_id: int,
    ) -> Any:
        create_kwargs = data.model_dump()
        
        if hasattr(cls.model, "tenant_id"):
            create_kwargs["tenant_id"] = tenant_id
            
        if hasattr(cls.model, "company_id"):
            create_kwargs["company_id"] = company_id
            
        db_obj = cls.model(**create_kwargs)
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    @classmethod
    def update(
        cls,
        db: Session,
        id: int,
        tenant_id: int,
        data: Any,
        company_id: int,
    ) -> Optional[Any]:
        db_obj = cls.get_by_id(db, id, tenant_id, company_id)
        if not db_obj:
            return None

        update_dict = data.model_dump(exclude_unset=True)
        for key, value in update_dict.items():
            setattr(db_obj, key, value)

        db.commit()
        db.refresh(db_obj)
        return db_obj

    @classmethod
    def delete(
        cls, db: Session, id: int, tenant_id: int, company_id: int
    ) -> bool:
        db_obj = cls.get_by_id(db, id, tenant_id, company_id)
        if not db_obj:
            return False

        try:
            db.delete(db_obj)
            db.commit()
            return True
        except IntegrityError as e:
            db.rollback()
            logger.error(f"Error de integridad al eliminar {cls.model.__name__} {id}: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="No se puede eliminar esta unidad de medida porque tiene registros relacionados. Primero debe eliminar o reasignar esos registros."
            )


class UnitOfMeasureACEService(BaseService):
    model = UnitOfMeasureACE


class UnitOfMeasureOMAService(BaseService):
    model = UnitOfMeasureOMA


class UnitOfMeasureAmericanService(BaseService):
    model = UnitOfMeasureAmerican


class UnitOfMeasureCustomsService(BaseService):
    model = UnitOfMeasureCustoms


class UnitOfMeasureService(BaseService):
    model = UnitOfMeasure


class UnitOfMeasureGeneralService(BaseService):
    model = UnitOfMeasure


def get_all_uom_general(session: Session, skip: int = 0, limit: int = 100) -> Sequence[UnitOfMeasureGeneral]:
    return BaseService._get_all(session, UnitOfMeasureGeneral, skip, limit)


def update_uom_general(session: Session, db_obj: UnitOfMeasureGeneral, data: UnitOfMeasureGeneralUpdate) -> UnitOfMeasureGeneral:
    return BaseService._update(session, db_obj, data)


def delete_uom_general(session: Session, db_obj: UnitOfMeasureGeneral) -> UnitOfMeasureGeneral:
    return BaseService._delete(session, db_obj)
