import os, sys
import pymysql
import logging
from datetime import datetime, timedelta, date
from dotenv import load_dotenv

BASE_DIR = "/home/andrespa/ebcoom.inteliventa.ai"
os.chdir(BASE_DIR)
sys.path.insert(0, BASE_DIR)
load_dotenv(f"{BASE_DIR}/.env")

# === IMPORTS ===
from interaccion_planok import crear_seguimiento, obtener_rut_vendedor_por_proyecto
from crear_contacto import create_contact 

# === CONFIGURACIÓN ===
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
}

TIPO_CONTACTOPOK = int(os.getenv("CONI_GESTIONADO", "0")) 
PROYECTOS_ACTIVO_STR = os.getenv("PROYECTOS_ACTIVO", "")
PROYECTOS_ACTIVO = set()
if PROYECTOS_ACTIVO_STR.strip():
    try:
        PROYECTOS_ACTIVO = {int(x) for x in PROYECTOS_ACTIVO_STR.split(",")}
    except ValueError:
        pass

# ───────────────────────────────────────────────
# Helpers
# ───────────────────────────────────────────────

def _to_dt(fecha):
    """Convierte string de DB a datetime."""
    if isinstance(fecha, datetime): return fecha
    if isinstance(fecha, date): return datetime.combine(fecha, datetime.min.time())
    if not fecha: return None
    
    # Intentamos parsear formatos comunes
    s = str(fecha).strip()
    for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d", "%Y-%m-%dT%H:%M:%S"):
        try:
            return datetime.strptime(s, fmt)
        except ValueError:
            pass
    return None

def fetch_pending_leads():
    # Traemos 'uno' (primer intento) y 'dos' (segundo intento pendiente)
    sql = """
        SELECT *
        FROM Lead_entrantes
        WHERE enviado_cf_2 IN ('uno','dos')
          AND (Enviado_25h IS NULL OR Enviado_25h = 0)
    """
    conn = pymysql.connect(**DB_CONFIG)
    try:
        with conn.cursor() as cur:
            cur.execute(sql)
            return cur.fetchall()
    finally:
        conn.close()

def update_enviado_cf2(id_centralizador, nuevo_valor, respuesta=None):
    conn = pymysql.connect(**DB_CONFIG)
    try:
        with conn.cursor() as cur:
            sql = "UPDATE Lead_entrantes SET enviado_cf_2 = %s, respuesta_cf = %s WHERE ID_centralizador = %s"
            cur.execute(sql, (nuevo_valor, respuesta, id_centralizador))
        conn.commit()
    finally:
        conn.close()

def update_enviado_planok_flag(id_cliente):
    """Marca el flag Enviado_25h para no volver a procesar el cierre."""
    conn = pymysql.connect(**DB_CONFIG)
    try:
        with conn.cursor() as cur:
            # Usamos ID_cliente con cuidado, o idealmente ID_centralizador si pudieras cambiar la estructura
            # Asumimos que ID_cliente es seguro para este flujo
            sql = "UPDATE Lead_entrantes SET Enviado_25h = 1 WHERE ID_cliente = %s"
            cur.execute(sql, (id_cliente,))
        conn.commit()
    finally:
        conn.close()

def claim_cierre_planok(id_centralizador, id_cliente):
    """Reserva por cliente el cierre para impedir dos envíos concurrentes."""
    conn = pymysql.connect(**DB_CONFIG)
    try:
        with conn.cursor() as cur:
            if id_cliente not in (None, "", 0, "0"):
                # El FOR UPDATE serializa dos cron que intenten cerrar al mismo cliente.
                cur.execute(
                    "SELECT ID_centralizador, enviado_cf_2, Enviado_25h "
                    "FROM Lead_entrantes WHERE ID_cliente = %s FOR UPDATE",
                    (id_cliente,)
                )
                filas = cur.fetchall()
                if any(
                    fila.get("Enviado_25h") == 1
                    or fila.get("enviado_cf_2") == "procesando_planok"
                    for fila in filas
                ):
                    conn.rollback()
                    return False

                cur.execute(
                    """
                    UPDATE Lead_entrantes
                    SET enviado_cf_2 = 'procesando_planok',
                        respuesta_cf = 'Cierre PlanOK reservado; envío en proceso'
                    WHERE ID_cliente = %s
                      AND enviado_cf_2 = 'dos'
                      AND (Enviado_25h IS NULL OR Enviado_25h = 0)
                    """,
                    (id_cliente,)
                )
            else:
                cur.execute(
                    """
                    UPDATE Lead_entrantes
                    SET enviado_cf_2 = 'procesando_planok',
                        respuesta_cf = 'Cierre PlanOK reservado; envío en proceso'
                    WHERE ID_centralizador = %s
                      AND enviado_cf_2 = 'dos'
                      AND (Enviado_25h IS NULL OR Enviado_25h = 0)
                    """,
                    (id_centralizador,)
                )
            reservado = cur.rowcount >= 1
        conn.commit()
        return reservado
    finally:
        conn.close()

def confirmar_cierre_planok(id_centralizador, id_cliente, seg_id, respuesta_cf=None):
    """Confirma en una sola transacción que PlanOK recibió el seguimiento."""
    conn = pymysql.connect(**DB_CONFIG)
    try:
        with conn.cursor() as cur:
            detalle = f"PlanOK seg_id={seg_id}; Chatfuel={respuesta_cf or ''}"
            if id_cliente not in (None, "", 0, "0"):
                cur.execute(
                    """
                    UPDATE Lead_entrantes
                    SET Enviado_25h = 1,
                        enviado_cf_2 = 'final',
                        respuesta_cf = %s
                    WHERE ID_cliente = %s
                      AND enviado_cf_2 = 'procesando_planok'
                    """,
                    (detalle, id_cliente)
                )
            else:
                cur.execute(
                    """
                    UPDATE Lead_entrantes
                    SET Enviado_25h = 1,
                        enviado_cf_2 = 'final',
                        respuesta_cf = %s
                    WHERE ID_centralizador = %s
                      AND enviado_cf_2 = 'procesando_planok'
                    """,
                    (detalle, id_centralizador)
                )
        conn.commit()
    finally:
        conn.close()

def cerrar_en_planok(lead, proyecto_id, now, comentario, respuesta_cf=None):
    """
    Cierra una gestión una sola vez desde este proceso.

    Si PlanOK no confirma con seg_id, se conserva 'procesando_planok'. Esto evita
    un reenvío automático potencialmente duplicado y obliga a verificar el caso.
    """
    id_centralizador = lead.get("ID_centralizador")
    id_cliente = lead.get("ID_cliente")
    if not claim_cierre_planok(id_centralizador, id_cliente):
        logging.info(
            f"Lead {id_centralizador}: cierre no reservado; ya fue tomado o cerrado."
        )
        return False

    try:
        rut_vend = obtener_rut_vendedor_por_proyecto(proyecto_id)
        seg_id = crear_seguimiento(
            id_cliente=lead.get("ID_cliente"),
            id_proyecto=proyecto_id,
            id_medio=int(lead.get('ID_medio_llegada') or 0),
            id_tipo_contacto=TIPO_CONTACTOPOK,
            id_expectativa=1,
            comentario=comentario,
            fecha_recontacto_str=now.strftime('%Y-%m-%dT%H:%M:%SZ'),
            mantener_ejecutivo=False,
            rutvendedor=rut_vend
        )

        if not seg_id:
            logging.error(
                f"Lead {id_centralizador}: PlanOK no confirmó seg_id. "
                "Queda en 'procesando_planok' para revisión; no se reenviará solo."
            )
            return False

        confirmar_cierre_planok(
            id_centralizador, id_cliente, seg_id, respuesta_cf
        )
        logging.info(
            f"Lead {id_centralizador}: ciclo cerrado en PlanOK (seg_id={seg_id})."
        )
        return True

    except Exception:
        logging.exception(
            f"Lead {id_centralizador}: resultado incierto al cerrar en PlanOK. "
            "Queda en 'procesando_planok' y no se reenviará automáticamente."
        )
        return False

# ───────────────────────────────────────────────
# FLUJO PRINCIPAL (Orquestador)
# ───────────────────────────────────────────────
def process_leads():
    leads = fetch_pending_leads()
    if not leads:
        logging.info("Sin leads pendientes (uno/dos).")
        return

    # Usamos datetime.now() asumiendo que el servidor y la DB están en la misma zona horaria
    # Si la DB está en UTC y el server en Chile, habría que ajustar.
    now = datetime.now() 

    for lead in leads:
        id_centralizador = lead.get("ID_centralizador")
        status = (lead.get("enviado_cf_2") or "").lower()
        
        # 1. Validación de Proyecto
        try:
            proyecto_id = int(lead.get("ID_proyecto") or 0)
        except:
            proyecto_id = 0
            
        if proyecto_id not in PROYECTOS_ACTIVO:
            update_enviado_cf2(id_centralizador, 'ignorado_proyecto', 'Proyecto inactivo')
            continue

        # 2. Obtener Fechas
        fecha_ingreso_dt = _to_dt(lead.get("Fecha_ingreso")) # Para fase 1
        base_dt_now      = _to_dt(lead.get("Now"))           # Para fase 2 (Precisión)

        try:
            # ==========================================
            # FASE 1: PRIMER CONTACTO (0 - 24 horas)
            # ==========================================
            if status == "uno":
                
                if lead.get('enviado_cf') == 1:
                    logging.info(f"Lead {id_centralizador}: Ya fue enviado antes (enviado_cf=1). Corrigiendo estado a 'dos'.")
                    update_enviado_cf2(id_centralizador, "dos", "Corregido autom. por script")
                    continue                
                
                if not fecha_ingreso_dt:
                    logging.warning(f"Lead {id_centralizador}: Sin Fecha_ingreso.")
                    continue

                # Calculamos antigüedad
                horas_pasadas = (now - fecha_ingreso_dt).total_seconds() / 3600

                if horas_pasadas <= 24:
                    logging.info(f"Lead {id_centralizador}: Intentando Primer Envío...")
                    
                    # Llamamos a create_contact. Él se encarga de validar si ya habló (UserInteraction)
                    resp = create_contact(lead)
                    
                    if resp.get('status') == 'skipped':
                        # CASO CLAVE: Si se saltó porque "Ya tiene conversación" o "Ya enviado",
                        # NO lo pasamos a 'dos'. Lo cerramos aquí.
                        msg = resp.get('message', '')
                        if "conversación activa" in msg or "Ya enviado" in msg:
                            logging.info(f"Lead {id_centralizador}: Ya gestionado/conversando. Fin del flujo.")
                            update_enviado_cf2(id_centralizador, "final_conversando", str(resp))
                        else:
                            # Otros skips (ej. límite diario) se mantienen en 'uno' para intentar mañana
                            logging.info(f"Lead {id_centralizador}: Skipped ({msg}). Se mantiene en 'uno'.")
                    
                    elif resp.get('error'):
                        logging.error(f"Lead {id_centralizador}: Error Chatfuel: {resp}")
                    
                    else:
                        # ÉXITO: Pasamos a etapa 'dos' para esperar las 24h
                        update_enviado_cf2(id_centralizador, "dos", str(resp))
                        logging.info(f"Lead {id_centralizador}: Éxito Fase 1 -> Pasa a 'dos'")
                
                else:
                    # Se pasó el tiempo del primer envío (> 24h)
                    logging.info(f"Lead {id_centralizador}: Expiró tiempo Fase 1 (>24h).")
                    update_enviado_cf2(id_centralizador, "expirado_fase1", "Fuera de ventana inicial")


            # ==========================================
            # FASE 2: SEGUNDO CONTACTO (24 - 26 horas post-lead)
            # ==========================================
            elif status == "dos":
                if not base_dt_now:
                    logging.warning(f"Lead {id_centralizador}: Sin campo 'Now' para cálculo preciso.")
                    update_enviado_cf2(id_centralizador, "error_data", "Falta campo Now")
                    continue

                # Ventana estricta basada en el campo 'Now' de la DB
                ventana_inicio = base_dt_now + timedelta(hours=24)
                ventana_fin    = base_dt_now + timedelta(hours=26) # Ventana de 2 horas para actuar

                if now < ventana_inicio:
                    # Aún es muy temprano (ej. han pasado 20h). Esperamos.
                    continue
                
                elif ventana_inicio <= now <= ventana_fin:
                    logging.info(f"Lead {id_centralizador}: DENTRO DE VENTANA 24H (Fase 2). Ejecutando...")
                    
                    # 1. Reintento Chatfuel (Modo "Dos")
                    lead_cf = dict(lead)
                    lead_cf["Activar_Contacto"] = "Dos" # Forzamos la lógica de segundo envío
                    
                    resp_cf = create_contact(lead_cf)
                    
                    # Si create_contact responde "stopped" es porque el cliente respondió en el intertanto
                    if resp_cf.get('status') == 'stopped':
                        logging.info(f"Lead {id_centralizador}: Cliente respondió antes del 2do envío. Cancelado.")
                        update_enviado_cf2(id_centralizador, "final_respondio", str(resp_cf))
                        continue

                    # 2. Cierre protegido en PlanOK.
                    cerrar_en_planok(
                        lead=lead,
                        proyecto_id=proyecto_id,
                        now=now,
                        comentario=(
                            "Gestión ConiLead: Se enviaron recordatorios, "
                            "cliente no contestó (Ciclo Terminado)."
                        ),
                        respuesta_cf=str(resp_cf)
                    )

                else:
                    # Los pendientes históricos que antes cabían en la ventana de
                    # 240h ya no reciben un Chatfuel tardío, pero sí conservan el
                    # cierre que corresponde en PlanOK.
                    logging.info(
                        f"Lead {id_centralizador}: fuera de ventana (>26h). "
                        "Se cerrará en PlanOK sin segundo envío tardío."
                    )
                    cerrar_en_planok(
                        lead=lead,
                        proyecto_id=proyecto_id,
                        now=now,
                        comentario=(
                            "Gestión ConiLead: ventana de recontacto expirada; "
                            "cliente no contestó (Ciclo Terminado)."
                        ),
                        respuesta_cf="Segundo envío omitido por ventana mayor a 26 horas"
                    )

        except Exception as e:
            logging.error(f"Error procesando lead {id_centralizador}: {e}", exc_info=True)

if __name__ == "__main__":
    process_leads()
