#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import json
import sys
import uuid
import logging
import pymysql
import requests
import re
import argparse
from datetime import datetime, date, timedelta
from dotenv import load_dotenv
from interaccion_planok import get_cached_token, patch_lead_gci
#from integraciones_gpt import nombre_arreglado
from functools import lru_cache
import math
import urllib.parse
# ─────────────────────────────────────────────────────────────
# Config
# ─────────────────────────────────────────────────────────────
load_dotenv()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

DB_CONFIG = {
    'host': os.getenv('DB_HOST'),
    'user': os.getenv('DB_USER'),
    'password': os.getenv('DB_PASSWORD'),
    'database': os.getenv('DB_NAME'),
    'cursorclass': pymysql.cursors.DictCursor
}

DB_CONFIG_CRM = {
    'host': os.getenv('DB_HOST_2'),
    'user': os.getenv('DB_USER_2'),
    'password': os.getenv('DB_PASSWORD_2'),
    'database': os.getenv('DB_NAME_2'),
    'cursorclass': pymysql.cursors.DictCursor
}

MANTENER_SEGUNDO = {"maria", "maría", "juan", "josé", "jose", "luis"}

LOOKBACK_HOURS = int(os.getenv("LEADS_LOOKBACK_HOURS", "24"))

RUN_PATCH_GCI = (os.getenv("RUN_PATCH_GCI", "0").lower() in ("1", "true", "yes"))
RUN_CREAR_CONTACTO = (os.getenv("RUN_CREAR_CONTACTO", "0").lower() in ("1", "true", "yes"))

# Base URL (sin /api/login ni querystrings)
API_BASE = os.getenv('GCI_API_BASE', 'https://api-gci-rest.integracionplanok.io').rstrip('/')
LEADS_URL = f"{API_BASE}/api/leads"

LEAD_ENTRADA_DIR = os.getenv('LEAD_ENTRADA_DIR', 'lead_entrada')
COT_LIST_URL = f"{API_BASE}/api/cotizaciones/listar"
MEDIO_CLIENTE_URL_TMPL = f"{API_BASE}/api/proyectos/{{id_proyecto}}/clientes/{{id_cliente}}/mediosDeLlegada"
VEND_BY_CLIENTE_URL = f"{API_BASE}/api/clientes/vendedor-by-cliente"


PROYECTOS_ACTIVO_STR = os.getenv("PROYECTOS_ACTIVO", "")  # "1,2,10,13"
PROYECTOS_ACTIVO = set()
if PROYECTOS_ACTIVO_STR.strip():
    try:
        PROYECTOS_ACTIVO = {int(x) for x in PROYECTOS_ACTIVO_STR.split(",")}
    except ValueError:
        logging.warning(f"PROYECTOS_ACTIVO contiene valores no enteros: {PROYECTOS_ACTIVO_STR}")

# Ruta a Python y script externo (ajusta a tu server si lo usas)
VENV_PYTHON = os.getenv("VENV_PYTHON")
CREAR_CONTACTO_PATH = os.getenv("CREAR_CONTACTO_PATH")
# ─────────────────────────────────────────────────────────────
# Utilidades
# ─────────────────────────────────────────────────────────────
def _api_session():
    token = get_cached_token()
    s = requests.Session()
    s.headers.update({"Authorization": f"Bearer {token}"})
    return s
    
def log_simple(id_centralizador):
    """Log plano que siempre se guarda: YYYY-mm-dd HH:MM:SS - ID"""
    try:
        with open("cron_simple.log", "a", encoding="utf-8") as f:
            f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {id_centralizador}\n")
    except Exception as e:
        logging.error(f"No se pudo escribir en log_simple: {e}")

def _titlecase_es(s: str) -> str:
    s = s.strip()
    # Título simple respetando acentos; maneja partículas comunes
    partes = re.split(r"\s+", s.lower())
    keep_lower = {"de", "del", "la", "las", "los", "y", "da", "dos", "do", "das"}
    out = []
    for i, p in enumerate(partes):
        if i > 0 and p in keep_lower:
            out.append(p)
        else:
            out.append(p[:1].upper() + p[1:])
    return " ".join(out)

@lru_cache(maxsize=10000)
def normalizar_nombre_apellido(full: str):
    """
    - Si viene 'Apellido, Nombre' → respeta eso.
    - Si viene 'Nombre [Segundo] Apellido[s]' → apellido = último token, nombre = primero (y segundo si regla MANTENER_SEGUNDO).
    - Si es una sola palabra → nombre=palabra, apellido="".
    - Titlecase con partículas.
    """
    if not full or not isinstance(full, str):
        return "", ""

    t = " ".join(full.split()).strip()
    if not t:
        return "", ""

    # Caso "Apellido, Nombre"
    if "," in t:
        ap, nom = [s.strip() for s in t.split(",", 1)]
        return _titlecase_es(nom), _titlecase_es(ap)

    # Sin coma: último token = apellido
    partes = t.split(" ")
    if len(partes) == 1:
        return _titlecase_es(partes[0]), ""

    apellido = partes[-1]
    # nombre = 1er token (+ 2do si coincide regla)
    nombre_tokens = [partes[0]]
    if len(partes) >= 3 and partes[0].lower() in MANTENER_SEGUNDO:
        # p.ej. "María José", "Juan Pablo", "José Luis"
        nombre_tokens.append(partes[1])

    nombre = " ".join(nombre_tokens)
    return _titlecase_es(nombre), _titlecase_es(apellido)

def pad(n: int) -> str:
    return str(n).zfill(2)

def now_timestamp() -> str:
    now = datetime.now()
    return f"{now.year}-{pad(now.month)}-{pad(now.day)} {pad(now.hour)}:{pad(now.minute)}:{pad(now.second)}"

def split_fecha_hora(iso_str: str):
    """Devuelve (fecha, hora) a partir de un ISO u otros formatos comunes."""
    if not iso_str:
        return "", ""
    try:
        d = datetime.fromisoformat(iso_str.replace("Z", "+00:00"))
    except ValueError:
        try:
            d = datetime.strptime(iso_str, "%Y-%m-%d %H:%M:%S")
        except ValueError:
            return "", ""
    fecha = f"{d.year}-{pad(d.month)}-{pad(d.day)}"
    hora  = f"{pad(d.hour)}:{pad(d.minute)}:{pad(d.second)}"
    return fecha, hora

def split_nombre(full: str):
    """Separa nombre/apellido. Si hay coma usa 'Apellido, Nombre'; si no, último token = apellido."""
    if not full or not isinstance(full, str):
        return "", ""
    t = " ".join(full.strip().split())
    if not t:
        return "", ""
    if "," in t:
        ap, nom = [s.strip() for s in t.split(",", 1)]
        return nom, ap
    parts = t.split(" ")
    if len(parts) == 1:
        return parts[0], ""
    apellido = parts[-1]
    nombre = " ".join(parts[:-1])
    return nombre, apellido

def fetch_leads_last_hours(horas=LOOKBACK_HOURS):
    token = get_cached_token()
    headers = {"Authorization": f"Bearer {token}"}

    hasta = datetime.now()
    desde = hasta - timedelta(hours=horas)


    params = {
        "fechaInicio": desde.strftime("%Y-%m-%d"),
        "fechaFin": hasta.strftime("%Y-%m-%d"),
    }
    logging.info(f"GET {LEADS_URL} params={params}")
    r = requests.get(LEADS_URL, headers=headers, params=params, timeout=60)
    r.raise_for_status()

    data = r.json()
    return data if isinstance(data, list) else data.get('items', [])

def guardar_json_crudo(nombre_archivo: str, data):
    """Guarda el JSON crudo de la corrida para trazabilidad."""
    try:
        if not os.path.exists(LEAD_ENTRADA_DIR):
            os.makedirs(LEAD_ENTRADA_DIR, exist_ok=True)
        filepath = os.path.join(LEAD_ENTRADA_DIR, nombre_archivo)
        with open(filepath, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        logging.info(f"Raw guardado: {filepath}")
    except Exception as e:
        logging.error(f"Error guardando raw: {e}")

def get_existing_centralizador_ids():
    """Consulta IDs ya insertados para evitar duplicados (normalizados)."""
    connection = pymysql.connect(**DB_CONFIG)
    with connection:
        with connection.cursor() as cursor:
            cursor.execute("SELECT TRIM(ID_centralizador) AS ID_centralizador FROM Lead_entrantes")
            rows = cursor.fetchall()
            # normalizamos todo a str + strip
            return {str(row['ID_centralizador']).strip() for row in rows}


def insert_data_to_db(records):
    """Inserta registros nuevos en Lead_entrantes, evitando duplicados por ID_centralizador
    sin requerir índice único (usa INSERT ... WHERE NOT EXISTS)."""

    if not records:
        return

    connection = pymysql.connect(**DB_CONFIG)
    inserted = 0
    with connection:
        with connection.cursor() as cursor:
            sql = """
            INSERT INTO Lead_entrantes (
                Timestamp, Now, Fecha_ingreso, Hora_ingreso, ID_centralizador, Estado, ID_cliente,
                Rut_cliente, Nombre_cliente, Apellido_cliente, Numero_telefono_fijo_cliente,
                Numero_telefono_movil_cliente, Email_cliente, Medio_llegada, Via_ingreso,
                Proyecto, ID_proyecto, Etapa, Producto, Tipologia, Nombre_vendedor,
                Telefono_vendedor, Email_vendedor, Rut_vendedor, ID_medio_llegada
            )
            SELECT
                %(Timestamp)s, %(Now)s, %(Fecha_ingreso)s, %(Hora_ingreso)s, %(ID_centralizador)s, %(Estado)s, %(ID_cliente)s,
                %(Rut_cliente)s, %(Nombre_cliente)s, %(Apellido_cliente)s, %(Numero_telefono_fijo_cliente)s,
                %(Numero_telefono_movil_cliente)s, %(Email_cliente)s, %(Medio_llegada)s, %(Via_ingreso)s,
                %(Proyecto)s, %(ID_proyecto)s, %(Etapa)s, %(Producto)s, %(Tipologia)s, %(Nombre_vendedor)s,
                %(Telefono_vendedor)s, %(Email_vendedor)s, %(Rut_vendedor)s, %(ID_medio_llegada)s
            FROM DUAL
            WHERE NOT EXISTS (
                SELECT 1 FROM Lead_entrantes
                WHERE ID_centralizador = %(ID_centralizador)s
            )
            """
            for r in records:
                # normaliza ID_medio_llegada a int
                try:
                    r['ID_medio_llegada'] = int(r.get('ID_medio_llegada') or 0)
                except (TypeError, ValueError):
                    r['ID_medio_llegada'] = 0

                cursor.execute(sql, r)
                # rowcount será 1 si insertó, 0 si ya existía
                inserted += cursor.rowcount

            connection.commit()

    saltados = len(records) - inserted
    logging.info(f"Insertados {inserted} nuevos; saltados {saltados} por ID_centralizador existente.")


def transform_lead_to_record(lead: dict) -> dict:
    """Mapeo EXACTO a tu esquema final, SIN llamadas a OpenAI para nombre/apellido."""
    # Nombre y apellido locales (rápidos)
    nombre_norm, apellido_norm = normalizar_nombre_apellido(lead.get('nombre', '') or '')

    # Fecha/hora
    f, h = split_fecha_hora(lead.get('fecha_ingreso'))

    record = {
        # Campos "centrales"
        'ID_centralizador':      str(lead.get('id_centralizador', '') or lead.get('id', '')),
        'Fecha_ingreso':         f,
        'Hora_ingreso':          h,
        'ID_cliente':            str(lead.get('id_cliente', '') or ''),
        'Rut_cliente':           lead.get('rut', '') or lead.get('rut_cliente', ''),

        # Nombre y apellido (local, sin API)
        'Nombre_cliente':        nombre_norm,
        'Apellido_cliente':      apellido_norm,

        # Contacto cliente
        'Numero_telefono_fijo_cliente':   lead.get('telefono_fijo', '') or '',
        'Numero_telefono_movil_cliente':  lead.get('telefono_movil', '') or '',
        'Email_cliente':                  lead.get('email', '') or '',
        'phone_norm_gen':                 lead.get('phone_norm_gen', '') or '',

        # Origen
        'Medio_llegada':         lead.get('medio_llegada', '') or '',
        'Via_ingreso':           lead.get('via_ingreso', '') or '',

        # Proyecto / producto
        'Proyecto':              lead.get('proyecto', '') or '',
        'ID_proyecto':           str(lead.get('id_proyecto', '') or ''),
        'Producto':              lead.get('producto', '') or '',

        # Vendedor
        'Nombre_vendedor':       lead.get('vendedor', '') or '',
        'Telefono_vendedor':     lead.get('vendedor_telefono', '') or '',
        'Email_vendedor':        lead.get('correo_vendedor', '') or '',
        'Rut_vendedor':          lead.get('rut_usuario', '') or '',

        # Fijos/derivados
        'Tipologia':             "",
        'Etapa':                 "",
        'Timestamp':             now_timestamp(),
        'Now':                   now_timestamp(),
        'Estado':                lead.get('estado', 'Pendiente'),

        # Medio llegada ID
        'ID_medio_llegada':      lead.get('id_medio_llegada', 0) or 0,
    }
    return record



def maybe_patch_and_external(id_centralizador: str, id_proyecto: str):
    """Solo PATCH opcional (crear_contacto se ejecuta una vez al final)."""
    try:
        ip = int(id_proyecto)
    except (ValueError, TypeError):
        ip = None

    if RUN_PATCH_GCI and ip and ip in PROYECTOS_ACTIVO:
        try:
            patch_lead_gci(id_centralizador)
            logging.info(f"PATCH GCI enviado → ID_centralizador={id_centralizador}")
        except Exception as e:
            logging.error(f"Error PATCH GCI: {e}")
    else:
        if not RUN_PATCH_GCI:
            logging.debug("RUN_PATCH_GCI=0 → omite PATCH GCI.")
        elif ip not in PROYECTOS_ACTIVO:
            logging.info(f"ID_proyecto {ip} no está en PROYECTOS_ACTIVO; omite PATCH.")

def run_crear_contacto_once():
    if not RUN_CREAR_CONTACTO:
        logging.debug("RUN_CREAR_CONTACTO=0 → omite crear_contacto.py.")
        return
    try:
        import subprocess, shlex
        cmd = [VENV_PYTHON, CREAR_CONTACTO_PATH]
        logging.info(f"Ejecutando crear_contacto.py una sola vez: {shlex.join(cmd)}")
        result = subprocess.run(
            cmd, check=True, capture_output=True, text=True, encoding='utf-8',
            env={**os.environ, "PYTHONIOENCODING": "utf-8"}
        )
        if result.stdout:
            logging.info(f"crear_contacto.py STDOUT: {result.stdout.strip()}")
        if result.stderr:
            logging.warning(f"crear_contacto.py STDERR: {result.stderr.strip()}")
    except Exception as e:
        logging.error(f"Error al ejecutar crear_contacto.py: {e}")


def parse_args():
    p = argparse.ArgumentParser(description="Cron GCI: leads y cotizaciones")
    sub = p.add_subparsers(dest="cmd")

    # defaults: AYER–HOY
    hoy = date.today()
    #ayer = hoy - timedelta(days=1)
    
    hoy = hoy - timedelta(days=1)
    ayer = hoy - timedelta(days=2)    
    
    
    ayer_str = ayer.strftime("%Y-%m-%d")
    hoy_str = hoy.strftime("%Y-%m-%d")


    sub.add_parser("test", help="Test de leads (1 registro, sin insertar)")

    # test 1 cotización (rango opcional; por defecto AYER–HOY)
    p_ct = sub.add_parser("c-test", help="Test 1 cotización (sin insertar)")
    p_ct.add_argument("desde", nargs="?", default=ayer_str, help=f"Default: {ayer_str}")
    p_ct.add_argument("hasta", nargs="?", default=hoy_str, help=f"Default: {hoy_str}")

    # importar cotizaciones (rango opcional; por defecto AYER–HOY)
    p_ci = sub.add_parser("c-import", help="Importar cotizaciones a Lead_entrantes")
    p_ci.add_argument("desde", nargs="?", default=ayer_str, help=f"Default: {ayer_str}")
    p_ci.add_argument("hasta", nargs="?", default=hoy_str, help=f"Default: {hoy_str}")

    return p.parse_args()

def fetch_cotizaciones(origen="Web", fecha_inicio=None, fecha_fin=None, limite=50, pagina_inicial=1):
    """
    Itera paginado sobre /api/cotizaciones/listar.
    - Si el rango no tiene cotizaciones, la API puede devolver 404 con
      {"message":"Número de página superior al disponible"} → devolvemos [].
    """
    s = _api_session()
    out = []
    page = pagina_inicial

    while True:
        params = {
            "origen": origen,
            "fechaInicio": fecha_inicio,
            "fechaFin": fecha_fin,
            "pagina": page,
            "limite": limite,  # opcional, por si quieres menos páginas
        }
        r = s.get(COT_LIST_URL, params=params, timeout=60)

        if r.status_code == 404:
            # Cuando no hay páginas disponibles para ese rango
            msg = ""
            try:
                msg = (r.json() or {}).get("message", "")
            except Exception:
                msg = r.text
            if "Número de página superior al disponible" in (msg or ""):
                logging.info(f"cotizaciones: rango sin resultados (404 en p{page}). Devuelvo lista vacía.")
                return out  # [] si aún no había nada; o lo acumulado si empezó en otra página
            # si es otro 404, tratamos como error real
            logging.error(f"Fallo cotizaciones/listar p{page}: 404 | {msg[:400]}")
            r.raise_for_status()

        if not r.ok:
            logging.error(f"Fallo cotizaciones/listar p{page}: {r.status_code} {r.reason} | {r.text[:400]}")
            r.raise_for_status()

        js = r.json() or {}
        cots = js.get("cotizaciones", [])
        out.extend(cots)

        pag = js.get("paginacion") or {}
        total = pag.get("totalCotizaciones")
        tot_pages = pag.get("totalPaginas")

        # Logs informativos
        if total is not None and tot_pages is not None:
            logging.info(f"cotizaciones p{page}/{tot_pages}, acum={len(out)}/{total}")
        else:
            logging.info(f"cotizaciones p{page}, acum={len(out)}")

        # cortar si no hay más páginas
        if not tot_pages or page >= tot_pages:
            break
        page += 1

    return out


def fetch_medio_cliente(id_proyecto: int, id_cliente: int):
    """GET /api/proyectos/{id_proyecto}/clientes/{id_cliente}/mediosDeLlegada → (id, glosa)"""
    s = _api_session()
    url = MEDIO_CLIENTE_URL_TMPL.format(id_proyecto=id_proyecto, id_cliente=id_cliente)
    r = s.get(url, timeout=30)
    if not r.ok:
        logging.warning(f"Medio cliente fallo (p{ id_proyecto }/c{ id_cliente }): {r.status_code} | {r.text[:200]}")
        return None, None
    js = r.json() or {}
    return js.get("idUltimoMedioLlegada"), js.get("glosaUltimoMedioLlegada")

def fetch_vendedor_por_cliente(id_cliente: int, id_proyecto: int):
    """
    GET /api/clientes/vendedor-by-cliente?identificador=id cliente&valorIdentificador=...&tipoCliente=Natural&idProyecto=...
    OJO: identificador debe ir como 'id cliente' (un espacio) — lo enviamos en params para que se encodee bien.
    """
    s = _api_session()
    params = {
        "identificador": "id cliente",     # el espacio se encodea como %20 (no %2520)
        "valorIdentificador": id_cliente,
        "tipoCliente": "Natural",
        "idProyecto": id_proyecto
    }
    r = s.get(VEND_BY_CLIENTE_URL, params=params, timeout=30)
    if not r.ok:
        logging.warning(f"vendedor-by-cliente fallo (p{ id_proyecto }/c{ id_cliente }): {r.status_code} | {r.text[:200]}")
        return {}
    arr = r.json() or []
    if not isinstance(arr, list) or not arr:
        return {}
    vendedor = (arr[0] or {}).get("vendedor") or {}
    return vendedor.get("vendedor_seguimiento_reciente") or {}

def transform_cotizacion_to_record(cot: dict) -> dict:
    """
    Match solicitado:
    - ID centralizador ← cot.id
    - Fecha/Hora ingreso ← cot.fechaCotizacion
    - Cliente.* ← cot.cliente
    - Proyecto/Etapa/Producto/Tipología ← cot.productoPrincipal
    - Estado ← 'Bueno'
    - Medio/Vía/ID_medio ← luego de enriquecer
    - Vendedor.* ← luego de enriquecer
    """
    # fecha/hora
    fecha_iso = cot.get("fechaCotizacion")
    fecha, hora = split_fecha_hora(fecha_iso)

    cli = cot.get("cliente") or {}
    prod = cot.get("productoPrincipal") or {}
    proy = (prod.get("proyecto") or {})
    etapa = (prod.get("etapa") or {})

    # base record (sin medio ni vendedor aún)
    rec = {
        "ID_centralizador": str(cot.get("id") or ""),
        "Fecha_ingreso": fecha,
        "Hora_ingreso": hora,
        "ID_cliente": str(cli.get("id") or ""),
        "Rut_cliente": cli.get("rut") or "",
        "Nombre_cliente": cli.get("nombre") or "",
        "Apellido_cliente": cli.get("apellidoP") or "",
        "Numero_telefono_fijo_cliente": cli.get("fonoFijo") or "",
        "Numero_telefono_movil_cliente": cli.get("celular") or "",
        "phone_norm_gen": cli.get("phone_norm_gen") or "",
        "Email_cliente": cli.get("correo") or "",
        "Proyecto": proy.get("glosa") or "",
        "ID_proyecto": str(proy.get("id") or ""),
        "Producto": str(prod.get("glosa") or ""),
        "Tipologia": prod.get("programa") or "",    # ← Tipología desde programa
        "Etapa": etapa.get("glosa") or "",
        "Timestamp": now_timestamp(),
        "Now": now_timestamp(),
        "Estado": "Bueno",
        # Por completar:
        "Medio_llegada": "",
        "Via_ingreso": "",
        "Nombre_vendedor": "",
        "Telefono_vendedor": "",
        "Email_vendedor": "",
        "Rut_vendedor": "",
        "ID_medio_llegada": 0,
    }
    return rec

def enriquecer_con_medio_y_vendedor(rec: dict):
    """Rellena Medio/Vía/ID_medio y datos del vendedor en el record."""
    try:
        id_proy = int(rec.get("ID_proyecto") or 0)
        id_cli = int(rec.get("ID_cliente") or 0)
    except ValueError:
        id_proy = 0
        id_cli = 0

    # Medio de llegada
    if id_proy and id_cli:
        id_medio, glosa_medio = fetch_medio_cliente(id_proy, id_cli)
        if id_medio:
            rec["ID_medio_llegada"] = int(id_medio)
        if glosa_medio:
            rec["Medio_llegada"] = glosa_medio
            rec["Via_ingreso"] = glosa_medio  # según tu match: misma glosa
    # Vendedor
    vend = fetch_vendedor_por_cliente(id_cli, id_proy) if (id_proy and id_cli) else {}
    if vend:
        rec["Nombre_vendedor"] = vend.get("nombre_vendedor_seguimiento", "") or ""
        rec["Telefono_vendedor"] = vend.get("telefono_vendedor_seguimiento", "") or ""
        rec["Email_vendedor"] = vend.get("email_vendedor_seguimiento", "") or ""
        rec["Rut_vendedor"] = vend.get("rut_largo_vendedor_seguimiento", "") or ""

def importar_cotizaciones_a_leads(fecha_inicio: str, fecha_fin: str, origen="Web"):
    """
    1) lista cotizaciones (rango + origen),
    2) enriquece cada una,
    3) inserta solo si ID_centralizador no existe,
    4) NO PATCH (regla solicitada),
    5) sí ejecutar Chatfuel (crear_contacto.py) una vez si hubo inserts.
    """
    cots = fetch_cotizaciones(origen=origen, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin)
    logging.info(f"Cotizaciones recibidas: {len(cots)}")

    existentes = get_existing_centralizador_ids()
    nuevos = []
    for cot in cots:
        rec = transform_cotizacion_to_record(cot)
        idc = rec["ID_centralizador"]
        if not idc:
            continue
        if idc in existentes:
            continue
        enriquecer_con_medio_y_vendedor(rec)
        # normaliza ID_medio_llegada antes de insertar
        try:
            rec["ID_medio_llegada"] = int(rec.get("ID_medio_llegada") or 0)
        except (TypeError, ValueError):
            rec["ID_medio_llegada"] = 0
        nuevos.append(rec)


    if nuevos:
        insert_data_to_db(nuevos)
        for rec in nuevos:
            log_simple(rec["ID_centralizador"])
            #maybe_patch_and_external(rec["ID_centralizador"], rec["ID_proyecto"])
        return len(nuevos)
    else:
        logging.info("No hay cotizaciones nuevas para insertar.")
        return 0


def test_cotizacion_uno(fecha_inicio: str, fecha_fin: str, origen="Web"):
    """Test: muestra 1 cotización original + record enriquecido (sin insertar)."""
    cots = fetch_cotizaciones(origen=origen, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin)
    if not cots:
        print("No hay cotizaciones en el rango.")
        return
    cot = cots[0]
    rec = transform_cotizacion_to_record(cot)
    enriquecer_con_medio_y_vendedor(rec)
    print("COT (original):")
    print(json.dumps(cot, indent=2, ensure_ascii=False))
    print("\nRECORD (transformado+enriquecido):")
    print(json.dumps(rec, indent=2, ensure_ascii=False))
# ─────────────────────────────────────────────────────────────
# Flujos
# ─────────────────────────────────────────────────────────────

def main():
    # 1) Traer leads
    leads = fetch_leads_last_hours()
    logging.info(f"Leads recibidos últimas {LOOKBACK_HOURS}h: {len(leads)}")

    existentes = get_existing_centralizador_ids()
    nuevos_records = []
    ids_a_procesar = []

    for lead in leads:
        rec = transform_lead_to_record(lead)
        id_cent = (rec.get('ID_centralizador') or "").strip()
        if not id_cent:
            logging.warning(f"Lead sin ID_centralizador. Salta: {lead}")
            continue
        if id_cent in existentes:
            continue
        nuevos_records.append(rec)
        ids_a_procesar.append((id_cent, rec.get('ID_proyecto')))

    inserted_count = 0
    if nuevos_records:
        antes = datetime.now()
        insert_data_to_db(nuevos_records)
        despues = datetime.now()
        inserted_count = len(nuevos_records)

        for id_centralizador, id_proyecto in ids_a_procesar:
            log_simple(id_centralizador)
            #maybe_patch_and_external(id_centralizador, id_proyecto)

        logging.info(f"Inserción + PATCH tomó {(despues-antes).total_seconds():.2f}s")
    else:
        logging.info("No hay leads nuevos para insertar.")

    inserted_leads = inserted_count  # lo que ya calculas
    inserted_cots = 0
    #replicamos a n8n
    replicar_leads_a_crm()
    
    LEER_COTIZADORWEB = (os.getenv("LEER_COTIZADORWEB", "0").lower() in ("1", "true", "yes"))
    if LEER_COTIZADORWEB:
        logging.info("Leyendo cotizaciones desde Cotizador Web...")
        hoy = date.today()
        ayer = hoy - timedelta(days=1)
        inserted_cots = importar_cotizaciones_a_leads(ayer.strftime("%Y-%m-%d"), hoy.strftime("%Y-%m-%d"))
    else:
        logging.info("LEER_COTIZADORWEB=0 → omite lectura de cotizaciones.")

    if (inserted_leads > 0) or (inserted_cots > 0):
        replicar_leads_a_crm()
        run_crear_contacto_once()


def test_one():
    """Muestra en consola un solo registro (original + transformado), sin DB ni PATCH."""
    leads = fetch_leads_last_hours()
    if not leads:
        logging.info("No hay leads para hoy.")
        return
    lead = leads[0]  # primer registro
    rec = transform_lead_to_record(lead)
    logging.info("Lead original desde API:")
    print(json.dumps(lead, indent=2, ensure_ascii=False))
    logging.info("Lead transformado al esquema DB:")
    print(json.dumps(rec, indent=2, ensure_ascii=False))


#---N8N

SQL_INSERT_CRM = """
INSERT INTO Lead_entrantes_crm (
    Timestamp, Now, Fecha_ingreso, Hora_ingreso,
    ID_centralizador, Estado,
    ID_cliente, Rut_cliente,
    Nombre_cliente, Apellido_cliente,
    Numero_telefono_fijo_cliente, Numero_telefono_movil_cliente,
    phone_norm_gen, Email_cliente,
    Medio_llegada, Via_ingreso,
    Proyecto, ID_proyecto, Etapa, Producto, Tipologia,
    Nombre_vendedor, Telefono_vendedor, Email_vendedor, Rut_vendedor,
    ID_medio_llegada,
    empresa_nombre, id_proyecto_n8n
)
VALUES (
    %(Timestamp)s, %(Now)s, %(Fecha_ingreso)s, %(Hora_ingreso)s,
    %(ID_centralizador)s, %(Estado)s,
    %(ID_cliente)s, %(Rut_cliente)s,
    %(Nombre_cliente)s, %(Apellido_cliente)s,
    %(Numero_telefono_fijo_cliente)s, %(Numero_telefono_movil_cliente)s,
    %(phone_norm_gen)s, %(Email_cliente)s,
    %(Medio_llegada)s, %(Via_ingreso)s,
    %(Proyecto)s, %(ID_proyecto)s, %(Etapa)s, %(Producto)s, %(Tipologia)s,
    %(Nombre_vendedor)s, %(Telefono_vendedor)s, %(Email_vendedor)s, %(Rut_vendedor)s,
    %(ID_medio_llegada)s,
    %(empresa_nombre)s, %(id_proyecto_n8n)s
)
"""

def replicar_leads_a_crm():
    """
    Replica a Lead_entrantes_crm SOLO:
    - registros de las últimas 24h (Lead_entrantes.Now)
    - que NO existan aún en CRM por (ID_centralizador + id_proyecto_n8n)
    """

    empresa = os.getenv("EMPRESA_NOMBRE")
    id_proyecto_n8n = int(os.getenv("ID_INMOBILIARIA") or 0)

    if not empresa or not id_proyecto_n8n:
        logging.error("EMPRESA_NOMBRE o ID_INMOBILIARIA no definidos")
        return 0

    conn_crm = pymysql.connect(**DB_CONFIG_CRM)
    conn_src = pymysql.connect(**DB_CONFIG)

    try:
        # 1️⃣ Obtener IDs ya existentes en CRM para este proyecto
        with conn_crm.cursor() as cur:
            cur.execute(
                """
                SELECT TRIM(ID_centralizador) AS idc
                FROM Lead_entrantes_crm
                WHERE id_proyecto_n8n = %s
                """,
                (id_proyecto_n8n,)
            )
            existentes = {r["idc"] for r in cur.fetchall()}

        # 2️⃣ Leer leads origen (últimas 24h)
        nuevos = []
        with conn_src.cursor() as cur:
            cur.execute(
                """
                SELECT *
                FROM Lead_entrantes
                WHERE Now >= NOW() - INTERVAL 24 HOUR
                """
            )

            for row in cur.fetchall():
                idc = str(row.get("ID_centralizador") or "").strip()
                if not idc:
                    continue

                if idc in existentes:
                    continue

                # normalizaciones
                row_crm = dict(row)  # copia limpia
                
                row_crm["ID_centralizador"] = idc
                row_crm["ID_cliente"] = int(row.get("ID_cliente") or 0)
                row_crm["ID_proyecto"] = int(row.get("ID_proyecto") or 0)
                row_crm["ID_medio_llegada"] = int(row.get("ID_medio_llegada") or 0)
                
                # ⚠️ Fechas CRM SIEMPRE recalculadas
                row_crm["Timestamp"] = now_timestamp()
                row_crm["Now"] = now_timestamp()
                
                row_crm["empresa_nombre"] = empresa
                row_crm["id_proyecto_n8n"] = id_proyecto_n8n
                
                nuevos.append(row_crm)

        if not nuevos:
            logging.info("CRM: no hay nuevos leads para replicar")
            return 0

        # 3️⃣ Insertar en CRM
        with conn_crm.cursor() as cur:
            cur.executemany(SQL_INSERT_CRM, nuevos)
        conn_crm.commit()

        logging.info(f"CRM: insertados {len(nuevos)} nuevos registros")
        return len(nuevos)

    finally:
        conn_src.close()
        conn_crm.close()



#---N8N

# ─────────────────────────────────────────────────────────────
# Entry point
# ─────────────────────────────────────────────────────────────

if __name__ == "__main__":
    try:
        args = parse_args()
        if args.cmd == "test":
            test_one()

        elif args.cmd == "c-test":
            logging.info(f"Rango cotizaciones: {args.desde} → {args.hasta}")
            test_cotizacion_uno(args.desde, args.hasta)
        elif args.cmd == "c-import":
            logging.info(f"Rango cotizaciones: {args.desde} → {args.hasta}")
            importar_cotizaciones_a_leads(args.desde, args.hasta)
        else:
            main()
    except Exception as e:
        logging.exception(f"Error fatal en cron_import_leads: {e}")
        raise
