import base64
import binascii
import os
import logging
import mimetypes
from datetime import datetime
import httpx
import uuid
import conexiones
import mysql.connector
import re
import random
import requests
from dotenv import load_dotenv

# Cargar variables de entorno desde archivo .env
current_dir = os.path.dirname(os.path.abspath(__file__))
env_file_path = os.path.join(current_dir, '.env')

# Verificar que el archivo .env existe
if not os.path.exists(env_file_path):
    logging.error(f"Archivo .env no encontrado en: {env_file_path}")
    raise FileNotFoundError(f"No se encontró el archivo .env en {env_file_path}")

# Cargar el archivo .env
load_dotenv(env_file_path)

# Lista de variables requeridas
REQUIRED_KEYS = [
    'OPENAI_API_KEY',
    'PINECONE_API_KEY', 
    'OPENAI_API_VALOR_MAS',
    'SERVER_ENV',
    'XI_API_KEY',
    'AUDIO_URL',
    'RUTA_AUDIO',
    'N8N_WEBHOOK_URL_GEMINI'
]

# Cargar y validar las variables de entorno requeridas
missing_keys = []
config_vars = {}

for key in REQUIRED_KEYS:
    value = os.getenv(key)
    if not value and key != 'SERVER_ENV':  # SERVER_ENV puede estar vacío
        missing_keys.append(key)
    config_vars[key] = value

if missing_keys:
    error_msg = (
        f"Variables faltantes en .env: {', '.join(missing_keys)}\n"
        f"Archivo .env: {env_file_path}\n"
        "Por favor verifica las variables en el archivo .env."
    )
    logging.error(error_msg)
    raise ValueError(error_msg)

# Asignar variables a constantes globales para facilitar el acceso
OPENAI_API_KEY = config_vars['OPENAI_API_KEY']
PINECONE_API_KEY = config_vars['PINECONE_API_KEY']
OPENAI_API_VALOR_MAS = config_vars['OPENAI_API_VALOR_MAS']
SERVER_ENV = config_vars['SERVER_ENV']
XI_API_KEY = config_vars['XI_API_KEY']
AUDIO_URL = config_vars['AUDIO_URL']
RUTA_AUDIO = config_vars['RUTA_AUDIO']
N8N_WEBHOOK_URL_GEMINI = config_vars['N8N_WEBHOOK_URL_GEMINI']

# Normalizar rutas terminadas en '/'
if AUDIO_URL and not AUDIO_URL.endswith('/'):
    AUDIO_URL += '/'
if RUTA_AUDIO and not RUTA_AUDIO.endswith('/'):
    RUTA_AUDIO += '/'

# Configuraciones adicionales de validación
OPENAI_API_KEY_VALIDATED = bool(OPENAI_API_KEY)
PINECONE_API_KEY_VALIDATED = bool(PINECONE_API_KEY)
XI_API_KEY_VALIDATED = bool(XI_API_KEY)

logging.info("Variables de entorno cargadas exitosamente")
logging.info(f"N8N_WEBHOOK_URL_GEMINI: {N8N_WEBHOOK_URL_GEMINI}")
logging.info(f"AUDIO_URL: {AUDIO_URL}")
logging.info(f"RUTA_AUDIO: {RUTA_AUDIO}")

def conectar_db():
    # traerse la variable de config del archivo de conexiones.py para conectarse al localhost
    config = conexiones.config
    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

def limpiar_texto(texto):
    # Mapeo de caracteres erróneos a caracteres correctos con acentos
    reemplazos = {
        "Ã¡": "á", "Ã©": "é", "Ã­": "í", "Ã³": "ó", "Ãº": "ú",
        "Ã±": "ñ", "Ã‘": "Ñ", "Ã¼": "ü", "Ã‰": "É", "Ãš": "Ú",
        "Ã“": "Ó", "Ã�": "Í", "Ã€": "À"
    }
    
    

    # Reemplazar caracteres mal codificados
    for incorrecto, correcto in reemplazos.items():
        texto = texto.replace(incorrecto, correcto)

    # Eliminar saltos de línea y espacios innecesarios
    texto = re.sub(r"\s+", " ", texto).strip()

    return texto

def obtener_configuracion_voz_y_generar_audio(id_asistente, texto):
    texto_limpio = limpiar_texto(texto)

    try:
        connection = conectar_db()
        if connection is None:
            raise Exception("Error al conectar con la base de datos.")

    except Exception as e:
        return {"error": f"Error al conectar con la base de datos: {str(e)}"}

    try:
        cursor = connection.cursor(dictionary=True)

        query_voz = """
            SELECT 
                asistentes.id_voz,
                voces.nombre_voz,
                voces.id_eleven,
                asistentes_voces.similarity_boost,
                asistentes_voces.stability,
                asistentes_voces.style,
                asistentes_voces.use_speaker_boost
            FROM asistentes
            INNER JOIN asistentes_voces ON asistentes.id_voz = asistentes_voces.id
            INNER JOIN voces ON asistentes_voces.id_modelo_voz = voces.id
            WHERE asistentes.id = %s
        """
        cursor.execute(query_voz, (id_asistente,))
        asistente = cursor.fetchone()

        if not asistente:
            return {"error": "No se encontró un asistente con el ID proporcionado."}

        # Convertimos los valores para replicar PHP
        use_speaker_boost = "true" if asistente["use_speaker_boost"] == 1 else "false"

        # Construimos el JSON manualmente igual que en PHP
        voice_settings = (
            f"\"similarity_boost\": {asistente['similarity_boost']}, "
            f"\"stability\": {asistente['stability']}, "
            f"\"style\": {asistente['style']}, "
            f"\"use_speaker_boost\": {use_speaker_boost}"
        )

        payload = (
            "{\n"
            f"  \"model_id\": \"eleven_multilingual_v2\",\n"
            f"  \"text\": \"{texto_limpio}\",\n"
            f"  \"voice_settings\": {{\n    {voice_settings}\n  }}\n"
            "}"
        )

        # URL de ElevenLabs
        api_url = f"https://api.elevenlabs.io/v1/text-to-speech/{asistente['id_eleven']}?optimize_streaming_latency=0&output_format=mp3_44100_128"

        headers = {
            "Content-Type": "application/json",
            "xi-api-key": XI_API_KEY  # Usar la variable global
        }

        # Imprimir el JSON antes de enviarlo para comparar con PHP
        print("JSON enviado a ElevenLabs desde Python:")
        print(payload)

        # Enviar solicitud con `data=` en vez de `json=`
        response = requests.post(api_url, headers=headers, data=payload)

        if response.status_code == 200:
            numero_aleatorio = random.randint(10000, 99999)
            nombre_archivo = f"audio_{numero_aleatorio}_{id_asistente}.mp3"
            ruta_audio = f"{RUTA_AUDIO}{nombre_archivo}"  # Usar variable global
            audio_url = f"{AUDIO_URL}{nombre_archivo}"    # Usar variable global

            with open(ruta_audio, "wb") as audio_file:
                audio_file.write(response.content)

            return audio_url
        else:
            return {
                "success": False,
                "error": f"Error en ElevenLabs: {response.status_code} - {response.text}"
            }

    finally:
        if connection.is_connected():
            cursor.close()
            connection.close()

def main(thread_id: str, pregunta: str, idcliente: int, asistente: str, volume_up: str, id_asistente: int):
    # Generar nuevo thread_id si viene "nada"
    json_data = {}
    if thread_id == "nada":
        thread_id = str(uuid.uuid4())
        json_data["Tarea_Creada"] = thread_id
        logging.info(f"Nuevo thread_id generado: {thread_id}")
    else:
        logging.info(f"Usando thread_id recibido: {thread_id}")

    data = {
        "thread_id": thread_id,
        "pregunta": pregunta,
        "idcliente": str(idcliente),
        "asistente": asistente
    }

    try:
        with httpx.Client(timeout=30.0) as client:
            response = client.post(N8N_WEBHOOK_URL_GEMINI, data=data)  # Usar variable global
            response.raise_for_status()
            respuesta_texto = response.text.strip()

        json_data["Respuesta"] = respuesta_texto
        json_data["thread_id"] = thread_id
        json_data["Exito"] = "Exito"
        
        if volume_up == "si":
            ruta_audio = obtener_configuracion_voz_y_generar_audio(id_asistente, respuesta_texto)
            json_data["ruta_audio"] = ruta_audio

        return json_data

    except httpx.HTTPStatusError as e:
        logging.error(f"Error HTTP en webhook N8N: {e.response.text}")
        return {
            "status": "error",
            "message": f"Error en N8N: {e.response.text}",
            "code": e.response.status_code
        }
    except Exception as e:
        logging.error(f"Error interno en main: {str(e)}")
        return {
            "status": "error",
            "message": f"Error interno: {str(e)}"
        }