import sys
import os
from pathlib import Path
from datetime import datetime
import logging
import paramiko
import shutil

from cnxpdo import get_connection
from dotenv import load_dotenv

# Configurar path para importar el módulo storage
current_dir = os.path.dirname(os.path.abspath(__file__))  # /landing/propuestas
parent_dir = os.path.dirname(current_dir)                 # /landing
sys.path.append(parent_dir)

# Importar el adaptador de storage
from storage.storage_factory import StorageFactory

# Agrega la ruta absoluta al sys.path
sys.path.append('/var/www/html/config')

# ========== CONFIGURACIÓN DE RUTAS PARA VARIABLES DE ENTORNO ==========

# Obtener el directorio donde se encuentra este script (landing/propuestas/)
script_dir = os.path.dirname(os.path.abspath(__file__))  # /var/www/dev.api.imparables.cic-ware.com/landing/propuestas

# Devolverme al directorio landing
tasa_dir = os.path.dirname(script_dir)  # /var/www/dev.api.imparables.cic-ware.com/landing

# Devolverme al directorio raíz del proyecto
root_dir = os.path.dirname(tasa_dir)  # /var/www/dev.api.imparables.cic-ware.com

# Construir la ruta hacia el archivo .env en crm/configuraciones/
env_path = os.path.join(root_dir, "crm", "configuraciones", ".env")

# Cargar variables de entorno
if os.path.exists(env_path):
    load_dotenv(env_path)
    logging.info(f"✅ Variables de entorno cargadas desde: {env_path}")
else:
    logging.warning(f"⚠ No se encontró el archivo .env en: {env_path}")




# ========== FUNCIONES DE ACCIÓN DE LOS ENDPOINTS ==========

async def propuesta_nueva(data: dict):
    conexionBD = None
    cursor = None
    id_propuesta = None
    id_documento = None
    id_documentos = []

    try:
        conexionBD = get_connection()
        if conexionBD is None:
            return {
                "success": 0,
                "message": "Error de conexión"
            }
        cursor = conexionBD.cursor(dictionary=True)
        
        nombreCompleto = data.get('nombreCompleto', '')
        email = data.get('email', '')
        tipoDocumento = data.get('tipoDocumento', '')
        documento = data.get('documento', '')
        telefono = data.get('telefono', '')
        ciudad = data.get('ciudad', '')
        tituloPropuesta = data.get('tituloPropuesta', '')
        ambitoPropuesta = data.get('ambitoPropuesta', '')
        descripcionPropuesta = data.get('descripcionPropuesta', '')
        adjuntos = data.get('adjunto', [])
        
        path_files = os.getenv('ftp_path_files') #ruta destino a guardar los archivos fisicos /var/www/dev.api.imparables.cic-ware.com/crm/files

        # Validaciones: Asegurarse de que los campos obligatorios no estén vacíos
        if not (nombreCompleto and email and tipoDocumento and documento and telefono and ciudad and tituloPropuesta and ambitoPropuesta and descripcionPropuesta):
            return {
                "success": False,
                "message": "Faltan campos obligatorios",
                "data": {}
            }

        # Si la persona no existe, creamos un nuevo registro
        query = """ 
            INSERT INTO 
            propuesta_ciudadano (
                nombre_completo,
                correo,
                tipo_documento,
                documento,
                telefono,
                ciudad,
                titulo,
                ambito,
                descripcion,
                fecha_hora
            ) VALUES (
                %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW()
            )
        """
        
        valores = (
            nombreCompleto,
            email,
            tipoDocumento,
            documento,
            telefono,
            ciudad,
            tituloPropuesta,
            ambitoPropuesta,
            descripcionPropuesta
        )
        
        # Ejecutamos la consulta para insertar
        cursor.execute(query, valores)
        id_propuesta = cursor.lastrowid  # Obtener el ID del registro insertado

        if adjuntos:
            # Validar que el adjunto no esté vacío
            for adjunto in adjuntos:
                if adjunto.file is None or adjunto.file.read() == b'':
                    return {
                        "success": False, 
                        "message": f"El archivo adjunto '{adjunto.filename}' está vacío o no es válido.",
                        "data": {}
                    }
                # Volver a posicionar el cursor del archivo al inicio
                adjunto.file.seek(0)
            
            # Llamar a la función para guardar los archivos en el sistema local
            local_response = guardar_archivos_local(id_propuesta, adjuntos)
            if not local_response.get("success", False):
                return {
                    "success": False,
                    "message": local_response.get("message", "Error al guardar el archivo en el sistema local"),
                    "data": {}
                }
            
            # Guardar archivos en S3 después del guardado local
            s3_response = await guardar_archivos_con_adaptador(id_propuesta, adjuntos)
            
            # Insertar los archivos en la base de datos
            for adjunto in adjuntos:
                query_documento = """
                    INSERT INTO documentos_propuestas (
                        nombre_archivo, ruta, id_propuesta_ciud, fecha
                    ) VALUES (%s, %s, %s, NOW())
                """
                valores_documento = (
                    adjunto.filename,
                    f"{path_files}/{id_propuesta}/{adjunto.filename}",
                    id_propuesta
                )
                cursor.execute(query_documento, valores_documento)
                id_documento = cursor.lastrowid  # Obtener el ID del documento insertado
                id_documentos.append(id_documento)

        # Commit para confirmar la inserción
        conexionBD.commit()

        # Retornar la respuesta de éxito con el ID del nuevo registro
        return {
            "success": True,
            "message": "Propuesta agregada exitosamente",
            "data": {
                "id_propuesta_ciudadano": id_propuesta,
                "id_documento": id_documentos if adjuntos else None,
            }
        }
        
    except Exception as e:
        # Registra el error y detalles
        if cursor and hasattr(cursor, 'statement'):
            print(f"Última consulta ejecutada: {cursor.statement}")
        return {"success": False, "message": f"Error en la inserción: {str(e)}", "data": {}}
    
    finally:
        # Cerrar cursor y conexión
        if cursor:
            cursor.close()
        if conexionBD:
            conexionBD.close()



def obtener_todas_propuestas():
    conexionBD = None
    cursor = None

    try:
        # Establecer la conexión a la base de datos
        conexionBD = get_connection()  # Asegúrate de que get_connection esté funcionando correctamente
        if conexionBD is None:
            return {
                "success": False,
                "message": "Error de conexión"
            }

        # Crear el cursor para ejecutar la consulta
        cursor = conexionBD.cursor(dictionary=True)

        # Consulta SQL
        query = "SELECT * FROM propuestas_landing"
        query += " ORDER BY id DESC LIMIT 6"  # Limitar a los primeros 4 registros

        # Ejecutar la consulta correctamente con el cursor de conexionBD
        cursor.execute(query)
        datos = cursor.fetchall()

        return {
            "success": True,
            "message": "Propuestas obtenidas exitosamente",
            "data": datos
        }

    except Exception as e:
        print(f"Error en la inserción: {str(e)}")
        if cursor and hasattr(cursor, 'statement'):
            print(f"Última consulta ejecutada: {cursor.statement}")
        return {
            "success": False, 
            "message": f"Error en la inserción: {str(e)}", 
            "data": {}
        }
    
    finally:
        # Cerrar cursor y conexión
        if cursor:
            cursor.close()
        if conexionBD:
            conexionBD.close()





# ========== FUNCIONES SEGUNDARIAS ==========
#! funcion q hace conexión con un servidor externo por SFTP (no se esta usando)
def conexion_guardado_sftp(id_propuesta, adjuntos):
    # =============================
    # 📌 Configuración SFTP con clave SSH
    # =============================
    
    server = os.getenv('ftp_server')
    username = os.getenv('ftp_user_name')
    #pem_key_path = os.getenv('ftp_private_key_path')
    passVal = os.getenv('ftp_user_pass')
    port = int(os.getenv('ftp_port'))
    path_files = os.getenv('ftp_path_files') #ruta destino a guardar los archivos fisicos /var/www/dev.api.imparables.cic-ware.com/crm/files
    
    transport = None
    sftp = None
        
    try:
        #print(f"🔐 [SFTP] Conectando al servidor {server} como {username} con clave privada en la ruta {path_files}")
        print(f"🔐 [SFTP] Conectando al servidor {server} como {username} usando contraseña")
        
        #key = paramiko.RSAKey.from_private_key_file(pem_key_path)
        transport = paramiko.Transport((server, port))
        #transport.connect(username=username, pkey=key)
        transport.connect(username=username, password=passVal)
        
        # Ahora puedes proceder con las demás operaciones de SFTP como normalmente
        sftp = paramiko.SFTPClient.from_transport(transport)
        
        print(f"sftp: {sftp}")
        
        # Crear la carpeta con el id_propuesta si no existe
        sftp_directory = os.path.join(path_files, str(id_propuesta))  # Directorio con el ID de la propuesta
        
        try:
            # Verificar si la carpeta ya existe
            sftp.stat(sftp_directory)
            print(f"📂 La carpeta '{sftp_directory}' ya existe en el servidor.")
        except FileNotFoundError:
            # Si la carpeta no existe, la creamos
            sftp.mkdir(sftp_directory)
            print(f"📂 Carpeta '{sftp_directory}' creada en el servidor.")
        
        # Verificar la cantidad de archivos en la carpeta
        files_in_directory = sftp.listdir_attr(sftp_directory)  # Obtiene los atributos de los archivos en el directorio
        
        # Si hay más de 5 archivos, eliminar los más antiguos
        if len(files_in_directory) >= 5:
            # Ordenar los archivos por fecha de modificación (de más antiguo a más reciente)
            files_in_directory.sort(key=lambda x: x.st_mtime)
            
            # Eliminar los archivos más antiguos (los primeros en la lista)
            files_to_remove = files_in_directory[:len(files_in_directory) - 5]
            for file in files_to_remove:
                file_path = os.path.join(sftp_directory, file.filename)
                sftp.remove(file_path)
                print(f"🗑️ Archivo eliminado: {file_path}")
        
        # Subir los archivos dentro de la carpeta creada
        for adjunto in adjuntos:
            local_filepath = adjunto.filename
            remote_filepath = os.path.join(sftp_directory, adjunto.filename)  # Ruta remota para el archivo
            
            # Guardar el archivo en el servidor SFTP
            with sftp.open(remote_filepath, 'wb') as remote_file:
                remote_file.write(adjunto.file.read())  # Escribir el contenido del archivo en el servidor
            print(f"📤 Archivo '{local_filepath}' subido correctamente a '{remote_filepath}'")

        return {"success": True, "message": "Archivos subidos exitosamente"}
    
    except Exception as e:
        return {"success": False, "message": f"Error al subir el archivo al servidor: {e}"}
    
    finally:
        # Cerrar el cliente y la conexión SFTP
        if sftp:
            sftp.close()
        if transport:
            transport.close()


def guardar_archivos_local(id_propuesta, adjuntos):
    # Definir la ruta base donde se guardarán los archivos (según tu estructura de directorios)
    #base_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'crm', 'files')
    path_files = os.getenv('ftp_path_files') #ruta destino a guardar los archivos fisicos /var/www/dev.api.imparables.cic-ware.com/crm/files

    propuesta_path = os.path.join(path_files, str(id_propuesta))

    try:
        # Crear carpeta si no existe
        if not os.path.exists(propuesta_path):
            os.makedirs(propuesta_path)
            print(f"📂 Carpeta creada: {propuesta_path}")
        else:
            print(f"📂 Carpeta ya existe: {propuesta_path}")

        # Obtener archivos existentes
        existing_files = os.listdir(propuesta_path)
        files_in_directory = [
            (file, os.path.getmtime(os.path.join(propuesta_path, file)))
            for file in existing_files
            if os.path.isfile(os.path.join(propuesta_path, file))
        ]

        num_archivos_existentes = len(files_in_directory)
        num_archivos_nuevos = len(adjuntos)
        total_despues = num_archivos_existentes + num_archivos_nuevos
        excedente = max(total_despues - 5, 0)

        # Eliminar los archivos más antiguos si exceden el límite
        if excedente > 0 and files_in_directory:
            files_in_directory.sort(key=lambda x: x[1])  # ordenar por fecha de modificación ascendente
            files_to_remove = files_in_directory[:excedente]

            for file, _ in files_to_remove:
                file_path = os.path.join(propuesta_path, file)
                os.remove(file_path)
                print(f"🗑️ Archivo eliminado: {file_path}")

        # Guardar nuevos archivos
        for adjunto in adjuntos:
            local_filename = adjunto.filename
            local_file_path = os.path.join(propuesta_path, local_filename)

            with open(local_file_path, 'wb') as destination_file:
                adjunto.file.seek(0)  # Asegurar que el puntero del archivo está al inicio
                shutil.copyfileobj(adjunto.file, destination_file)

            print(f"📤 Archivo guardado: {local_file_path}")

        return {"success": True, "message": "Archivos guardados exitosamente"}

    except Exception as e:
        return {
            "success": False,
            "message": f"Error al guardar los archivos en el sistema local: {e}"
        }



def manejar_propuesta_existente(cursor, id_propuesta, adjuntos, path_files):

    query_files = """
        SELECT id, nombre_archivo 
        FROM documentos_propuestas 
        WHERE id_propuesta_ciud = %s
        ORDER BY fecha ASC
    """
    cursor.execute(query_files, (id_propuesta,))
    existing_files = cursor.fetchall()
    
    resultado = reemplazar_archivos_antiguos(cursor, id_propuesta, adjuntos, existing_files, path_files)

    if not resultado["success"]:
        return resultado

    return {
        "success": True,
        "message": "Propuesta actualizada exitosamente",
        "data": {
            "id_propuesta_ciudadano": id_propuesta,
            "id_documentos": resultado["id_documentos"],
        }
    }



def reemplazar_archivos_antiguos(cursor, id_propuesta, adjuntos, existing_files, path_files):
    if not adjuntos:
        return {"success": False, "message": "No se proporcionaron archivos nuevos."}

    id_documentos_actualizados = []
    num_reemplazos = min(len(adjuntos), len(existing_files))

    # Reemplazar archivos existentes
    for i in range(num_reemplazos):
        nuevo_adjunto = adjuntos[i]
        archivo_antiguo = existing_files[i]

        # Opcional: Eliminar archivo físico antiguo
        ruta_archivo_antiguo = f"{path_files}/{id_propuesta}/{archivo_antiguo['nombre_archivo']}"
        if os.path.exists(ruta_archivo_antiguo):
            os.remove(ruta_archivo_antiguo)

        # Actualizar en la base de datos
        query_update = """
            UPDATE documentos_propuestas
            SET nombre_archivo = %s, ruta = %s, fecha = NOW()
            WHERE id = %s
        """
        cursor.execute(query_update, (
            nuevo_adjunto.filename,
            f"{path_files}/{id_propuesta}/{nuevo_adjunto.filename}",
            archivo_antiguo['id']
        ))
        id_documentos_actualizados.append(archivo_antiguo['id'])

    # Reemplazar archivos físicamente
    guardar_resultado = guardar_archivos_local(id_propuesta, adjuntos)
    if not guardar_resultado.get("success", False):
        return {
            "success": False,
            "message": guardar_resultado.get("message", "Error al guardar los archivos"),
            "data": {}
        }

    return {
        "success": True,
        "id_documentos": id_documentos_actualizados
    }


async def guardar_archivos_con_adaptador(id_propuesta, adjuntos):
    """
    Función de ejemplo que usa el adaptador de storage para guardar archivos.
    Esta función puede ser llamada desde propuesta_nueva() para usar S3 o local según configuración.
    """
    try:
        # Obtener el storage configurado (S3 o local)
        storage = StorageFactory.get_storage()
        folder_path = f"propuestas/{id_propuesta}"
        
        # Subir archivos usando el adaptador
        upload_result = await storage.save_files(folder_path, adjuntos)
        
        if not upload_result.get("success", False):
            return {
                "success": False,
                "message": upload_result.get("message", "Error al subir archivos"),
                "data": {}
            }
        
        # Retornar información de los archivos subidos
        return {
            "success": True,
            "message": "Archivos guardados exitosamente con adaptador",
            "data": {
                "files": upload_result["files"],
                "storage_type": "s3" if hasattr(storage, 's3_client') else "local"
            }
        }
        
    except Exception as e:
        return {
            "success": False,
            "message": f"Error al guardar archivos con adaptador: {str(e)}",
            "data": {}
        }

