from models.aulas import Aula
from models.salones import Salon
from models.docentes import Docente
from models.auxiliares import Auxiliar
from models.colaboradores import Colaborador
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import selectinload
from controllers.base_controller import BaseController
from datetime import datetime
from controllers.asistencia_servidores_controller import AsistenciaServidoresController
import logging

# Configuración de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class AulasController(BaseController):
    def __init__(self, session=None):
        super().__init__(model=Aula, session=session)
        logger.info("AulasController inicializado.")

    def crear_aula_con_asistencia(self, datos, user_context=None):
        """Crea el aula y registra los servidores en la tabla pivote de asistencia."""
        errores = self.validar_datos(datos)
        if errores:
            return False, "\n".join(errores)

        fecha_dt = self.validar_y_convertir_fecha(datos.get('fecha'))
        if not fecha_dt:
            return False, "Formato de fecha incorrecto. Debe ser YYYY-MM-DD."
        datos['fecha'] = fecha_dt

        # Extraemos los IDs. Maestra y Auxiliar son 1-a-1 y se guardan en el modelo Aula.
        # Los colaboradores pueden ser múltiples y se registran en la tabla de asistencia.
        id_maestra = datos.get('id_maestra')
        id_auxiliar = datos.get('id_auxiliar')
        ids_colaboradores = datos.pop('ids_colaboradores', [])
        
        if ids_colaboradores and not isinstance(ids_colaboradores, list):
            ids_colaboradores = [ids_colaboradores]

        def operacion(db):
            asistencia_ctrl = AsistenciaServidoresController()
            
            # 1. Crear el aula (vinculada a maestra y auxiliar si existen)
            aula = Aula(**datos)
            db.add(aula)
            db.flush()
            
            # 2. Registrar asistencias individuales
            if id_maestra:
                asistencia_ctrl.registrar_asistencia(db, id_maestra, aula.fecha, 'Maestra', 'aula', aula.id) # id_maestra es id_persona
            
            if id_auxiliar:
                asistencia_ctrl.registrar_asistencia(db, id_auxiliar, aula.fecha, 'Auxiliar', 'aula', aula.id) # id_auxiliar es id_persona

            for s_id in ids_colaboradores:
                if s_id:
                    asistencia_ctrl.registrar_asistencia(
                        db, 
                        s_id, # s_id es id_persona
                        aula.fecha,
                        'Colaborador',
                        'aula',
                        aula.id
                    )

        return self.ejecutar_transaccion(operacion, "Aula y asistencia registradas.", user_context=user_context)

    def actualizar_aula(self, id, datos, user_context=None):
        """
        Actualiza un aula existente con los datos proporcionados.
        """
        if not id or not isinstance(id, int):
            return False, "El ID del aula es obligatorio y debe ser un número entero."

        errores = self.validar_datos(datos)
        if errores:
            return False, "\n".join(errores)

        fecha_dt = self.validar_y_convertir_fecha(datos.get('fecha'))
        if not fecha_dt:
            return False, "Formato de fecha incorrecto. Debe ser YYYY-MM-DD."
        datos['fecha'] = fecha_dt

        id_maestra = datos.get('id_maestra')
        id_auxiliar = datos.get('id_auxiliar')
        ids_colaboradores = datos.pop('ids_colaboradores', [])

        if ids_colaboradores and not isinstance(ids_colaboradores, list):
            ids_colaboradores = [ids_colaboradores]

        def operacion(db):
            aula = db.query(Aula).filter(Aula.id == id, Aula.is_deleted.is_(False)).first()
            if not aula:
                raise ValueError("Aula no encontrada.")
            
            for key, value in datos.items():
                setattr(aula, key, value)

            # Actualizar asistencias individuales (limpiar e insertar nuevas)
            asistencia_ctrl = AsistenciaServidoresController()
            asistencia_ctrl.limpiar_asistencias_referencia(db, 'aula', aula.id)

            if id_maestra: # id_maestra es id_persona
                asistencia_ctrl.registrar_asistencia(db, id_maestra, aula.fecha, 'Maestra', 'aula', aula.id) 
            if id_auxiliar: # id_auxiliar es id_persona
                asistencia_ctrl.registrar_asistencia(db, id_auxiliar, aula.fecha, 'Auxiliar', 'aula', aula.id) 
            for s_id in ids_colaboradores:
                if s_id:
                    asistencia_ctrl.registrar_asistencia(db, s_id, aula.fecha, 'Colaborador', 'aula', aula.id)
            
            logger.info(f"Aula actualizada: ID {id}")

        return self.ejecutar_transaccion(operacion, "Aula actualizada exitosamente.", user_context=user_context)

    def eliminar_aula(self, id, user_context=None):
        """
        Elimina un aula por su ID.
        :param id: ID del aula a eliminar.
        :return: (Exito, Mensaje)
        """
        if not id or not isinstance(id, int):
            return False, "El ID del aula es obligatorio y debe ser un número entero."

        def operacion(db):
            aula = db.query(Aula).filter(Aula.id == id, Aula.is_deleted.is_(False)).first()
            if not aula:
                raise ValueError("Aula no encontrada.")
            
            self.marcar_eliminado(aula, db)
            logger.info(f"Aula eliminada: ID {id}")

        return self.ejecutar_transaccion(operacion, "Aula eliminada exitosamente.", user_context=user_context)

    def listar_aulas(self):
        """
        Lista todas las aulas.
        :return: Lista de objetos Aula.
        """
        return self.listar_aulas_por_fecha()

    def listar_aulas_por_fecha(self, fecha=None):
        """
        Lista las aulas filtrando por fecha cuando se proporciona.
        :param fecha: Fecha en formato YYYY-MM-DD o datetime.date.
        :return: Lista de objetos Aula.
        """
        db = self.get_db_session()
        try:
            query = self.query_activa(db).options(selectinload(Aula.salon))
            if fecha:
                fecha_dt = self.validar_y_convertir_fecha(fecha)
                if fecha_dt:
                    query = query.filter(Aula.fecha == fecha_dt)

            aulas = query.all()
            logger.info(f"{len(aulas)} aulas obtenidas de la base de datos.")
            return aulas
        except SQLAlchemyError as e:
            logger.error(f"Error al listar aulas: {e}")
            return []
        except ValueError as e:
            logger.error(f"Formato de fecha inválido al listar aulas: {e}")
            return []
        finally:
            if not self.session:
                db.close()

    def listar_salones(self):
        """
        Lista todos los salones disponibles.
        :return: Lista de objetos Salon.
        """
        db = self.get_db_session()
        try:
            salones = db.query(Salon).filter(Salon.is_deleted.is_(False)).all()
            logger.info(f"{len(salones)} salones obtenidos para AulasController.")
            return salones
        except SQLAlchemyError as e:
            logger.error(f"Error al listar salones en AulasController: {e}")
            return []
        finally:
            if not self.session:
                db.close()

    def listar_docentes(self):
        """
        Lista todos los docentes (maestras) disponibles.
        :return: Lista de objetos Docente.
        """
        db = self.get_db_session()
        try:
            docentes = db.query(Docente).filter(Docente.is_deleted.is_(False)).all()
            logger.info(f"{len(docentes)} docentes obtenidos para AulasController.")
            return docentes
        except SQLAlchemyError as e:
            logger.error(f"Error al listar docentes en AulasController: {e}")
            return []
        finally:
            if not self.session:
                db.close()

    def listar_auxiliares(self):
        """
        Lista todos los auxiliares disponibles.
        :return: Lista de objetos Auxiliar.
        """
        db = self.get_db_session()
        try:
            auxiliares = db.query(Auxiliar).filter(Auxiliar.is_deleted.is_(False)).all()
            logger.info(f"{len(auxiliares)} auxiliares obtenidos para AulasController.")
            return auxiliares
        except SQLAlchemyError as e:
            logger.error(f"Error al listar auxiliares en AulasController: {e}")
            return []
        finally:
            if not self.session:
                db.close()

    def listar_colaboradores(self):
        """
        Lista todos los colaboradores disponibles.
        :return: Lista de objetos Colaborador.
        """
        db = self.get_db_session()
        try:
            colaboradores = db.query(Colaborador).filter(Colaborador.is_deleted.is_(False)).all()
            logger.info(f"{len(colaboradores)} colaboradores obtenidos para AulasController.")
            return colaboradores
        except SQLAlchemyError as e:
            logger.error(f"Error al listar colaboradores en AulasController: {e}")
            return []
        finally:
            if not self.session:
                db.close()

    def obtener_aula(self, id):
        """
        Obtiene un aula por su ID.
        :param id: ID del aula.
        :return: Objeto Aula o None.
        """
        db = self.get_db_session()
        try:
            return self.query_activa(db).filter(Aula.id == id).first()
        except SQLAlchemyError as e:
            logger.error(f"Error al obtener aula: {e}")
            return None
        finally:
            if not self.session:
                db.close()

    def buscar_aula(self, id=None, fecha=None):
        """
        Busca un aula por ID o fecha.
        :param id: ID del aula a buscar.
        :param fecha: Fecha del aula a buscar.
        :return: (Exito, Objeto, Mensaje)
        """
        if not id and not fecha:
            return False, None, "Debe proporcionar un ID o una fecha para buscar el aula."
        if id and not isinstance(id, int):
            return False, None, "El ID debe ser un número entero."
        if fecha and not isinstance(fecha, str):
            return False, None, "La fecha debe ser una cadena de texto."
        if isinstance(fecha, str):
            try:
                datetime.strptime(fecha, "%Y-%m-%d")
            except ValueError:
                return False, None, "El campo 'fecha' debe tener el formato 'YYYY-MM-DD'."

        aula = self.buscar_por_id_o_fecha(id=id, fecha=fecha, nombre_campo="fecha")
        if aula:
            return True, aula, "Aula encontrada exitosamente."
        else:
            if id:
                return False, None, f"No existe un aula con ID {id}."
            elif fecha:
                return False, None, f"No existe un aula con fecha '{fecha}'."
            return False, None, "Aula no encontrada."

    def validar_datos(self, datos):
        """
        Valida los datos proporcionados para crear o actualizar un aula.
        :param datos: Diccionario con los datos del aula.
        :return: Lista de errores encontrados.
        """
        errores = []

        # Validación de IDs de Servidores (claves foráneas)
        for campo_id in ["id_maestra", "id_auxiliar"]:
            val = datos.get(campo_id)
            if val is not None and not isinstance(val, int):
                errores.append(f"El campo '{campo_id}' debe ser un número entero (ID).")

        ids_col = datos.get("ids_colaboradores")
        if ids_col is not None:
            if not isinstance(ids_col, (int, list)):
                errores.append("El campo 'ids_colaboradores' debe ser un ID o una lista de IDs.")
            elif isinstance(ids_col, list) and not all(isinstance(i, int) for i in ids_col):
                errores.append("Todos los IDs de colaboradores deben ser números enteros.")

        if not isinstance(datos.get("condicion"), str):
            errores.append("El campo 'condicion' debe ser una cadena de texto.")
        if not isinstance(datos.get("ninos"), int):
            errores.append("El campo 'ninos' debe ser un número entero.")
        if not isinstance(datos.get("ninas"), int):
            errores.append("El campo 'ninas' debe ser un número entero.")
        if not isinstance(datos.get("fecha"), str):
            errores.append("El campo 'fecha' debe ser una cadena de texto con formato 'YYYY-MM-DD'.")
        else:
            try:
                datetime.strptime(datos["fecha"], '%Y-%m-%d')
            except ValueError:
                errores.append("El campo 'fecha' debe tener el formato 'YYYY-MM-DD'.")
        if not isinstance(datos.get("id_salon"), int):
            errores.append("El campo 'id_salon' debe ser un número entero.")

        # Validación de lógica de Condición vs Asistencia (basada en la presencia de servidores)
        tiene_maestra = 1 if datos.get("id_maestra") else 0
        tiene_auxiliar = 1 if datos.get("id_auxiliar") else 0
        
        ids_col = datos.get("ids_colaboradores") or []
        if isinstance(ids_col, int):
            ids_col = [ids_col]
        cant_colaboradores = len(ids_col)
        
        ninos = datos.get("ninos", 0)
        ninas = datos.get("ninas", 0)
        condicion = datos.get("condicion")

        total_asistencia = sum([tiene_maestra, tiene_auxiliar, cant_colaboradores, ninos, ninas])

        if total_asistencia > 0 and condicion == "Cerrado":
            errores.append("No se puede registrar como 'Cerrado' si hay asistencia reportada.")
        elif total_asistencia == 0 and condicion == "Abierto":
            errores.append("Si no hay asistencia (niños, niñas o servidores), la condición debe ser 'Cerrado'.")

        return errores
