"""
Capa de servicio para lógica de negocio de tenants
"""

import json
import logging
from typing import List, Optional

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

from .dto import TenantCreateDTO, TenantResponseDTO, TenantUpdateDTO
from .models import Tenant, TenantType

logger = logging.getLogger(__name__)


class TenantService:
    """Servicio para gestión de tenants"""

    def __init__(self, db: Session):
        self.db = db

    def create_tenant(self, tenant_data: TenantCreateDTO) -> TenantResponseDTO:
        """
        Crea un nuevo tenant en el sistema

        Args:
            tenant_data: Datos del tenant a crear

        Returns:
            TenantResponseDTO con información del tenant creado

        Raises:
            HTTPException: Si el slug o realm ya existen
        """
        try:
            # Verificar que no exista el slug
            existing = (
                self.db.query(Tenant).filter(Tenant.slug == tenant_data.slug).first()
            )
            if existing:
                raise HTTPException(
                    status_code=400,
                    detail=f"Tenant with slug '{tenant_data.slug}' already exists",
                )

            # Crear tenant
            db_tenant = Tenant(
                name=tenant_data.name,
                slug=tenant_data.slug,
                keycloak_realm=tenant_data.keycloak_realm,
                type=TenantType(tenant_data.type.value),
                contact_name=tenant_data.contact_name,
                contact_email=tenant_data.contact_email,
                contact_phone=tenant_data.contact_phone,
                is_active=True,
            )

            self.db.add(db_tenant)
            self.db.commit()
            self.db.refresh(db_tenant)

            return TenantResponseDTO.model_validate(db_tenant)

        except IntegrityError as e:
            self.db.rollback()
            logger.error(f"IntegrityError creating tenant: {str(e)}")
            raise HTTPException(
                status_code=400, detail="Tenant with this slug or realm already exists"
            )
        except HTTPException:
            raise
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error creating tenant: {str(e)}")
            raise HTTPException(status_code=500, detail="Error creating tenant")

    def get_tenant(self, tenant_id: int) -> Optional[TenantResponseDTO]:
        """
        Obtiene un tenant por ID

        Args:
            tenant_id: ID del tenant

        Returns:
            TenantResponseDTO o None si no existe
        """
        tenant = self.db.query(Tenant).filter(Tenant.id == tenant_id).first()
        if not tenant:
            return None
        return TenantResponseDTO.model_validate(tenant)

    def get_tenant_by_slug(self, slug: str) -> Optional[TenantResponseDTO]:
        """Obtiene un tenant por slug"""
        tenant = self.db.query(Tenant).filter(Tenant.slug == slug).first()
        if not tenant:
            return None
        return TenantResponseDTO.model_validate(tenant)

    def list_tenants(
        self, skip: int = 0, limit: int = 100, active_only: bool = False
    ) -> List[TenantResponseDTO]:
        """
        Lista todos los tenants

        Args:
            skip: Número de registros a omitir
            limit: Número máximo de registros a retornar
            active_only: Si True, solo retorna tenants activos

        Returns:
            Lista de TenantResponseDTO
        """
        query = self.db.query(Tenant)

        if active_only:
            query = query.filter(Tenant.is_active)

        tenants = query.offset(skip).limit(limit).all()
        return [TenantResponseDTO.model_validate(t) for t in tenants]

    def update_tenant(
        self, tenant_id: int, tenant_data: TenantUpdateDTO
    ) -> Optional[TenantResponseDTO]:
        """
        Actualiza un tenant

        Args:
            tenant_id: ID del tenant a actualizar
            tenant_data: Datos a actualizar

        Returns:
            TenantResponseDTO actualizado o None si no existe
        """
        tenant = self.db.query(Tenant).filter(Tenant.id == tenant_id).first()
        if not tenant:
            return None

        # Actualizar solo campos proporcionados
        update_data = tenant_data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            setattr(tenant, field, value)

        try:
            self.db.commit()
            self.db.refresh(tenant)
            return TenantResponseDTO.model_validate(tenant)
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error updating tenant {tenant_id}: {str(e)}")
            raise HTTPException(status_code=500, detail="Error updating tenant")

    def delete_tenant(self, tenant_id: int) -> bool:
        """
        Elimina (desactiva) un tenant

        Args:
            tenant_id: ID del tenant a eliminar

        Returns:
            True si se eliminó, False si no existe
        """
        tenant = self.db.query(Tenant).filter(Tenant.id == tenant_id).first()
        if not tenant:
            return False

        # Soft delete: marcar como inactivo
        tenant.is_active = False

        try:
            self.db.commit()
            return True
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error deleting tenant {tenant_id}: {str(e)}")
            raise HTTPException(status_code=500, detail="Error deleting tenant")

    def upgrade_to_dedicated(
        self, tenant_id: int, db_config: dict
    ) -> Optional[TenantResponseDTO]:
        """
        Actualiza un tenant de BD compartida a BD dedicada

        Args:
            tenant_id: ID del tenant
            db_config: Configuración de BD dedicada

        Returns:
            TenantResponseDTO actualizado
        """
        tenant = self.db.query(Tenant).filter(Tenant.id == tenant_id).first()
        if not tenant:
            return None

        tenant.type = TenantType.DEDICATED
        tenant.db_config = json.dumps(db_config)

        try:
            self.db.commit()
            self.db.refresh(tenant)
            return TenantResponseDTO.model_validate(tenant)
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error upgrading tenant {tenant_id}: {str(e)}")
            raise HTTPException(status_code=500, detail="Error upgrading tenant")
