"""
Capa de servicio para lógica de negocio de catálogos de errores
"""

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

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

from .dto import (
    ErrorClassificationCreateDTO,
    ErrorClassificationResponseDTO,
    ErrorClassificationUpdateDTO,
    ErrorCatalogCreateDTO,
    ErrorCatalogResponseDTO,
    ErrorCatalogUpdateDTO,
)
from .models import ErrorClassification, ErrorCatalog

logger = logging.getLogger(__name__)


class ErrorClassificationService:
    """Servicio para gestión de clasificaciones de errores"""

    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 50,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[ErrorClassification], int]:
        """Get all error classifications with pagination"""
        query = db.query(ErrorClassification).filter(
            ErrorClassification.tenant_id == tenant_id,
            ErrorClassification.company_id == company_id
        )

        if filters:
            if filters.get("code"):
                query = query.filter(
                    ErrorClassification.code.ilike(f"%{filters['code']}%")
                )
            if filters.get("level"):
                query = query.filter(
                    ErrorClassification.level.ilike(f"%{filters['level']}%")
                )

        total = query.count()
        classifications = query.offset(skip).limit(limit).all()

        return classifications, total

    @staticmethod
    def get_by_code(
        db: Session, code: str, tenant_id: int, company_id: int
    ) -> Optional[ErrorClassification]:
        """Get error classification by code"""
        return (
            db.query(ErrorClassification)
            .filter(
                ErrorClassification.code == code,
                ErrorClassification.tenant_id == tenant_id,
                ErrorClassification.company_id == company_id
            )
            .first()
        )

    @staticmethod
    def get_by_id(
        db: Session, id: int, tenant_id: int, company_id: int
    ) -> Optional[ErrorClassification]:
        """Get error classification by ID"""
        return (
            db.query(ErrorClassification)
            .filter(
                ErrorClassification.id == id,
                ErrorClassification.tenant_id == tenant_id,
                ErrorClassification.company_id == company_id
            )
            .first()
        )

    @staticmethod
    def create(
        db: Session, classification_data: ErrorClassificationCreateDTO, tenant_id: int, company_id: int
    ) -> ErrorClassification:
        """Create a new error classification"""
        try:
            db_classification = ErrorClassification(
                **classification_data.model_dump(exclude_unset=True),
                tenant_id=tenant_id,
                company_id=company_id
            )

            db.add(db_classification)
            db.commit()
            db.refresh(db_classification)

            return db_classification

        except IntegrityError as e:
            db.rollback()
            logger.error(
                f"IntegrityError creating error classification: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="Error classification already exists",
            )
        except Exception as e:
            db.rollback()
            logger.error(f"Error creating error classification: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error creating error classification"
            )

    @staticmethod
    def update(
        db: Session, id: int, tenant_id: int, classification_data: ErrorClassificationUpdateDTO, company_id: int
    ) -> Optional[ErrorClassification]:
        """Update an error classification"""
        try:
            db_classification = ErrorClassificationService.get_by_id(
                db, id, tenant_id, company_id)
            if not db_classification:
                return None

            for key, value in classification_data.model_dump(exclude_unset=True).items():
                setattr(db_classification, key, value)

            db.commit()
            db.refresh(db_classification)
            return db_classification

        except IntegrityError as e:
            db.rollback()
            logger.error(
                f"IntegrityError updating error classification: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="Error updating error classification",
            )
        except Exception as e:
            db.rollback()
            logger.error(f"Error updating error classification: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error updating error classification"
            )

    @staticmethod
    def delete(
        db: Session, id: int, tenant_id: int, company_id: int
    ) -> bool:
        """Delete an error classification"""
        try:
            db_classification = ErrorClassificationService.get_by_id(
                db, id, tenant_id, company_id)
            if not db_classification:
                return False

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

        except Exception as e:
            db.rollback()
            logger.error(f"Error deleting error classification: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error deleting error classification"
            )


class ErrorCatalogService:
    """Servicio para gestión de catálogos de errores"""

    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 50,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[ErrorCatalog], int]:
        """Get all error catalogs with pagination"""
        query = db.query(ErrorCatalog).filter(
            ErrorCatalog.tenant_id == tenant_id,
            ErrorCatalog.company_id == company_id
        )

        if filters:
            if filters.get("code"):
                query = query.filter(
                    ErrorCatalog.code.ilike(f"%{filters['code']}%"))
            if filters.get("description"):
                query = query.filter(
                    ErrorCatalog.description.ilike(
                        f"%{filters['description']}%")
                )
            if filters.get("classification_id"):
                query = query.filter(
                    ErrorCatalog.classification_id == filters['classification_id']
                )

        total = query.count()
        catalogs = query.offset(skip).limit(limit).all()

        return catalogs, total

    @staticmethod
    def get_by_code(
        db: Session, code: str, tenant_id: int, company_id: int
    ) -> Optional[ErrorCatalog]:
        """Get error catalog by code"""
        return db.query(ErrorCatalog).filter(
            ErrorCatalog.code == code,
            ErrorCatalog.tenant_id == tenant_id,
            ErrorCatalog.company_id == company_id
        ).first()

    @staticmethod
    def get_by_id(
        db: Session, id: int, tenant_id: int, company_id: int
    ) -> Optional[ErrorCatalog]:
        """Get error catalog by ID"""
        return db.query(ErrorCatalog).filter(
            ErrorCatalog.id == id,
            ErrorCatalog.tenant_id == tenant_id,
            ErrorCatalog.company_id == company_id
        ).first()

    @staticmethod
    def get_by_classification(
        db: Session, classification_id: int, tenant_id: int, company_id: int
    ) -> List[ErrorCatalog]:
        """Get all errors by classification"""
        return (
            db.query(ErrorCatalog)
            .filter(
                ErrorCatalog.classification_id == classification_id,
                ErrorCatalog.tenant_id == tenant_id,
                ErrorCatalog.company_id == company_id
            )
            .all()
        )

    @staticmethod
    def create(
        db: Session, error_data: ErrorCatalogCreateDTO, tenant_id: int, company_id: int
    ) -> ErrorCatalog:
        """Create a new error catalog"""
        try:
            # Validate classification exists if provided
            if error_data.classification_id:
                classification = (
                    db.query(ErrorClassification)
                    .filter(
                        ErrorClassification.id == error_data.classification_id,
                        ErrorClassification.tenant_id == tenant_id,
                        ErrorClassification.company_id == company_id
                    )
                    .first()
                )
                if not classification:
                    raise HTTPException(
                        status_code=400,
                        detail="Classification not found",
                    )

            db_error = ErrorCatalog(
                **error_data.model_dump(exclude_unset=True),
                tenant_id=tenant_id,
                company_id=company_id
            )

            db.add(db_error)
            db.commit()
            db.refresh(db_error)

            return db_error

        except HTTPException:
            raise
        except IntegrityError as e:
            db.rollback()
            logger.error(f"IntegrityError creating error catalog: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="Error already exists",
            )
        except Exception as e:
            db.rollback()
            logger.error(f"Error creating error catalog: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error creating error catalog"
            )

    @staticmethod
    def update(
        db: Session, id: int, tenant_id: int, error_data: ErrorCatalogUpdateDTO, company_id: int
    ) -> Optional[ErrorCatalog]:
        """Update an error catalog"""
        try:
            db_error = ErrorCatalogService.get_by_id(
                db, id, tenant_id, company_id)
            if not db_error:
                return None

            for key, value in error_data.model_dump(exclude_unset=True).items():
                setattr(db_error, key, value)

            db.commit()
            db.refresh(db_error)
            return db_error

        except IntegrityError as e:
            db.rollback()
            logger.error(f"IntegrityError updating error catalog: {str(e)}")
            raise HTTPException(
                status_code=400,
                detail="Error updating error catalog",
            )
        except Exception as e:
            db.rollback()
            logger.error(f"Error updating error catalog: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error updating error catalog"
            )

    @staticmethod
    def delete(
        db: Session, id: int, tenant_id: int, company_id: int
    ) -> bool:
        """Delete an error catalog"""
        try:
            db_error = ErrorCatalogService.get_by_id(
                db, id, tenant_id, company_id)
            if not db_error:
                return False

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

        except Exception as e:
            db.rollback()
            logger.error(f"Error deleting error catalog: {str(e)}")
            raise HTTPException(
                status_code=500, detail="Error deleting error catalog"
            )
