import logging
import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from helpers.db import conectar
from helpers.logging_setup import setup_logging

setup_logging("socios_accionistas")

def cuit_valido(cuit):
    return cuit and str(cuit).isdigit() and len(str(cuit)) >= 11

def levenshtein(s1, s2):
    if len(s1) < len(s2):
        return levenshtein(s2, s1)

    if len(s2) == 0:
        return len(s1)

    prev = list(range(len(s2) + 1))
    for i, c1 in enumerate(s1):
        curr = [i + 1]
        for j, c2 in enumerate(s2):
            insert = prev[j + 1] + 1
            delete = curr[j] + 1
            replace = prev[j] + (c1 != c2)
            curr.append(min(insert, delete, replace))
        prev = curr

    return prev[-1]

def normalizar_nombre(name):
    """Normaliza un nombre ordenando las palabras alfabéticamente"""
    words = name.lower().strip().split()
    return ' '.join(sorted(words))

def nombre_similar(name1, name2):
    """Calcula similitud basada en palabras en común"""
    words1 = set(name1.lower().strip().split())
    words2 = set(name2.lower().strip().split())
    common = words1 & words2
    total = words1 | words2
    if len(total) == 0:
        return 0
    return len(common) / len(total) * 100

def validarCuitDni(nombre_buscar, identificacion_buscar, cursor_busqueda):
    cursor_busqueda.execute("{CALL sp_buscar_cuit_dni_nombre (?,?)}", (nombre_buscar, identificacion_buscar))
    columns = [column[0] for column in cursor_busqueda.description]
    result = cursor_busqueda.fetchall()

    cuitDni = None
    if result and len(result) > 0:
        nombre_normalizado_input = normalizar_nombre(nombre_buscar)
        mejor_cuit = None
        mejor_distancia = float('inf')

        for r in result:
            row_dict = dict(zip(columns, r))
            cuit = row_dict['cuit']
            nombre = row_dict['nombre']
            if cuit is None or nombre is None:
                continue
            nombre_normalizado_db = normalizar_nombre(nombre)
            distancia = levenshtein(nombre_normalizado_input, nombre_normalizado_db)
            logging.debug(f"CANDIDATO_CUIT | nombre='{nombre}' cuit={cuit} distancia={distancia}")
            if distancia < mejor_distancia:
                mejor_distancia = distancia
                mejor_cuit = cuit

        if mejor_cuit is None:
            logging.warning("CUIT_SIN_RESULTADO | no se encontró coincidencia en padrón")
        elif mejor_distancia <= 3:
            cuitDni = mejor_cuit
            logging.info(f"CUIT_ASIGNADO | cuit={mejor_cuit} distancia={mejor_distancia}")
        else:
            logging.warning(f"CUIT_RECHAZADO | cuit={mejor_cuit} distancia={mejor_distancia} motivo=nombres_muy_diferentes")
    else:
        logging.warning("CUIT_SIN_RESULTADO | no se encontró coincidencia en padrón")

    return cuitDni

# Conexión a la base de datos
conn = conectar()
cursor = conn.cursor()
cursor_busqueda = conn.cursor()

## Traer registros de personas con cargo diferente a socio
cursor.execute("{CALL data_tablas_edictos_update (?)}", 'update_personas_no_socios')
rows = cursor.fetchall()

cantidad_registros = len(rows)
logging.info(f"PROCESO_INICIADO | proceso=update_personas_no_socios registros={cantidad_registros}")

registros_actualizados = 0
registros_saltados = 0

for row in rows:

    if not row.documento_soc:
        logging.warning(f"REGISTRO_OMITIDO | sin documento_soc id={row.id} id_fide={row.id_fide}")
        registros_saltados += 1
        continue

    logging.info(f"PROCESANDO | id={row.id} nombre='{row.apellido_nombre}'")
    
    cuitDni = None
    nombre = row.apellido_nombre 

    # Si el CUIT ya es válido, usarlo directo
    if cuit_valido(row.cuit):
        cuitDni = str(row.cuit)
        logging.info(f"CUIT_DIRECTO | id={row.id} cuit={cuitDni}")

    else:
        logging.debug(f"CUIT_INVALIDO | id={row.id} iniciando búsqueda")
        try:
            # Buscar por DNI
            if row.DNI:
                dni_limpio = ''.join(filter(str.isdigit, str(row.DNI)))
                logging.debug(f"BUSQUEDA_DNI | id={row.id} dni={dni_limpio} nombre='{row.apellido_nombre}'")
                cuitDni = validarCuitDni(row.apellido_nombre, dni_limpio, cursor_busqueda)

            # Fallback: buscar por CUIT si vino corto o mal cargado
            if not cuitDni and row.cuit:
                logging.debug(f"BUSQUEDA_CUIT_PARCIAL | id={row.id} cuit={row.cuit} nombre='{row.apellido_nombre}'")
                cuitDni = validarCuitDni(row.apellido_nombre, str(row.cuit), cursor_busqueda)
            
            # Resultado de la búsqueda
            if cuitDni:
                logging.info(f"CUIT_ENCONTRADO | id={row.id} cuit={cuitDni}")
            else:
                logging.warning(f"CUIT_NO_ENCONTRADO | id={row.id} nombre='{row.apellido_nombre}'")

        except Exception as e:
            logging.error(f"ERROR_BUSQUEDA | id={row.id} {type(e).__name__}: {e}")


    logging.debug(f"RESUMEN_REGISTRO | id={row.id} nombre='{row.apellido_nombre}' cuit_final={cuitDni or 'NO_ENCONTRADO'}")

    if cuitDni:
        params = (
        row.id,                                                        # @id
        row.id_fide,                                                   # @id_fide
        None,                                                          # @provincia_boletin
        None,                                                          # @provincia_nombre
        row.fecha_publicacion,                                         # @periodo
        row.documento_soc,                                             # @p_cuit
        cuitDni,                                                       # @p_cuit_socio
        row.apellido_nombre,                                           # @apellido_nombre
        row.domicilio,                                                 # @domicilio
        row.estado_civil,                                              # @estado_civil
        row.conyuge,                                                   # @conyuge
        row.codigo_cargo,                                              # @p_cargo
        "SI" if row.permanece_en_cargo and str(row.permanece_en_cargo).lower() in ["si", "sí"] else "NO",
        row.created_by,                                                # @user_id
        row.id_padron_sociedad,                                        # @id_padron_empresa
        row.id_padron_integrante                                       # @id_padron_socio
    )    
    
        cursor.execute("{CALL edictos_ia_procesar_directivos (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}", params)
        conn.commit()
        registros_actualizados += 1
        logging.info(f"REGISTRO_ACTUALIZADO | id={row.id} cuit={cuitDni}")
    else:
        registros_saltados += 1
        logging.warning(f"REGISTRO_OMITIDO | id={row.id} motivo=cuit_no_encontrado")
        continue
    
   

logging.info(f"PROCESO_FINALIZADO | proceso=update_personas_no_socios actualizados={registros_actualizados} omitidos={registros_saltados}")

cursor.close()
cursor_busqueda.close()
conn.close()