from typing import List, Optional, Tuple, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import cast, String, select
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException
import logging

from .models import Legend
from .dto import LegendCreate, LegendUpdate

logger = logging.getLogger(__name__)


class LegendService:
    @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[Legend], int]:
        query = db.query(Legend).filter(
            Legend.tenant_id == tenant_id,
            Legend.company_id == company_id
        )

        if filters:
            if filters.get("code"):
                code_str = str(filters["code"]).strip()
                if code_str:
                    query = query.filter(
                        cast(Legend.code, String).ilike(f"%{code_str}%")
                    )
            if filters.get("description"):
                query = query.filter(
                    Legend.description.ilike(f"%{filters['description']}%")
                )

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

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

    @staticmethod
    def create(
        db: Session,
        data: LegendCreate,
        tenant_id: int,
        company_id: int
    ) -> Legend:
        db_obj = Legend(
            **data.model_dump(),
            tenant_id=tenant_id,
            company_id=company_id
        )
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def update(
        db: Session,
        id: int,
        tenant_id: int,
        data: LegendUpdate,
        company_id: int
    ) -> Optional[Legend]:
        db_obj = LegendService.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

    @staticmethod
    def delete(
        db: Session,
        id: int,
        tenant_id: int,
        company_id: int
    ) -> bool:
        db_obj = LegendService.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 leyenda {id}: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="No se puede eliminar esta leyenda porque tiene registros relacionados (pedimentos, facturas, etc.). Primero debe eliminar o reasignar esos registros."
            )
