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

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

from .models import Concept
from .dto import ConceptCreate, ConceptUpdate

logger = logging.getLogger(__name__)


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

        if filters:
            if filters.get("code"):
                query = query.filter(Concept.code.ilike(f"%{filters['code']}%"))
            if filters.get("description"):
                query = query.filter(
                    Concept.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[Concept]:
        return db.query(Concept).filter(
            Concept.id == id,
            Concept.tenant_id == tenant_id,
            Concept.company_id == company_id
        ).first()

    @staticmethod
    def create(
        db: Session, data: ConceptCreate, tenant_id: int, company_id: int
    ) -> Concept:
        data_dict = data.model_dump()
        data_dict['company_id'] = company_id
        data_dict['tenant_id'] = tenant_id

        db_obj = Concept(**data_dict)
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def update(
        db: Session, id: int, tenant_id: int, data: ConceptUpdate, company_id: int
    ) -> Optional[Concept]:
        db_obj = ConceptService.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 = ConceptService.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"IntegrityError deleting concept {id}: {str(e)}")
            if "foreign key constraint" in str(e).lower():
                raise HTTPException(
                    status_code=400, 
                    detail="No se puede eliminar el concepto porque tiene registros relacionados"
                )
            raise HTTPException(status_code=400, detail="Error al eliminar el concepto")
        except Exception as e:
            db.rollback()
            logger.error(f"Error deleting concept {id}: {str(e)}")
            raise HTTPException(status_code=500, detail="Error al eliminar el concepto")
