# Archivo: SolicitudVuelo.py
# Ruta: App\Global_Module\AdminSolicitudes\Vuelos\SolicitudVuelo.py
# Descripción: Módulo para crear Solicitudes de Vuelo
# Autor: Equipo de Desarrollo IGSA
# Fecha: 2025

"""
Módulo de Solicitud de Vuelos.

Este módulo gestiona todo el flujo de creación de solicitudes de vuelo:
1. Obtención de catálogos (centros de costos)
2. Validación de datos básicos
3. Validación de centro de costos
4. Validación de proyecto (si existe)
5. Validación de reglas de negocio
6. Generación de folio
7. Inserción en base de datos
8. Respuesta al cliente
"""

from flask import jsonify, render_template, request
from datetime import datetime, date
import sys
import os

# Importar consultas SQL
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from Consultas_SQL.Global.AdminSolicitudes.Vuelos.SolicitudVueloSQL import SolicitudVueloSQL

# Importar validador de reglas globales
from App.Utilities_module.RulesGlobal import RulesValidator


class SolicitudVueloController:
    """
    Controlador principal para el módulo de Solicitud de Vuelos.
    
    Esta clase maneja toda la lógica de negocio para crear solicitudes de vuelo,
    incluyendo validaciones, generación de folios e inserción en base de datos.
    """
    
    def __init__(self):
        """Inicializa el controlador."""
        self.sql = SolicitudVueloSQL()
        self.rules_validator = RulesValidator()
    
    # ========================================
    # FUNCIÓN ORQUESTADORA PRINCIPAL
    # ========================================
    
    def crear_solicitud_vuelo(self, datos_json):
        """
        🎯 FUNCIÓN ORQUESTADORA PRINCIPAL
        
        Esta función coordina todo el flujo de creación de una solicitud de vuelo.
        
        FLUJO DE EJECUCIÓN:
        1. Creación estructura de datos recibidos
        2. Validar centro de costos (existe y está activo)
        3. Validar proyecto en ERP (si se proporcionó)
        4. Validar reglas de negocio (kilometraje, anticipación, etc.)
        5. Generar folio único
        6. Insertar solicitud principal
        7. Insertar pasajeros
        8. Insertar opciones de vuelo
        9. Retornar respuesta exitosa con folio
        
        Args:
            datos_json (dict): Diccionario con todos los datos de la solicitud (camelCase)
            
        Returns:
            dict: Respuesta estructurada con success, mensaje, datos o errores
        """
        try:
            print("\n" + "="*80)
            print("🎯 INICIANDO CREACIÓN DE SOLICITUD DE VUELO")
            print("="*80)
            
            # ====================================================================
            # PASO 1: VALIDAR ESTRUCTURA DE DATOS
            # ====================================================================
            print("\n📋 PASO 1: Validando estructura de datos...")
            validacion_estructura = self._validar_estructura_datos(datos_json)
            if not validacion_estructura['valido']:
                print(f"❌ Estructura inválida: {validacion_estructura['errores']}")
                return {
                    'success': False,
                    'mensaje': 'Datos incompletos o inválidos',
                    'errores': validacion_estructura['errores']
                }
            print("✅ Estructura válida")
            
            # ====================================================================
            # PASO 2: VALIDAR CENTRO DE COSTOS
            # ====================================================================
            print("\n💰 PASO 2: Validando centro de costos...")
            centro_costos = datos_json.get('centroCostos')
            resultado_cc = self.sql.validar_centro_costos(centro_costos)
            
            if not resultado_cc['existe']:
                print(f"❌ Centro de costos inválido: {resultado_cc['error']}")
                return {
                    'success': False,
                    'mensaje': resultado_cc['error']
                }
            print(f"✅ Centro de costos válido: {resultado_cc['datos']['NombreCeCo']}")
            
            # ====================================================================
            # PASO 3: VALIDAR PROYECTO (SI EXISTE)
            # ====================================================================
            proyecto = datos_json.get('proyecto')
            vigencia_proyecto = None
            
            if proyecto and proyecto.strip():
                print(f"\n📁 PASO 3: Validando proyecto '{proyecto}' en ERP...")
                resultado_proyecto = self.sql.validar_proyecto_erp(proyecto)
                
                if not resultado_proyecto['existe']:
                    print(f"❌ Proyecto inválido: {resultado_proyecto['error']}")
                    return {
                        'success': False,
                        'mensaje': resultado_proyecto['error']
                    }
                
                if not resultado_proyecto['vigente']:
                    print("❌ Proyecto no vigente")
                    return {
                        'success': False,
                        'mensaje': f'El proyecto "{proyecto}" no está vigente en el ERP'
                    }
                
                print(f"✅ Proyecto válido: {resultado_proyecto['datos']['NombreProyecto']}")
            else:
                print("\nℹ️  PASO 3: Sin proyecto especificado (opcional)")
            
            # ====================================================================
            # PASO 4: VALIDAR REGLAS DE NEGOCIO
            # ====================================================================
            print("\n📏 PASO 4: Validando reglas de negocio...")
            validacion_reglas = self._validar_reglas_negocio(datos_json)
            if not validacion_reglas['valido']:
                print(f"❌ Reglas de negocio no cumplidas: {validacion_reglas['errores']}")
                return {
                    'success': False,
                    'mensaje': 'No se cumplen las reglas de negocio',
                    'errores': validacion_reglas['errores']
                }
            print("✅ Todas las reglas de negocio cumplidas")
            
            # ====================================================================
            # PASO 5: PREPARAR DATOS PARA INSERCIÓN
            # ====================================================================
            print("\n📦 PASO 5: Preparando datos para inserción...")
            datos_solicitud = self._preparar_datos_solicitud(datos_json)
            print("✅ Datos preparados")
            
            # ====================================================================
            # PASO 6: INSERTAR SOLICITUD PRINCIPAL
            # ====================================================================
            print("\n💾 PASO 6: Insertando solicitud principal...")
            resultado_insert = self.sql.insertar_solicitud(datos_solicitud)
            
            if not resultado_insert['success']:
                print(f"❌ Error al insertar solicitud: {resultado_insert['error']}")
                return {
                    'success': False,
                    'mensaje': 'Error al guardar la solicitud',
                    'error': resultado_insert['error']
                }
            
            flight_request_id = resultado_insert['flight_request_id']
            folio = resultado_insert['folio']
            print(f"✅ Solicitud insertada - ID: {flight_request_id}, Folio: {folio}")
            
            # ====================================================================
            # PASO 7: INSERTAR PASAJEROS
            # ====================================================================
            print("\n👥 PASO 7: Insertando pasajeros...")
            pasajeros = datos_json.get('pasajeros', [])
            
            if pasajeros:
                pasajeros_preparados = self._preparar_pasajeros(pasajeros)
                resultado_pasajeros = self.sql.insertar_pasajeros(flight_request_id, pasajeros_preparados)
                
                if not resultado_pasajeros['success']:
                    print(f"❌ Error al insertar pasajeros: {resultado_pasajeros['error']}")
                    return {
                        'success': False,
                        'mensaje': 'Error al guardar pasajeros',
                        'error': resultado_pasajeros['error']
                    }
                print(f"✅ Pasajeros insertados: {resultado_pasajeros['insertados']}")
            else:
                print("ℹ️  Sin pasajeros para insertar")
            
            # ====================================================================
            # PASO 8: INSERTAR OPCIONES DE VUELO
            # ====================================================================
            print("\n✈️  PASO 8: Insertando opciones de vuelo...")
            vuelos = datos_json.get('vuelos', [])
            
            if vuelos:
                vuelos_preparados = self._preparar_vuelos(vuelos)
                resultado_vuelos = self.sql.insertar_vuelos(flight_request_id, vuelos_preparados)
                
                if not resultado_vuelos['success']:
                    print(f"❌ Error al insertar vuelos: {resultado_vuelos['error']}")
                    return {
                        'success': False,
                        'mensaje': 'Error al guardar opciones de vuelo',
                        'error': resultado_vuelos['error']
                    }
                print(f"✅ Vuelos insertados: {resultado_vuelos['insertados']}")
            else:
                print("ℹ️  Sin opciones de vuelo para insertar")
            
            # ====================================================================
            # PASO 9: RETORNAR RESPUESTA EXITOSA
            # ====================================================================
            print("\n" + "="*80)
            print("🎉 SOLICITUD DE VUELO CREADA EXITOSAMENTE")
            print(f"📄 Folio: {folio}")
            print(f"🆔 ID: {flight_request_id}")
            print("="*80 + "\n")
            
            return {
                'success': True,
                'mensaje': 'Solicitud de vuelo creada exitosamente',
                'folio': folio,
                'flightRequestId': flight_request_id,
                'datos': {
                    'pasajerosInsertados': len(pasajeros),
                    'vuelosInsertados': len(vuelos)
                }
            }
            
        except Exception as e:
            print(f"\n❌ ERROR CRÍTICO: {str(e)}")
            import traceback
            traceback.print_exc()
            
            return {
                'success': False,
                'mensaje': 'Error al procesar la solicitud',
                'error': str(e)
            }
    
    # ========================================
    # FUNCIONES DE VALIDACIÓN
    # ========================================
    
    def _validar_estructura_datos(self, datos):
        """
        Valida que los datos recibidos tengan la estructura mínima requerida.
        
        Args:
            datos (dict): Datos del frontend en camelCase
            
        Returns:
            dict: {'valido': bool, 'errores': list}
        """
        errores = []
        
        # ====================================================================
        # CAMPOS OBLIGATORIOS DE NIVEL SUPERIOR
        # ====================================================================
        campos_obligatorios = ['centroCostos', 'descripcion', 'origen', 'destino', 'fechas', 'pasajeros']
        
        for campo in campos_obligatorios:
            if campo not in datos or not datos[campo]:
                errores.append(f'Campo obligatorio faltante: {campo}')
        
        # ====================================================================
        # VALIDAR ESTRUCTURA DE FECHAS
        # ====================================================================
        if 'fechas' in datos:
            fechas = datos['fechas']
            
            # Campos obligatorios de fechas
            campos_fecha_obligatorios = ['salida', 'horaSalida']
            
            for campo in campos_fecha_obligatorios:
                if campo not in fechas or not fechas[campo]:
                    errores.append(f'Campo de fecha obligatorio faltante: {campo}')
            
            # Campos opcionales de fechas (no se validan como obligatorios)
            # - regreso (opcional)
            # - horaRegreso (opcional)
        
        # ====================================================================
        # VALIDAR QUE HAYA AL MENOS UN PASAJERO
        # ====================================================================
        if 'pasajeros' in datos:
            if not isinstance(datos['pasajeros'], list) or len(datos['pasajeros']) == 0:
                errores.append('Debe haber al menos un pasajero')
        
        # ====================================================================
        # VALIDAR QUE HAYA AL MENOS 3 VUELOS
        # ====================================================================
        if 'vuelos' in datos:
            if not isinstance(datos['vuelos'], list):
                errores.append('El campo vuelos debe ser una lista')
            elif len(datos['vuelos']) < 3:
                errores.append(f'Se requieren mínimo 3 opciones de vuelo (recibidas: {len(datos["vuelos"])})')
        else:
            errores.append('El campo vuelos es obligatorio')
        
        return {
            'valido': len(errores) == 0,
            'errores': errores
        }
    
    def _validar_reglas_negocio(self, datos):
        """
        Valida las reglas de negocio específicas para solicitudes de vuelo.
        
        Por ejemplo:
        - Kilometraje mínimo para requerir vuelo
        - Anticipación mínima
        - Equipaje máximo
        
        Args:
            datos (dict): Datos de la solicitud en camelCase
            
        Returns:
            dict: {'valido': bool, 'errores': list}
        """
        errores = []
        
        # ====================================================================
        # VALIDAR FECHAS
        # ====================================================================
        try:
            fechas = datos.get('fechas', {})
            
            # Obtener fecha de salida (OBLIGATORIA)
            fecha_salida_str = fechas.get('salida')
            if not fecha_salida_str:
                errores.append('La fecha de salida es obligatoria')
                return {'valido': False, 'errores': errores}
            
            fecha_salida = datetime.strptime(fecha_salida_str, '%Y-%m-%d').date()
            
            # Obtener fecha de regreso (OPCIONAL)
            fecha_regreso_str = fechas.get('regreso')
            fecha_regreso = None
            
            if fecha_regreso_str:  # Solo validar si se proporcionó
                fecha_regreso = datetime.strptime(fecha_regreso_str, '%Y-%m-%d').date()
                
                # Validar que regreso sea posterior a salida
                if fecha_regreso < fecha_salida:
                    errores.append('La fecha de regreso no puede ser anterior a la fecha de salida')
            
            # Validar que la fecha de salida no sea en el pasado
            hoy = date.today()
            if fecha_salida < hoy:
                errores.append('La fecha de salida no puede ser en el pasado')
            
            # ====================================================================
            # VALIDAR ANTICIPACIÓN MÍNIMA (ADVERTENCIA, NO ERROR)
            # ====================================================================
            dias_anticipacion = (fecha_salida - hoy).days
            
            if dias_anticipacion < 7:
                # ⚠️ ADVERTENCIA: Esto es solo informativo, no bloquea el proceso
                print(f"⚠️  ADVERTENCIA: Anticipación de solo {dias_anticipacion} días (recomendado: 7+ días)")
                # NO agregamos a errores, solo advertimos
        
        except ValueError as e:
            errores.append(f'Error en formato de fecha: {str(e)}')
        except Exception as e:
            errores.append(f'Error al validar fechas: {str(e)}')
        
        # ====================================================================
        # VALIDAR KILOMETRAJE (OPCIONAL - solo si hay distancias)
        # ====================================================================
        distancias = datos.get('distancias', {})
        if distancias:
            try:
                kilometraje_str = distancias.get('carretera', '0 km')
                if 'km' in kilometraje_str:
                    kilometraje = float(kilometraje_str.replace(' km', '').replace(',', ''))
                    
                    # Advertencia si el kilometraje es muy bajo
                    if kilometraje < 100:
                        print(f"⚠️  ADVERTENCIA: Kilometraje muy bajo ({kilometraje} km)")
                        # NO agregamos a errores, solo advertimos
            except Exception as e:
                print(f"⚠️  No se pudo validar kilometraje: {str(e)}")
        
        # ====================================================================
        # VALIDAR PASAJEROS
        # ====================================================================
        pasajeros = datos.get('pasajeros', [])
        if len(pasajeros) == 0:
            errores.append('Debe haber al menos un pasajero')
        
        # ====================================================================
        # VALIDAR VUELOS (MÍNIMO 3)
        # ====================================================================
        vuelos = datos.get('vuelos', [])
        if len(vuelos) < 3:
            errores.append(f'Se requieren mínimo 3 opciones de vuelo (actual: {len(vuelos)})')
        
        return {
            'valido': len(errores) == 0,
            'errores': errores
        }
    
    # ========================================
    # FUNCIONES DE PREPARACIÓN DE DATOS
    # ========================================
    
    def _preparar_datos_solicitud(self, datos):
        """
        Prepara los datos de la solicitud principal para inserción en BD.
        Convierte de camelCase (frontend) a formato SQL.
        
        Args:
            datos (dict): Datos del frontend en camelCase
            
        Returns:
            dict: Datos preparados para SQL
        """
        fechas = datos.get('fechas', {})
        
        # Preparar origen (puede ser un objeto complejo)
        origen_obj = datos.get('origen', {})
        origen_str = origen_obj.get('name', '') if isinstance(origen_obj, dict) else str(origen_obj)
        
        # Preparar destino (puede ser un objeto complejo)
        destino_obj = datos.get('destino', {})
        destino_str = destino_obj.get('name', '') if isinstance(destino_obj, dict) else str(destino_obj)
        
        # Preparar kilometraje
        distancias = datos.get('distancias', {})
        kilometraje_str = distancias.get('carretera', '0 km')
        kilometraje = float(kilometraje_str.replace(' km', '').replace(',', '')) if 'km' in kilometraje_str else 0.0
        
        return {
            'CentroCostos': datos.get('centroCostos'),
            'Proyecto': datos.get('proyecto'),
            'VigenciaProyecto': None,  # Puede obtenerse del ERP si es necesario
            'DescripcionMotivo': datos.get('descripcion'),
            'Destino': destino_str,
            'KilometrajeViaje': kilometraje,
            'FechaSalida': fechas.get('salida'),
            'FechaRegreso': fechas.get('regreso'),
            'HoraAproxSalida': fechas.get('horaSalida'),
            'HoraAproxRegreso': fechas.get('horaRegreso'),
            'CreatedBy': 'SYSTEM'  # Cambiar cuando tengamos usuario en sesión
        }
    
    def _preparar_pasajeros(self, lista_pasajeros):
        """
        Prepara los datos de pasajeros para inserción en BD.
        Convierte de camelCase (frontend) a formato SQL.
        
        Args:
            lista_pasajeros (list): Lista de pasajeros del frontend en camelCase
            
        Returns:
            list: Lista de pasajeros preparados para SQL
        """
        pasajeros_preparados = []
        
        for idx, pasajero in enumerate(lista_pasajeros, start=1):
            pasajero_preparado = {
                'NombrePasajero': pasajero.get('nombreCompleto'),
                'NumeroEmpleado': pasajero.get('numeroEmpleado'),
                'Empresa': pasajero.get('empresa'),
                'FechaNacimiento': pasajero.get('fechaNacimiento'),
                'TipoIdentificacion': pasajero.get('identificacionOficial', 'INE'),  # Valor por defecto
                'NumeroIdentificacion': pasajero.get('identificacionOficial'),
                'VigenciaIdentificacion': None,  # No proporcionado por el frontend
                'Nacionalidad': pasajero.get('nacionalidad'),
                'CorreoElectronico': pasajero.get('email'),
                'Telefono': pasajero.get('telefono'),
                'NumeroPasaporte': None,  # No proporcionado por el frontend
                'VigenciaPasaporte': None,  # No proporcionado por el frontend
                'TieneVisaVigente': False,  # No proporcionado por el frontend
                'TipoVisa': None,  # No proporcionado por el frontend
                'VigenciaVisa': None,  # No proporcionado por el frontend
                'PaisVisa': None,  # No proporcionado por el frontend
                'RequiereEquipajeExtra': pasajero.get('requiereEquipaje', False),
                'CantidadMaletas': pasajero.get('cantidadMaletas'),
                'ExcesoEquipajeKg': pasajero.get('excesoEquipaje'),
                'OrdenPasajero': idx
            }
            pasajeros_preparados.append(pasajero_preparado)
        
        return pasajeros_preparados
    
    def _preparar_vuelos(self, lista_vuelos):
        """
        Prepara los datos de opciones de vuelo para inserción en BD.
        Convierte de camelCase (frontend) a formato SQL.
        
        Args:
            lista_vuelos (list): Lista de vuelos del frontend en camelCase
            
        Returns:
            list: Lista de vuelos preparados para SQL
        """
        vuelos_preparados = []
        
        for idx, vuelo in enumerate(lista_vuelos, start=1):
            vuelo_preparado = {
                'Aerolinea': vuelo.get('aerolinea'),
                'NumeroVuelo': vuelo.get('numeroVuelo'),
                'FechaVuelo': None,  # No proporcionado por el frontend actual
                'CostoTotal': float(vuelo.get('costoTotal', 0)),
                'OrdenPreferencia': idx
            }
            vuelos_preparados.append(vuelo_preparado)
        
        return vuelos_preparados


# ========================================
# FUNCIÓN PARA REGISTRAR RUTAS EN FLASK
# ========================================

def ejecutar_solicitud_vuelo(app, mail):
    """
    Registra las rutas de Flask para el módulo de Solicitud de Vuelos.
    
    Args:
        app: Instancia de Flask
        mail: Instancia de Flask-Mail (para envío de correos si es necesario)
    """
    
    # Instancia global del controlador
    controller = SolicitudVueloController()
    
    # ========================================
    # RUTA: RENDERIZAR HTML
    # ========================================
    
    @app.route('/Global/AdminSolicitudes/Vuelos/SolicitudVuelo', methods=['GET'])
    def solicitud_vuelo_page():
        """
        Renderiza la página HTML del módulo de Solicitud de Vuelos.
        
        Returns:
            HTML renderizado
        """
        return render_template('Global/AdminSolicitudes/Vuelos/SolicitudVuelo.html')
    
    # ========================================
    # RUTA: OBTENER CENTROS DE COSTOS
    # ========================================
    
    @app.route('/Global/AdminSolicitudes/Vuelos/SolicitudVuelo/centros-costos', methods=['GET'])
    def obtener_centros_costos():
        """
        Endpoint para obtener todos los centros de costos activos.
        
        Returns:
            JSON con lista de centros de costos
        """
        try:
            print("\n📥 GET /centros-costos - Obteniendo centros de costos...")
            
            resultado = controller.sql.obtener_centros_costos()
            
            if resultado['success']:
                print(f"✅ Centros de costos obtenidos: {len(resultado['centros'])}")
                return jsonify({
                    'success': True,
                    'centros': resultado['centros']
                }), 200
            else:
                print(f"❌ Error: {resultado['error']}")
                return jsonify({
                    'success': False,
                    'mensaje': 'Error al obtener centros de costos',
                    'error': resultado['error']
                }), 500
                
        except Exception as e:
            print(f"❌ ERROR EN ENDPOINT: {str(e)}")
            import traceback
            traceback.print_exc()
            
            return jsonify({
                'success': False,
                'mensaje': 'Error al obtener centros de costos',
                'error': str(e)
            }), 500
    
    # ========================================
    # RUTA: TEST DE CONEXIÓN
    # ========================================
    
    @app.route('/Global/AdminSolicitudes/Vuelos/SolicitudVuelo/test', methods=['GET'])
    def test_solicitud_vuelo():
        """
        Ruta de prueba para verificar que el backend funciona correctamente.
        
        Returns:
            JSON con mensaje de éxito
        """
        return jsonify({
            'success': True,
            'mensaje': '¡Backend de Solicitud de Vuelos funcionando correctamente!',
            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            'modulo': 'Solicitud de Vuelos',
            'version': '1.0',
            'axios_ready': True
        })
    
    # ========================================
    # RUTA: CREAR SOLICITUD
    # ========================================
    
    @app.route('/Global/AdminSolicitudes/Vuelos/SolicitudVuelo/crear', methods=['POST'])
    def crear_solicitud_vuelo_endpoint():
        """
        Endpoint para crear una nueva solicitud de vuelo.
        
        Recibe JSON con toda la información de la solicitud en camelCase y ejecuta
        el flujo completo de validación e inserción.
        
        Returns:
            JSON con resultado de la operación
        """
        try:
            print("\n" + "="*80)
            print("📥 RECIBIENDO PETICIÓN POST /crear")
            print("="*80)
            
            # Obtener datos del request
            datos_json = request.get_json()
            
            if not datos_json:
                print("❌ No se recibieron datos en el request")
                return jsonify({
                    'success': False,
                    'mensaje': 'No se recibieron datos'
                }), 400
            
            print(f"✅ Datos recibidos correctamente")
            print(f"📊 Centro de Costos: {datos_json.get('centroCostos')}")
            print(f"📊 Proyecto: {datos_json.get('proyecto', 'N/A')}")
            print(f"📊 Pasajeros: {len(datos_json.get('pasajeros', []))}")
            print(f"📊 Vuelos: {len(datos_json.get('vuelos', []))}")
            
            # Ejecutar función orquestadora
            resultado = controller.crear_solicitud_vuelo(datos_json)
            
            # Determinar código de respuesta HTTP
            status_code = 200 if resultado['success'] else 400
            
            return jsonify(resultado), status_code
            
        except Exception as e:
            print(f"\n❌ ERROR EN ENDPOINT: {str(e)}")
            import traceback
            traceback.print_exc()
            
            return jsonify({
                'success': False,
                'mensaje': 'Error al procesar la solicitud',
                'error': str(e)
            }), 500
    
    print("✅ Rutas de Solicitud de Vuelo registradas correctamente")