import json
import os
import logging
import pymysql
from logging.handlers import RotatingFileHandler
from datetime import datetime, timedelta
from dotenv import load_dotenv

from interaccion_planok import (
    crear_cliente_natural,
    buscar_cliente_natural,
    crear_seguimiento,
    obtener_id_medio_llegada,
    obtener_rut_vendedor_por_proyecto
)

# -------------------------------------------------------------------------
# CONFIGURACIÓN DE LOGGING OPTIMIZADA
# -------------------------------------------------------------------------
log_handler = RotatingFileHandler(
    filename='interaccion_incompletos.log',
    maxBytes=5 * 1024 * 1024,  # 5 MB
    backupCount=3,
    encoding='utf-8'
)

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[log_handler, logging.StreamHandler()]
)

# -------------------------------------------------------------------------
# CARGA DE VARIABLES DE ENTORNO
# -------------------------------------------------------------------------
load_dotenv()

DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_HOST = os.getenv('DB_HOST')
DB_NAME = os.getenv('DB_NAME')

TIPO_CONTACTOPOK = int(os.getenv("CONI_GESTIONADO", "0"))
ENVIAR_PLANOK = os.getenv("ENVIAR_PLANOK", "1")
PROYECTOS_ACTIVO_STR = os.getenv("PROYECTOS_ACTIVO", "").strip()

if PROYECTOS_ACTIVO_STR:
    try:
        PROYECTOS_ACTIVO = {
            int(x) for x in PROYECTOS_ACTIVO_STR.split(",") if x.strip().isdigit()
        }
        logging.debug(f"Proyectos activos cargados: {PROYECTOS_ACTIVO}")
    except ValueError as e:
        PROYECTOS_ACTIVO = set()
        logging.warning(f"Error en PROYECTOS_ACTIVO: {e}")
else:
    PROYECTOS_ACTIVO = set()
    logging.warning("Variable PROYECTOS_ACTIVO vacía.")

# -------------------------------------------------------------------------
# HELPERS GENERALES
# -------------------------------------------------------------------------
NULL_STRINGS = {'', 'null', 'none', 'nan', 'n/a', 'undefined'}


def clean_str(value, default=''):
    """
    Convierte None/'null'/'None'/etc. a default y devuelve string limpio.
    """
    if value is None:
        return default

    s = str(value).strip()
    if s.lower() in NULL_STRINGS:
        return default
    return s


def safe_int(value, default=0):
    """
    Convierte de forma segura a int.
    Soporta None, '', 'null', '3', '3.0', etc.
    """
    if value is None:
        return default

    s = str(value).strip().lower()
    if s in NULL_STRINGS:
        return default

    try:
        return int(float(s))
    except (ValueError, TypeError):
        return default


def clean_phone(value, default=''):
    """
    Deja solo dígitos del teléfono.
    """
    s = clean_str(value, default)
    return ''.join(ch for ch in s if ch.isdigit())


def clean_email(value, default=''):
    """
    Limpieza simple de correo.
    """
    email = clean_str(value, default)
    if not email:
        return default
    if '@' not in email:
        return default
    return email


def get_db_connection():
    return pymysql.connect(
        host=DB_HOST,
        user=DB_USER,
        password=DB_PASSWORD,
        database=DB_NAME,
        cursorclass=pymysql.cursors.DictCursor
    )


# -------------------------------------------------------------------------
# FUNCIONES AUXILIARES DE BASE DE DATOS
# -------------------------------------------------------------------------
def update_planok_response_on_interaction(wa_id, respuesta):
    """
    Guarda la respuesta de PlanOK en UserInteraction usando whatsapp_user_id.
    """
    try:
        connection = get_db_connection()
        with connection:
            with connection.cursor() as cursor:
                sql = """
                    UPDATE UserInteraction
                    SET planok_respuesta = %s
                    WHERE whatsapp_user_id = %s
                """
                cursor.execute(sql, (str(respuesta), wa_id))
            connection.commit()
    except Exception as e:
        logging.error(f"Error guardando planok_respuesta en UserInteraction: {e}")


def update_as_sent(whatsapp_user_id):
    """
    Marca el registro como enviado a CRM.
    """
    connection = get_db_connection()
    with connection:
        with connection.cursor() as cursor:
            sql = """
                UPDATE UserInteraction
                SET enviado_crm = 1,
                    fecha_envio_crm = NOW()
                WHERE whatsapp_user_id = %s
            """
            cursor.execute(sql, (whatsapp_user_id,))
        connection.commit()


def get_pending_incomplete_records():
    """
    Obtiene registros pendientes:
    - no enviados
    - dentro de últimas 48h
    - mayores a 25h o completos
    - sin otro ya enviado cerca en el tiempo para el mismo phone_norm_gen
    """
    try:
        connection = get_db_connection()
        with connection:
            with connection.cursor() as cursor:
                sql = """
                    SELECT *
                    FROM UserInteraction ui
                    WHERE (ui.enviado_crm IS NULL OR ui.enviado_crm = 0)
                      AND ui.fecha_registro >= NOW() - INTERVAL 48 HOUR
                      AND (
                            ui.fecha_registro <= NOW() - INTERVAL 25 HOUR
                            OR LOWER(TRIM(COALESCE(ui.estado_flujo, ''))) = 'completo'
                          )
                      AND NOT EXISTS (
                            SELECT 1
                            FROM UserInteraction x
                            WHERE x.phone_norm_gen = ui.phone_norm_gen
                              AND x.enviado_crm = 1
                              AND x.fecha_envio_crm >= DATE_SUB(ui.fecha_registro, INTERVAL 25 HOUR)
                      )
                    ORDER BY ui.fecha_registro ASC
                """
                cursor.execute(sql)
                records = cursor.fetchall()

                if records:
                    logging.info(f"Se recuperaron {len(records)} registros pendientes.")
                else:
                    logging.info("No se encontraron registros pendientes.")

                return records
    except pymysql.MySQLError as e:
        logging.error(f"Error DB al obtener pendientes: {e}")
        raise


# -------------------------------------------------------------------------
# LÓGICA PRINCIPAL DE PROCESAMIENTO
# -------------------------------------------------------------------------
def process_record(record):
    wa_id = clean_str(record.get('whatsapp_user_id'))

    try:
        user_data = {
            'whatsapp_user_id': wa_id,
            'whatsapp_phone': clean_phone(record.get('whatsapp_phone')),
            'source': clean_str(record.get('source')),
            'last_seen': record.get('last_seen'),
            'sessions': safe_int(record.get('sessions'), 0),
            'proyecto_id': safe_int(record.get('proyecto_id'), 0),
            'id_cliente': clean_str(record.get('crm_id_cliente')),
            'dni': clean_str(record.get('dni')),
            'digito_dni': clean_str(
                record.get('digitoverificador') or record.get('digito_dni')
            ),
            'nombre_cliente': clean_str(record.get('nombre_cliente')),
            'apellido_cliente': clean_str(record.get('apellido_cliente')),
            'mail': clean_email(record.get('mail')),
            'crm_email': clean_email(record.get('crm_email')),
            'estado_flujo': clean_str(record.get('estado_flujo')),
            'nivel_de_interes': safe_int(record.get('nivel_de_interes'), 3),
            'observacion_bot': clean_str(record.get('observacion_bot')),
            'crm_Rut_vendedor': clean_str(record.get('crm_Rut_vendedor')),
            'medio_llegada_id': safe_int(record.get('crm_medio_id'), 0),
            'rentadeclarada': clean_str(record.get('rentadeclarada')),
        }

        if not user_data['whatsapp_user_id'] or not user_data['whatsapp_phone']:
            msg = f"Registro inválido (sin whatsapp_user_id o whatsapp_phone). wa_id={wa_id}"
            logging.warning(msg)
            update_planok_response_on_interaction(wa_id, msg)
            return

        envio_exitoso, respuesta_msg = enviar_a_planok(user_data)

        if respuesta_msg:
            update_planok_response_on_interaction(wa_id, respuesta_msg)

        if envio_exitoso:
            update_as_sent(wa_id)
            logging.info(f"Procesado exitoso: {wa_id} | GCI ID: {respuesta_msg}")
        else:
            logging.warning(f"Proceso fallido/omitido para {wa_id}: {respuesta_msg}")

    except Exception as e:
        error_msg = f"Error script: {str(e)}"
        logging.exception(f"Error procesando {wa_id}: {e}")
        if wa_id:
            update_planok_response_on_interaction(wa_id, error_msg)


def enviar_a_planok(user_data):
    """
    Retorna:
    (True, seguimiento_id) si todo sale bien
    (False, motivo) si falla o se omite
    """
    if ENVIAR_PLANOK != "1":
        return False, "PlanOK desactivado"

    cliente_id_gci = None
    id_cliente = clean_str(user_data.get('id_cliente'))

    # ---------------------------------------------------------------------
    # 1) Determinar / buscar / crear cliente
    # ---------------------------------------------------------------------
    if id_cliente.isdigit() and int(id_cliente) > 0:
        cliente_id_gci = int(id_cliente)

    if not cliente_id_gci:
        dni = clean_str(user_data.get('dni'))
        digito_dni = clean_str(user_data.get('digito_dni'))
        nombre_cliente = clean_str(user_data.get('nombre_cliente'))
        apellido_cliente = clean_str(user_data.get('apellido_cliente'))
        whatsapp_phone = clean_phone(user_data.get('whatsapp_phone'))
        email_cliente = clean_email(user_data.get('mail') or user_data.get('crm_email'))

        if not dni.isdigit():
            return False, f"DNI inválido para crear/buscar cliente: '{dni}'"

        try:
            cliente_id_gci = buscar_cliente_natural(int(dni))
        except Exception as e:
            return False, f"Error buscando cliente natural DNI {dni}: {str(e)}"

        if not cliente_id_gci:
            if not digito_dni:
                return False, f"DV vacío para DNI {dni}"

            if not nombre_cliente:
                return False, f"Nombre vacío para DNI {dni}"

            if not apellido_cliente:
                return False, f"Apellido vacío para DNI {dni}"

            if not whatsapp_phone:
                return False, f"Celular vacío para DNI {dni}"

            payload_debug = {
                'identificador_personal': int(dni),
                'digito_verificador': digito_dni,
                'nombre': nombre_cliente,
                'apellido_paterno': apellido_cliente,
                'apellido_materno': '',
                'celular': whatsapp_phone,
                'email': email_cliente
            }

            logging.info(f"Intentando crear cliente natural con payload: {payload_debug}")

            try:
                cliente_id_gci = crear_cliente_natural(
                    identificador_personal=int(dni),
                    digito_verificador=digito_dni,
                    nombre=nombre_cliente,
                    apellido_paterno=apellido_cliente,
                    apellido_materno='',
                    celular=whatsapp_phone,
                    email=email_cliente
                )
            except Exception as e:
                logging.error(f"Fallo crear cliente natural. Payload: {payload_debug}")
                return False, f"Excepción al crear cliente natural: {str(e)}"

            if cliente_id_gci:
                logging.info(f"Cliente creado correctamente: {cliente_id_gci}")
            else:
                return False, f"Fallo crear cliente DNI: {dni}"

    # ---------------------------------------------------------------------
    # 2) Validar proyecto
    # ---------------------------------------------------------------------
    proyecto_id = safe_int(user_data.get('proyecto_id'), 0)

    if proyecto_id <= 0:
        return False, f"Proyecto inválido: {proyecto_id}"

    if proyecto_id not in PROYECTOS_ACTIVO:
        return False, f"Proyecto {proyecto_id} inactivo"

    if not cliente_id_gci:
        return False, "Sin ID Cliente GCI"

    # ---------------------------------------------------------------------
    # 3) Vendedor
    # ---------------------------------------------------------------------
    rutvendedor_dni = clean_str(user_data.get('crm_Rut_vendedor'))
    if not rutvendedor_dni:
        try:
            rutvendedor_dni = obtener_rut_vendedor_por_proyecto(proyecto_id)
            rutvendedor_dni = clean_str(rutvendedor_dni)
        except Exception as e:
            logging.warning(f"No se pudo obtener rut vendedor para proyecto {proyecto_id}: {e}")
            rutvendedor_dni = ''

    # ---------------------------------------------------------------------
    # 4) Medio de llegada
    # ---------------------------------------------------------------------
    id_medio_cf = safe_int(user_data.get('medio_llegada_id'), 0)

    # Si quieres fallback automático por proyecto, aquí lo puedes habilitar.
    # Por ahora respetamos tu lógica actual: usa crm_medio_id si viene.
    if id_medio_cf <= 0:
        logging.warning(
            f"medio_llegada_id inválido o vacío para cliente {cliente_id_gci}, proyecto {proyecto_id}"
        )

    # ---------------------------------------------------------------------
    # 5) Comentario / expectativa / recontacto
    # ---------------------------------------------------------------------
    fecha_recontacto = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
    comentario_ad = clean_str(user_data.get('observacion_bot'))
    if not comentario_ad:
        comentario_ad = "Gestión ConiLead, cliente no contestó."

    nivel_de_interes = safe_int(user_data.get('nivel_de_interes'), 3)
    if nivel_de_interes <= 0:
        nivel_de_interes = 3

    # ---------------------------------------------------------------------
    # 6) Crear seguimiento
    # ---------------------------------------------------------------------
    try:
        payload_seguimiento = {
            'id_cliente': cliente_id_gci,
            'id_proyecto': proyecto_id,
            'id_medio': id_medio_cf,
            'id_tipo_contacto': TIPO_CONTACTOPOK,
            'id_expectativa': nivel_de_interes,
            'comentario': comentario_ad,
            'fecha_recontacto_str': fecha_recontacto,
            'mantener_ejecutivo': False,
            'rutvendedor': rutvendedor_dni
        }

        logging.info(f"Intentando crear seguimiento con payload: {payload_seguimiento}")

        seguimiento_id = crear_seguimiento(
            id_cliente=cliente_id_gci,
            id_proyecto=proyecto_id,
            id_medio=id_medio_cf,
            id_tipo_contacto=TIPO_CONTACTOPOK,
            id_expectativa=nivel_de_interes,
            comentario=comentario_ad,
            fecha_recontacto_str=fecha_recontacto,
            mantener_ejecutivo=False,
            rutvendedor=rutvendedor_dni
        )

        if seguimiento_id:
            return True, str(seguimiento_id)

        logging.error(
            f"Fallo crear seguimiento (Cli: {cliente_id_gci}, Proy: {proyecto_id}, Medio: {id_medio_cf})"
        )
        return False, "API no devolvió ID seguimiento"

    except Exception as e:
        return False, f"Excepción API seguimiento: {str(e)}"


def process_records():
    records = get_pending_incomplete_records()
    if not records:
        return

    processed_wa_ids = set()

    for record in records:
        wa_id = clean_str(record.get('whatsapp_user_id'))
        if not wa_id:
            continue

        # Evita duplicar dentro de una misma corrida
        if wa_id in processed_wa_ids:
            logging.info(f"Registro duplicado en misma corrida, se omite: {wa_id}")
            continue

        processed_wa_ids.add(wa_id)
        process_record(record)


if __name__ == '__main__':
    process_records()