import pandas as pd
import json
import openai
from pinecone import Pinecone, ServerlessSpec
import os
from docx import Document
import pdfplumber
import paramiko
import sys
import mysql.connector

# Configura tus claves API
OPENAI_API_KEY = "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"
PINECONE_API_KEY = (
    "pcsk_2FxhEC_9SEynUFsCEwAbqyaKBqRYmqTNCysdyf8CkmyA14JGTjpq5K5PdB4t7cBcL2QmaV"
)

# Inicializa el cliente de OpenAI
openai.api_key = OPENAI_API_KEY

# Inicializa el cliente de Pinecone
pc = Pinecone(api_key=PINECONE_API_KEY)

# Datos para conectarse a la base de datos local
config = {
    "user": "usermas",
    "password": "u2$p1Lg@XY#XUpY2kf",
    "host": "10.51.20.5",
    "database": "data_catastro",
    "raise_on_warnings": True,
}


# Función para conectar a la base de datos
def conectar_db():
    try:
        connection = mysql.connector.connect(**config)
        if connection.is_connected():
            return connection
        else:
            return None
    except mysql.connector.Error as err:
        print(f"Error: {err}")
        return None


# Función para insertar en la tabla asistentes_archivos
def insertar_en_asistentes_archivos(
    id_asistente, nombre_archivo, nom_indexe, proposito_archivo, extencion
):
    try:
        # Conexión a la base de datos
        conexion = conectar_db()
        if conexion is None:
            print("No se pudo conectar a la base de datos.")
            return

        # Consulta de inserción
        query = """
            INSERT INTO asistentes_archivos (
                id_asistente, 
                nombre_archivo, 
                nombre_indexe, 
                proposito_archivo, 
                extencion
            ) VALUES (%s, %s, %s, %s, %s)
        """
        valores = (
            id_asistente,
            nombre_archivo,
            nom_indexe,
            proposito_archivo,
            extencion,
        )

        # Ejecutar la consulta
        cursor = conexion.cursor()
        cursor.execute(query, valores)
        conexion.commit()

    except mysql.connector.Error as err:
        print(f"Error al insertar datos: {err}")
    finally:
        if conexion.is_connected():
            cursor.close()
            conexion.close()


def generar_plantilla_funcion(proposito, fuente):
    try:
        # Prompt ajustado con el nuevo argumento
        prompt = f"""
        Analiza cuidadosamente el propósito proporcionado: "{proposito}".
        Genera una instrucción breve y clara para un asistente que sea meramente informativa. 
        La instrucción debe:
        - Identificar cuándo se debe ejecutar la función "ejecutar_vectorial".
        - Indicar que la función "ejecutar_vectorial" debe pasarse el argumento de nombre = "{fuente}".
        - Estar en una sola oración, clara y concisa.
        Ejemplo de formato esperado:
        - Cuando un usuario te pregunte sobre las fases y creación del CRM de Valor+, ejecuta la función "ejecutar_vectorial" con el argumento = fuente.
        """

        # Generar respuesta usando OpenAI con GPT-4o
        response = openai.chat.completions.create(
            model="gpt-4o",
            messages=[
                {
                    "role": "system",
                    "content": (
                        "Eres un generador de instrucciones eficientes para asistentes. "
                        "Crea plantillas breves y claras basadas en el propósito proporcionado."
                    ),
                },
                {
                    "role": "user",
                    "content": prompt,
                },
            ],
            max_tokens=100,  # Limitar la longitud de la respuesta
            temperature=0.5,
        )

        # Verificar si la respuesta contiene datos válidos
        if response and response.choices and len(response.choices) > 0:
            function_template = response.choices[0].message.content.strip()
            return {"status": "success", "function_template": function_template}
        else:
            return {
                "status": "error",
                "message": "La respuesta del modelo está vacía o no tiene el formato esperado.",
            }

    except openai.error.OpenAIError as e:
        return {
            "status": "error",
            "message": f"Error al interactuar con OpenAI: {str(e)}",
        }
    except Exception as e:
        return {"status": "error", "message": f"Error inesperado: {str(e)}"}


# Función para insertar en la tabla asistentes_funciones
def insertar_en_asistentes_funciones(id_asistente, funcion):
    try:
        # Conexión a la base de datos
        conexion = conectar_db()
        if conexion is None:
            print("No se pudo conectar a la base de datos.")
            return

        # Consulta de inserción
        query = """
            INSERT INTO asistentes_funciones (
                id_asistente, 
                funcion
            ) VALUES (%s, %s)
        """
        valores = (id_asistente, funcion)

        # Ejecutar la consulta
        cursor = conexion.cursor()
        cursor.execute(query, valores)
        conexion.commit()
        # print("Inserción exitosa en asistentes_funciones.")

    except mysql.connector.Error as err:
        print(f"Error al insertar datos: {err}")
    finally:
        if conexion.is_connected():
            cursor.close()
            conexion.close()


def main():
    if len(sys.argv) > 1:
        global json_data
        global ruta_archivos
        global nom_indexe
        global fuente
        global asistente
        global proposito

        ruta_archivos = sys.argv[1]
        nom_indexe = sys.argv[2]
        fuente = sys.argv[3]
        asistente = sys.argv[4]
        proposito = sys.argv[5]

        # Pasar proposito de hexadecimal a string
        proposito = bytes.fromhex(proposito).decode("utf-8")

        # Crear una variable JSON en memoria
        json_data = {}
        json_data["ruta_archivos"] = sys.argv[1]
        json_data["nom_indexe"] = sys.argv[2]


if __name__ == "__main__":
    main()

# Define el nombre y la dimensión del índice
index_name = fuente
dimension = 1536  # Dimensión para 'text-embedding-ada-002'

# Crea el índice si no existe
if index_name not in [index["name"] for index in pc.list_indexes()]:
    pc.create_index(
        name=index_name,
        dimension=dimension,
        metric="cosine",  # Puedes ajustar la métrica según tus necesidades
        spec=ServerlessSpec(
            cloud="aws", region="us-east-1"  # Ajusta la región según tus preferencias
        ),
    )

# Conecta con el índice
index = pc.Index(index_name)

# Función para procesar archivos Excel


def verificar_excel(file_path):
    sheets = pd.ExcelFile(file_path).sheet_names
    if len(sheets) > 1:
        return {
            "status": "error",
            "message": "El archivo Excel no debe tener más de una hoja.",
        }

    df = pd.read_excel(file_path)
    if df.empty:
        return {"status": "error", "message": "El archivo Excel no contiene datos."}

    # Verificar que no haya datos por fuera de la tabla principal
    used_cells = df.notnull().sum().sum()
    total_cells = df.shape[0] * df.shape[1]
    if used_cells < total_cells:
        return {
            "status": "error",
            "message": "El archivo Excel contiene celdas vacías fuera de la tabla principal.",
        }

    return {"status": "success", "data": df}


def procesar_excel(file_path):
    result = verificar_excel(file_path)
    if result["status"] == "error":
        return result

    df = result["data"]
    return {"status": "success", "data": {"Hoja1": df.to_dict(orient="records")}}


# Función para procesar archivos Word


def procesar_word(file_path):
    try:
        doc = Document(file_path)
        data = []
        max_size = 2000  # Límite máximo de caracteres por chunk
        for i, paragraph in enumerate(doc.paragraphs):
            content = paragraph.text.strip()
            if content:
                # Dividir contenido si excede el tamaño máximo permitido
                for j in range(0, len(content), max_size):
                    chunk = content[j : j + max_size]
                    data.append(
                        {
                            "page_number": i + 1,
                            "chunk_number": j // max_size + 1,
                            "content": chunk,
                        }
                    )
        return {"status": "success", "data": data}
    except Exception as e:
        return {
            "status": "error",
            "message": f"Error al procesar el archivo Word: {str(e)}",
        }


# Función para procesar archivos PDF


def procesar_pdf(file_path):
    try:
        data = []
        max_size = 2000  # Límite máximo de caracteres por chunk
        with pdfplumber.open(file_path) as pdf:
            for page_number, page in enumerate(pdf.pages, start=1):
                content = page.extract_text().strip()
                if content:
                    # Dividir contenido si excede el tamaño máximo permitido
                    for j in range(0, len(content), max_size):
                        chunk = content[j : j + max_size]
                        data.append(
                            {
                                "page_number": page_number,
                                "chunk_number": j // max_size + 1,
                                "content": chunk,
                            }
                        )
        return {"status": "success", "data": data}
    except Exception as e:
        return {
            "status": "error",
            "message": f"Error al procesar el archivo PDF: {str(e)}",
        }


# Función para generar embeddings
def generar_embedding(texto):
    response = openai.embeddings.create(input=texto, model="text-embedding-ada-002")
    return response.data[0].embedding


# Función para dividir los vectores en lotes


def dividir_en_lotes(vectores, tamano_lote):
    for i in range(0, len(vectores), tamano_lote):
        yield vectores[i : i + tamano_lote]


def descargar_archivo_sftp(host, username, password, remote_path, local_directory):
    try:
        # Crear directorio local si no existe
        if not os.path.exists(local_directory):
            os.makedirs(local_directory)

        # Extraer el nombre del archivo
        file_name = os.path.basename(remote_path)
        local_path = os.path.join(local_directory, file_name)

        # Conexión SFTP
        transport = paramiko.Transport((host, 22))
        transport.connect(username=username, password=password)
        sftp = paramiko.SFTPClient.from_transport(transport)

        # Descargar el archivo
        sftp.get(remote_path, local_path)
        sftp.close()
        transport.close()

        return local_path
    except Exception as e:
        print(f"Error al descargar el archivo: {e}")
        raise e


# Parámetros del servidor y archivo
host = "10.51.20.5"
username = "kubico"
password = "8w.bO0sGMo£0t>n2"
remote_path = ruta_archivos
local_directory = r"/var/www/cobra/DESARROLLO/valormas/archivos_vectoriales"  # Cambia a tu directorio local preferido

# Descargar archivo y asignar la ruta local a file_path
file_path = descargar_archivo_sftp(
    host, username, password, remote_path, local_directory
)

# Verificar la extensión del archivo
file_extension = os.path.splitext(file_path)[1]
data = None  # Inicializar variable de datos
result_log = {"steps": []}  # Para almacenar los resultados del flujo

if file_extension == ".xlsx":
    result_log["steps"].append({"step": "Procesar Excel", "status": "started"})
    result = procesar_excel(file_path)
    if result["status"] == "error":
        result_log["steps"].append(
            {"step": "Procesar Excel", "status": "error", "message": result["message"]}
        )
    else:
        data = result["data"]
        result_log["steps"].append({"step": "Procesar Excel", "status": "success"})
elif file_extension == ".docx":
    result_log["steps"].append({"step": "Procesar Word", "status": "started"})
    result = procesar_word(file_path)
    if result["status"] == "error":
        result_log["steps"].append(
            {"step": "Procesar Word", "status": "error", "message": result["message"]}
        )
    else:
        data = result["data"]
        result_log["steps"].append({"step": "Procesar Word", "status": "success"})
elif file_extension == ".pdf":
    result_log["steps"].append({"step": "Procesar PDF", "status": "started"})
    result = procesar_pdf(file_path)
    if result["status"] == "error":
        result_log["steps"].append(
            {"step": "Procesar PDF", "status": "error", "message": result["message"]}
        )
    else:
        data = result["data"]
        result_log["steps"].append({"step": "Procesar PDF", "status": "success"})
else:
    result_log["steps"].append(
        {
            "step": "Validar extensión",
            "status": "error",
            "message": "Formato de archivo no válido. Solo se aceptan archivos .xlsx, .docx y .pdf.",
        }
    )

# Cargar el archivo JSON procesado y subir los datos a Pinecone
if data:
    vectores = []
    if isinstance(data, dict):  # Caso para Excel
        for record in data.get("Hoja1", []):
            if isinstance(record, dict):  # Verificar que el registro es un diccionario
                texto = " ".join(
                    [f"{key}: {value}" for key, value in record.items() if value]
                )
                metadata = record
            else:
                result_log["steps"].append(
                    {
                        "step": "Validar Registro",
                        "status": "error",
                        "message": "Formato inesperado en el registro",
                    }
                )
                continue

            # Generar el embedding
            embedding = generar_embedding(texto)

            # Agregar al vector
            vectores.append(
                {
                    "id": f"vector_{len(vectores) + 1}",
                    "values": embedding,
                    "metadata": metadata,
                }
            )
    elif isinstance(data, list):  # Caso para Word y PDF
        for record in data:
            texto = record["content"]
            metadata = {
                "page_number": record["page_number"],
                "chunk_number": record["chunk_number"],
                "content": texto,
            }

            # Generar el embedding
            embedding = generar_embedding(texto)

            # Agregar al vector
            vectores.append(
                {
                    "id": f"vector_{len(vectores) + 1}",
                    "values": embedding,
                    "metadata": metadata,
                }
            )

    # Dividir los vectores en lotes y subir a Pinecone
    tamano_lote = 100  # Número máximo de vectores por lote
    for lote in dividir_en_lotes(vectores, tamano_lote):
        index.upsert(vectors=lote)

    result_log["steps"].append(
        {
            "step": "Subir a Pinecone",
            "status": "success",
            "message": "Documento Cargado Exitosamente",
        }
    )

    # Llamar a la función de inserción
    insertar_en_asistentes_archivos(
        id_asistente=asistente,
        nombre_archivo=os.path.basename(file_path),
        nom_indexe=fuente,
        proposito_archivo=proposito,
        extencion=file_extension,
    )

    # Generar plantilla de función usando GPT-4o
    plantilla_funcion = generar_plantilla_funcion(proposito, fuente)
    insertar_en_asistentes_funciones(
        id_asistente=asistente,  # Variable global definida en el script
        funcion=plantilla_funcion["function_template"],  # Plantilla generada
    )

# Imprimir el log final como JSON
print(json.dumps(result_log, ensure_ascii=False, indent=4))
