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

from fastapi import HTTPException, status
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from .models import UnitConversion
from .dto import UnitConversionCreate, UnitConversionUpdate


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

        if filters:
            if filters.get("from_unit_code"):
                query = query.filter(
                    UnitConversion.from_unit_code.ilike(f"%{filters['from_unit_code']}%")
                )
            if filters.get("to_unit_code"):
                query = query.filter(
                    UnitConversion.to_unit_code.ilike(f"%{filters['to_unit_code']}%")
                )

        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[UnitConversion]:
        return db.query(UnitConversion).filter(
            UnitConversion.id == id,
            UnitConversion.tenant_id == tenant_id,
            UnitConversion.company_id == company_id,
        ).first()

    @staticmethod
    def create(
        db: Session,
        data: UnitConversionCreate,
        tenant_id: int,
        company_id: int,
    ) -> UnitConversion:
        db_obj = UnitConversion(
            **data.model_dump(),
            tenant_id=tenant_id,
            company_id=company_id
        )
        try:
            db.add(db_obj)
            db.commit()
            db.refresh(db_obj)
            return db_obj
        except IntegrityError as exc:
            db.rollback()
            # Puede ser FK de unidades o duplicado de (from_unit_code, to_unit_code, tenant, company)
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Verifica que los códigos de unidad existan y que la conversión no esté duplicada",
            ) from exc

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

        try:
            db.commit()
            db.refresh(db_obj)
            return db_obj
        except IntegrityError as exc:
            db.rollback()
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Verifica que los códigos de unidad existan y que la conversión no esté duplicada",
            ) from exc

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

        db.delete(db_obj)
        db.commit()
        return True
