"""
Genera el JSONL de batch, lo sube a OpenAI y registra el batch en la DB.

Uso:
    python generar_batch.py [cantidad_edictos]

Variables de entorno requeridas:
    APIKEY_CHATGPT, PATH_BATCH_INPUT,
    SERVER_D, DATABASE_D, USERNAME_D, PASSWORD_D
"""

from dotenv import load_dotenv
import datetime
import logging
import json
import os
import pathlib
import socket
import sys
import uuid

# Asegura que la raíz del proyecto esté en sys.path para poder importar
# `helpers` y `procesamiento` cuando el script se ejecuta directamente.
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

import helpers.db as db
import procesamiento.batch_openai.openai_client as openai_client
from helpers.logging_setup import setup_logging, setup_logging_db

load_dotenv()
_run_id = setup_logging("generar_batch")

ID_PROMPT  = 23
TOKENS     = 1000
MAX_SIZE   = 209_715_200  # 200 MB
ID_PROCESO = int(os.getenv("ID_PROCESO_GENERAR_BATCH", 0))


def generar_batch(cantidad_edictos=20000, es_prueba=False):
    api_key = os.getenv("APIKEY_CHATGPT")
    if not api_key:
        raise ValueError("APIKEY_CHATGPT no está definida en las variables de entorno.")

    prompt_data = db.ejecutar_sp(
        'gestor_prompts',
        ['SELECT_ONE', None, None, None, None, None, None, None, ID_PROMPT],
    )
    
    if not prompt_data:
        return logging.warning("No se encontraron prompts")

    model         = "gpt-5.4-mini"
    system_prompt = prompt_data[0]["system_prompt"]
    #user_prompt   = prompt_data[0]["user_prompt"]
    user_prompt = r"""* Evita títulos y explicaciones.
            Respetar la estructura JSON solicitada.
            No inventar datos. Sólo informar los contenidos en el Edicto.

            No incluyas claves con valores nulos.

            No incluyas claves con valor "No disponible" o "Null".

            Valores como SA, S.A., SRL, S.R.L., SL, S.L., SAS, INC, LLC son referidos a empresa_tipo.

            En Datos_Empresa, NO informar cuit_empresa si comienza con "2".

            En el nodo persona_estado_civil, indicar si es casado/a o soltero/a solo si esta información está disponible. En caso de indicar con quien esta casado/a, agregar el nodo persona_conyuge con el nombre de la persona.

            En persona_cargo:
            Los ÚNICOS roles válidos son: "Presidente", "Vicepresidente", "Gerente", "Director", "Director Titular", "Director Suplente", "Administrador", "Administrador Titular", "Administrador Suplente", "Apoderado", "Socio". No usar ningún otro rol aunque aparezca en el texto.
            Para CUALQUIER tipo de sociedad (SAS, SA, SRL, etc.): las personas listadas en la sección "1.-" (o quienes constituyan la sociedad) deben ser consideradas socios fundadores. Asignarles el rol "Socio" siempre, aunque el texto no lo mencione explícitamente.
            el formato debe ser: "Persona_Cargo": [{"p_rol": " ", "permanece_en_cargo?": " "}]
            Si la persona tiene más de un cargo válido, agregar un objeto separado por cada rol dentro del array.

            En el nodo permanece_en_cargo? indicar "No" cuando la persona falleció, renunció, cesó o fue removida de su cargo.

            En Vinculada_vinculo:
            Considera la vinculación de "Socio", "Relacionadas", "Controladas", "Controlantes", por su rol DENTRO de la empresa.
            el formato debe ser: "Vinculada_vinculo ": [{"e_rol": " "}]
            Cada rol debe expresarse de forma separada.

            En el nodo tipo_edicto indicar el objeto principal del edicto.

            En el nodo cuit_empresa, aplicar este regex: str.replace(/2C (\d{2})(\d{4})(\d+)-(\d)/, "$1-$2-$3-$4").

            En el nodo persona_DNI, aplicar este regex: str.replace(/./g, "").

            En el nodo persona_cuit, aplicar este regex: str.replace(/2C (\d{2})(\d{4})(\d+)-(\d)/, "$1-$2-$3-$4").

            En el nodo Pais, aplicar 2 o 3 caracteres para la identificación internacional.

            En los nodos fecha, aplicar el formato DD-MM-YYYY"""
    logging.info(f"Modelo: {model}")

    edictos = db.ejecutar_sp(
        "openai_edictos_dia",
        ['BUSQUEDA_VACIOS', None, None, 11, None, cantidad_edictos],
    )
    if not edictos:
        return logging.info("No se encontraron edictos")

    logging.info(f"Edictos encontrados: {len(edictos)}")

    directorio = os.getenv('PATH_BATCH_INPUT')
    os.makedirs(directorio, mode=0o777, exist_ok=True)

    timestamp  = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    unique_id  = uuid.uuid4().hex[:6]
    batch_file = os.path.join(directorio, f'batchinput_{timestamp}_{unique_id}.jsonl')

    lineas = []
    errores = 0
    for edicto in edictos:
        id_fide = edicto.get('id_fide', '')
        contenido = edicto.get('contenido_archivo')
        if not contenido:
            logging.warning(f"id_fide={id_fide} sin contenido_archivo, ignorado")
            errores += 1
            continue
        try:
            linea = openai_client.formatear_linea_batch(
                edicto.get('fecha_publicacion', '') or '',
                id_fide                             or '',
                edicto.get('fuente', '')             or '',
                model, system_prompt, user_prompt,
                contenido,
                TOKENS,
            )
            lineas.append(linea)
        except Exception as e:
            logging.error(f"id_fide={id_fide} error al formatear línea: {e}")
            errores += 1

    logging.info(f"Líneas válidas: {len(lineas)} | Ignoradas/errores: {errores}")

    if not lineas:
        return logging.warning("No se generaron líneas válidas, proceso finalizado sin subir archivo")

    with open(batch_file, 'w', encoding='utf-8') as f:
        for linea in lineas:
            f.write(json.dumps(linea, ensure_ascii=False) + '\n')

    file_size = os.path.getsize(batch_file)
    logging.info(f"Archivo generado: {batch_file} ({file_size} bytes)")

    if file_size > MAX_SIZE:
        return logging.error(f"Archivo supera 200 MB ({file_size} bytes)")

    file_resp  = openai_client.subir_archivo(batch_file, api_key)
    logging.info(f"FILE_SUBIDO | file_id={file_resp['id']}")
    metadata   = {"env": "test" if es_prueba else "prod"}
    batch_resp = openai_client.crear_batch(file_resp['id'], api_key, metadata=metadata)
    logging.info(f"BATCH_CREADO | batch_id={batch_resp['id']} file_id={file_resp['id']}")
    db.ejecutar_sp('gestor_batch_procesos', [
        'INSERT', None, batch_resp['id'], None, 'pendiente',
        ID_PROMPT, 'batch grande diario', cantidad_edictos, None, batch_file, None
    ])
    logging.debug(f"BATCH_RESPUESTA | {json.dumps(batch_resp, ensure_ascii=False)}")


if __name__ == "__main__":
    logging.info("Inicio del proceso")
    args_cli  = [a for a in sys.argv[1:] if a != "--test"]
    es_prueba = "--test" in sys.argv[1:]
    cantidad  = int(args_cli[0]) if args_cli else 20000

    id_log = None
    try:
        result = db.ejecutar_sp('PRS_iniciar', [
            ID_PROCESO,
            socket.gethostbyname(socket.gethostname()),
            os.getpid(),
            socket.gethostname(),
            12,
            _run_id,
        ])
        if result:
            id_log = result[0].get('id_log')
            setup_logging_db(id_log)
    except Exception as e:
        logging.warning(f"PRS_iniciar falló: {e}")

    ok_count = 0
    error_count = 0
    total = cantidad
    try:
        generar_batch(cantidad, es_prueba=es_prueba)
        ok_count = 1
    except Exception as e:
        error_count = 1
        logging.error(f"Error en generar_batch: {e}")
    finally:
        if id_log is not None:
            try:
                db.ejecutar_sp('PRS_finalizar', [id_log, None, total, ok_count, error_count])
            except Exception as e:
                logging.warning(f"PRS_finalizar falló: {e}")