# Archivo: CotizEspSolicitudSQL.py
# Ruta: src\Consultas_SQL\Ventas\Cotiz\CotizEspSolicitudSQL.py
# Descripción: Funciones SQL para el módulo de cotizaciones especiales
# Autor: Equipo de Desarrollo IGSA
# Fecha: 2025

import pyodbc
import logging
from datetime import datetime
from typing import Dict, List, Optional, Union

# Configurar logging
logger = logging.getLogger('cotizaciones_especiales_sql')

from config import Productivo, ENVIRONMENT
from Consultas_SQL.conexion import get_connection

def buscar_oportunidad_crm(opportunity_number: float) -> Optional[Dict]:
    """
    Busca una oportunidad en la tabla Q_OpportunityCRM
    
    Args:
        opportunity_number (float): Número de oportunidad del CRM
    
    Returns:
        Dict: Diccionario con los datos de la oportunidad si se encuentra, None si no existe
        
    Raises:
        ConnectionError: Si no se puede establecer conexión con la base de datos
        Exception: Para otros errores de base de datos
    """
    
    query = """
        SELECT TOP 1
            Q_CRM.CRM_OpportunityNumber,
            Q_CRM.CRM_Version,
            Q_CRM.CRM_ContactID,
            Q_CRM.CRM_ContactName,
            Q_CRM.CRM_ContactType,
            Q_CRM.CRM_AssignedSalesperson,
            Q_CRM.CRM_ContactAdress,
            Q_CRM.CRM_ContactColonia,
            Q_CRM.CRM_ContactCity,
            Q_CRM.CRM_ContactNumber,
            Q_CRM.CRM_ContactCountry,
            Q_CRM.CRM_ContactLegalIdentifier,
            Q_CRM.CRM_ContactZip,
            Q_CRM.CRM_ContactState,
            Q_CRM.CRM_ContactEmail,
            Q_CRM.Status,
            Q_CRM.CreatedAt,
            Q_CRM.CreatedBy,
            Q_CRM.Active
        FROM Q_CRM
        WHERE Q_CRM.CRM_OpportunityNumber = ?
            AND Q_CRM.Active = 1
        ORDER BY Q_CRM.CRM_Version DESC
    """

    conn = None
    try:
        # Obtener conexión
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (opportunity_number,))
            
            # Obtener los nombres de las columnas
            columns = [column[0] for column in cursor.description]
            
            # Obtener la tupla de resultados
            row = cursor.fetchone()
            
            if row:
                # Convertir la tupla en diccionario
                result = dict(zip(columns, row))
                
                # Log de la búsqueda exitosa
                logger.info(f"Oportunidad {opportunity_number} encontrada exitosamente")
                
                # Convertir datetime a string para JSON serialization
                if 'CreatedAt' in result and result['CreatedAt']:
                    result['CreatedAt'] = result['CreatedAt'].isoformat()
                
                return result
            else:
                # Log cuando no se encuentra la oportunidad
                logger.warning(f"Oportunidad {opportunity_number} no encontrada en la base de datos")
                return None
            
    except pyodbc.Error as e:
        # Error específico de base de datos
        error_msg = f"Error de base de datos al buscar oportunidad {opportunity_number}: {str(e)}"
        logger.error(error_msg)
        raise Exception(error_msg)
        
    except ConnectionError as e:
        # Error de conexión
        error_msg = f"Error de conexión al buscar oportunidad {opportunity_number}: {str(e)}"
        logger.error(error_msg)
        raise ConnectionError(error_msg)
        
    except Exception as e:
        # Cualquier otro error
        error_msg = f"Error inesperado al buscar oportunidad {opportunity_number}: {str(e)}"
        logger.error(error_msg)
        raise Exception(error_msg)
        
    finally:
        # Asegurar que la conexión se cierre
        if conn:
            try:
                conn.close()
            except:
                pass

def verificar_oportunidad_existente(opportunity_number: str) -> Optional[Dict]:
    """
    Verifica si ya existe una oportunidad y retorna la versión más alta
    También valida que no esté cerrada/vendida o en proceso activo
    
    Args:
        opportunity_number (str): Número de oportunidad del CRM
    
    Returns:
        Dict: Datos de la oportunidad existente con versión más alta, None si no existe
        
    Raises:
        Exception: Si la oportunidad está cerrada/vendida o en proceso activo
    """
    
    query = """
        SELECT TOP 1
            Q_OpportunityCRM.CRM_OpportunityNumber,
            Q_OpportunityCRM.Version,
            Q_OpportunityCRM.CRM_OpportunityID,
            Q_OpportunityCRM.Status,
            Q_OpportunityCRM.Active
        FROM Q_OpportunityCRM
        WHERE Q_OpportunityCRM.CRM_OpportunityNumber = ?
            AND Q_OpportunityCRM.Active = 1
        ORDER BY Q_OpportunityCRM.Version DESC
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (opportunity_number,))
            
            columns = [column[0] for column in cursor.description]
            row = cursor.fetchone()
            
            if row:
                result = dict(zip(columns, row))
                
                # 🚨 VALIDACIONES DE STATUS
                status = result.get('Status', '').upper()
                
                # 1. Validar si está cerrada o vendida
                if status in ['VENDIDO', 'VENDIDA', 'ATENDIDO']:
                    logger.warning(f"Oportunidad {opportunity_number} tiene status {status} - proceso terminado")
                    raise ValueError(f"OPPORTUNITY_CLOSED:{status}")
                
                # 2. Validar si está en proceso activo (NUEVA VALIDACIÓN)
                if status in ['PENDIENTE', 'EN DESARROLLO', 'EN APROBACION', 'EN_DESARROLLO', 'EN_APROBACION']:
                    logger.warning(f"Oportunidad {opportunity_number} tiene status {status} - proceso activo")
                    raise ValueError(f"OPPORTUNITY_IN_PROCESS:{status}")
                
                logger.info(f"Oportunidad {opportunity_number} encontrada con versión {result['Version']} y status {status}")
                return result
            else:
                logger.info(f"Oportunidad {opportunity_number} no encontrada")
                return None
            
    except ValueError as e:
        # Re-lanzar errores de validación para que los maneje el proceso principal
        raise e
        
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al verificar oportunidad {opportunity_number}: {str(e)}"
        logger.error(error_msg)
        raise Exception(error_msg)
        
    except Exception as e:
        error_msg = f"Error inesperado al verificar oportunidad {opportunity_number}: {str(e)}"
        logger.error(error_msg)
        raise Exception(error_msg)
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def crear_actualizar_oportunidad(datos_oportunidad: Dict, user_id: int, version: int) -> Dict:
    """
    Crea o actualiza un registro en Q_OpportunityCRM
    
    Args:
        datos_oportunidad (Dict): Datos de la oportunidad
        user_id (int): ID del usuario
        version (int): Versión de la oportunidad
    
    Returns:
        Dict: Resultado de la operación
    """
    
    # Obtener el nombre completo del usuario para CreatedBy/UpdatedBy
    user_name = obtener_nombre_usuario(user_id)
    
    query = """
        INSERT INTO Q_OpportunityCRM (
            CRM_OpportunityNumber,
            Version,
            UserID,
            CRM_ContactID,
            CRM_ContactName,
            CRM_ContactType,
            CRM_AssignedSalesperson,
            CRM_ContactAdress,
            CRM_ContactColonia,
            CRM_ContactCity,
            CRM_ContactNumber,
            CRM_ContactCountry,
            CRM_ContactLegalIdentifier,
            CRM_ContactZip,
            CRM_ContactState,
            CRM_ContactEmail,
            Status,
            CreatedBy,
            UpdatedBy
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (
                datos_oportunidad.get('CRM_OpportunityNumber', ''),
                version,
                user_id,
                datos_oportunidad.get('CRM_ContactID', ''),
                datos_oportunidad.get('CRM_ContactName', ''),
                datos_oportunidad.get('CRM_ContactType', ''),
                datos_oportunidad.get('CRM_AssignedSalesperson', ''),
                datos_oportunidad.get('CRM_ContactAdress', ''),
                datos_oportunidad.get('CRM_ContactColonia', ''),
                datos_oportunidad.get('CRM_ContactCity', ''),
                datos_oportunidad.get('CRM_ContactNumber', ''),
                datos_oportunidad.get('CRM_ContactCountry', ''),
                datos_oportunidad.get('CRM_ContactLegalIdentifier', ''),
                datos_oportunidad.get('CRM_ContactZip', ''),
                datos_oportunidad.get('CRM_ContactState', ''),
                datos_oportunidad.get('CRM_ContactEmail', ''),
                'PENDIENTE',  # Status inicial
                user_name,
                user_name
            ))
            
            conn.commit()
            opportunity_id = f"{datos_oportunidad.get('CRM_OpportunityNumber')}-{version}"
            
            logger.info(f"Oportunidad {opportunity_id} creada exitosamente")
            return {
                'success': True,
                'opportunity_id': opportunity_id
            }
            
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al crear oportunidad: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error al guardar los datos de la oportunidad',
            'code': 'DATABASE_ERROR',
            'alert_type': 'error',
            'status_code': 500
        }
        
    except Exception as e:
        error_msg = f"Error inesperado al crear oportunidad: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error interno al procesar la oportunidad',
            'code': 'INTERNAL_ERROR',
            'alert_type': 'error',
            'status_code': 500
        }
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def crear_formulario_head(opportunity_id: str, quotation_type_id: str, user_id: int) -> Dict:
    """
    Crea un registro en Q_SpQ_FormsHead
    
    Args:
        opportunity_id (str): ID de la oportunidad
        quotation_type_id (str): ID del tipo de cotización
        user_id (int): ID del usuario
    
    Returns:
        Dict: Resultado de la operación
    """
    
    user_name = obtener_nombre_usuario(user_id)
    
    query = """
        INSERT INTO Q_SpQ_FormsHead (
            CRM_OpportunityID,
            QuotationTypeID,
            UserID,
            CreatedBy,
            UpdatedBy
        ) VALUES (?, ?, ?, ?, ?)
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (
                opportunity_id,
                quotation_type_id,
                user_id,
                user_name,
                user_name
            ))
            
            conn.commit()
            form_id = f"{opportunity_id}-{quotation_type_id}"
            
            logger.info(f"FormHead {form_id} creado exitosamente")
            return {
                'success': True,
                'form_id': form_id
            }
            
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al crear FormHead: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error al crear registro de formulario',
            'code': 'DATABASE_ERROR',
            'alert_type': 'error'
        }
        
    except Exception as e:
        error_msg = f"Error inesperado al crear FormHead: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error interno al crear formulario',
            'code': 'INTERNAL_ERROR',
            'alert_type': 'error'
        }
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def crear_formulario_details(form_id: str, preguntas: List[Dict]) -> Dict:
    """
    Crea múltiples registros en Q_SpQ_FormsDetail
    
    Args:
        form_id (str): ID del formulario
        preguntas (List[Dict]): Lista de preguntas y respuestas
    
    Returns:
        Dict: Resultado de la operación
    """
    
    # 🔍 DEBUG: Ver qué datos estamos recibiendo
    print(f"🔍 DEBUG - FormID: {form_id}")
    print(f"🔍 DEBUG - Total preguntas: {len(preguntas)}")
    for i, pregunta in enumerate(preguntas):
        print(f"🔍 DEBUG - Pregunta {i+1}: {pregunta}")
    
    query = """
        INSERT INTO Q_SpQ_FormsDetail (
            FormID,
            FormLine,
            Question,
            TypeQuestion,
            Answer
        ) VALUES (?, ?, ?, ?, ?)
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        registros_creados = 0
        
        with conn:
            cursor = conn.cursor()
            
            # Usar enumerate para tener el índice correcto de cada pregunta
            for index, pregunta in enumerate(preguntas, start=1):
                form_line = str(index).zfill(3)  # 001, 002, 003, etc.
                
                # 🔍 DEBUG: Ver qué valores vamos a insertar
                print(f"🔍 DEBUG - Insertando registro {index}:")
                print(f"    FormID: {form_id}")
                print(f"    FormLine: {form_line}")
                print(f"    Question: {pregunta['pregunta']}")
                print(f"    TypeQuestion: {pregunta['tipoElemento']}")
                print(f"    Answer: {pregunta['respuesta']}")
                print(f"    Expected FormLineID: {form_id}-{form_line}")
                
                cursor.execute(query, (
                    form_id,
                    form_line,
                    pregunta['pregunta'],
                    pregunta['tipoElemento'],
                    pregunta['respuesta']
                ))
                registros_creados += 1
                print(f"✅ DEBUG - Registro {index} insertado exitosamente")
            
            conn.commit()
            
            logger.info(f"Creados {registros_creados} detalles para FormID {form_id}")
            return {
                'success': True,
                'records_created': registros_creados
            }
            
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al crear FormDetails: {str(e)}"
        logger.error(error_msg)
        print(f"❌ DEBUG - Error SQL: {error_msg}")
        return {
            'success': False,
            'message': 'Error al guardar detalles del formulario',
            'code': 'DATABASE_ERROR',
            'alert_type': 'error'
        }
        
    except Exception as e:
        error_msg = f"Error inesperado al crear FormDetails: {str(e)}"
        logger.error(error_msg)
        print(f"❌ DEBUG - Error general: {error_msg}")
        return {
            'success': False,
            'message': 'Error interno al guardar detalles',
            'code': 'INTERNAL_ERROR',
            'alert_type': 'error'
        }
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def crear_tarea_cotizacion(seller_user_id: int, form_id: str, opportunity_id: str, user_name: str) -> Dict:
    """
    Crea una tarea en Q_SpQ_QuotationTasks
    
    Args:
        seller_user_id (int): ID del vendedor
        form_id (str): ID del formulario
        opportunity_id (str): ID de la oportunidad
        user_name (str): Nombre del usuario que crea la tarea
    
    Returns:
        Dict: Resultado de la operación
    """
    
    query = """
        INSERT INTO Q_SpQ_QuotationTasks (
            SellerUserID,
            FormID,
            Status,
            StatusDate,
            CRM_OpportunityID,
            UpdatedBy
        ) VALUES (?, ?, ?, CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Central America Standard Time' AS DATETIME), ?, ?)
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (
                seller_user_id,
                form_id,
                'Por Asignar',
                opportunity_id,
                user_name
            ))
            
            # Obtener el ID de la tarea recién creada
            cursor.execute("SELECT @@IDENTITY AS TaskID")
            task_id = cursor.fetchone()[0]
            
            conn.commit()
            
            logger.info(f"Tarea {task_id} creada para FormID {form_id}")
            return {
                'success': True,
                'task_id': task_id
            }
            
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al crear tarea: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error al crear tarea de cotización',
            'code': 'DATABASE_ERROR',
            'alert_type': 'error'
        }
        
    except Exception as e:
        error_msg = f"Error inesperado al crear tarea: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error interno al crear tarea',
            'code': 'INTERNAL_ERROR',
            'alert_type': 'error'
        }
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def obtener_nombre_usuario(user_id: int) -> str:
    """
    Obtiene el nombre completo del usuario desde la tabla Profiles
    
    Args:
        user_id (int): ID del usuario
    
    Returns:
        str: Nombre completo del usuario
    """
    
    query = """
        SELECT 
            CONCAT(
                COALESCE(Profiles.FirstName, ''), ' ',
                COALESCE(Profiles.LastName, ''), ' ',
                COALESCE(Profiles.SecondLastName, '')
            ) AS FullName
        FROM Profiles
        WHERE Profiles.UserID = ?
            AND Profiles.UserID IN (
                SELECT Users.UserID 
                FROM Users 
                WHERE Users.Status = 'ACTIVO'
            )
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            return f"Usuario_{user_id}"  # Fallback
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (user_id,))
            
            row = cursor.fetchone()
            if row:
                # Limpiar espacios extra del nombre concatenado
                full_name = ' '.join(row[0].split())
                return full_name if full_name.strip() else f"Usuario_{user_id}"
            else:
                return f"Usuario_{user_id}"
            
    except Exception as e:
        logger.warning(f"Error al obtener nombre de usuario {user_id}: {str(e)}")
        return f"Usuario_{user_id}"
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def debe_crear_tarea(quotation_type_id: str) -> bool:
    """
    Consulta si un tipo de formulario debe crear tarea en Q_SpQ_QuotationTasks
    
    Args:
        quotation_type_id (str): ID del tipo de cotización
    
    Returns:
        bool: True si debe crear tarea, False si no
    """
    
    query = """
        SELECT Q_QuotationType.CreateTask
        FROM Q_QuotationType
        WHERE Q_QuotationType.QuotationTypeID = ?
            AND Q_QuotationType.Active = 1
    """
    
    conn = None
    try:
        conn = get_connection()
        if not conn:
            logger.warning(f"No se pudo conectar para verificar CreateTask de {quotation_type_id}")
            return True  # Default: crear tarea si no hay conexión
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (quotation_type_id,))
            
            row = cursor.fetchone()
            if row:
                create_task = bool(row[0])
                logger.info(f"QuotationType {quotation_type_id} - CreateTask: {create_task}")
                return create_task
            else:
                logger.warning(f"QuotationType {quotation_type_id} no encontrado")
                return True  # Default: crear tarea si no se encuentra
            
    except pyodbc.Error as e:
        logger.error(f"Error de BD al consultar CreateTask para {quotation_type_id}: {str(e)}")
        return True  # Default: crear tarea en caso de error
        
    except Exception as e:
        logger.error(f"Error inesperado al consultar CreateTask para {quotation_type_id}: {str(e)}")
        return True  # Default: crear tarea en caso de error
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass            

def obtener_quotation_type_id(tipo_formulario: str) -> str:
    """
    Mapea el tipo de formulario al QuotationTypeID correspondiente
    
    Args:
        tipo_formulario (str): Tipo de formulario del frontend
    
    Returns:
        str: QuotationTypeID correspondiente
    """
    
    mapeo_tipos = {
        'General': 'FormGral',
        'SolicVis': 'FormSolicVis', # ← SOLVIS
        'PTTransNac': 'FormPTTransNac', # ← FPETABTRANS
        'PTTransUL': 'FormPTTransUL', # ← FPETABTRANSUL
        'Aire': 'FormAire', # ← Aire
        'SistCritUPS': 'FormSistCritUPS', # ← SistCritUPS
        'Bess': 'FormBess', # Step
        'Solar': 'FormSolar', # ← SOLARR
        'EnergyCogeneration': 'FormEneCog', # ← EnergyCogeneration
        'Imess': 'FormImess', # ← Imess
        'ProyecEsp': 'FormProyecEsp' # ← PROYESP
    }
    
    return mapeo_tipos.get(tipo_formulario, 'FormGral')  # Default a General

def obtener_seller_user_id(user_id: int) -> int:
    """
    Obtiene el SellerUserID desde la tabla Profiles
    Para vendedores directos, retorna el mismo user_id
    Para distribuidores, retorna el SellerUserID asignado
    
    Args:
        user_id (int): ID del usuario actual
    
    Returns:
        int: ID del vendedor asignado
    """
    
    query = """
        SELECT 
            COALESCE(Profiles.SellerUserID, Profiles.UserID) AS SellerID
        FROM Profiles
        WHERE Profiles.UserID = ?
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            return user_id  # Fallback al mismo usuario
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (user_id,))
            
            row = cursor.fetchone()
            if row and row[0]:
                return int(row[0])
            else:
                return user_id
            
    except Exception as e:
        logger.warning(f"Error al obtener SellerUserID para usuario {user_id}: {str(e)}")
        return user_id
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass

def actualizar_formulario_con_docs(form_id: str, docs_id: int) -> dict:
    """
    Actualiza el formulario con el DocsID generado
    
    Args:
        form_id (str): ID del formulario
        docs_id (int): ID del grupo de documentos
    
    Returns:
        dict: Resultado de la operación
    """
    
    query = """
        UPDATE Q_SpQ_FormsHead 
        SET DocsID = ?
        WHERE FormID = ?
            AND Active = 1
    """

    conn = None
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        with conn:
            cursor = conn.cursor()
            cursor.execute(query, (docs_id, form_id))
            
            rows_affected = cursor.rowcount
            conn.commit()
            
            if rows_affected > 0:
                logger.info(f"Formulario {form_id} actualizado con DocsID {docs_id}")
                return {'success': True}
            else:
                logger.warning(f"No se encontró formulario {form_id} para actualizar")
                return {
                    'success': False,
                    'message': 'Formulario no encontrado para actualizar'
                }
            
    except pyodbc.Error as e:
        error_msg = f"Error de base de datos al actualizar formulario: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error al actualizar formulario con documentos',
            'code': 'DATABASE_ERROR'
        }
        
    except Exception as e:
        error_msg = f"Error inesperado al actualizar formulario: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': 'Error interno al actualizar formulario',
            'code': 'INTERNAL_ERROR'
        }
        
    finally:
        if conn:
            try:
                conn.close()
            except:
                pass            

