<?php
set_time_limit(60);
ini_set('max_execution_time', 60);
/**
 * AGENTE IA - ESCOMONTT CAPACITACIÓN
 * v6 — Feriados automáticos + interruptor on/off
 */

define('VERIFY_TOKEN',      'escomontt2024');
define('WHATSAPP_TOKEN',    'EAAcZCoyn72JABRyR7zXEDK1PqJOhqPPZB5rSsRZB29UXo8xFxwM51vYmAekenpVV6ZAod8iZC5NYdWobYHwFXjsvAj3Np1iVyh6Lk2e2qkNHC3OASB8ZCrQlF0zU3q9enJoUlWDzlpwlkM4BPOTGHvPFEry9IV2ik1LDFkZAI2uBCJZANOrYfg5kqhIp55CZCRa0XKPVa61vxENBDuIiicUe4JOl42PAWZBZBpJ43SHawEM0jYMdZCeWZBBjHWZCuPpoCNuhYca3GOwnwPddmCBDiqw9xIZBgZDZD');
define('WHATSAPP_PHONE_ID', '1083040444890104');
define('KAPSO_API_KEY',     'da9fb20e7561d99a11e3c06a9382778d980370ef235c60b38dd21bbb21e194ce'); // Cuenta Kapso separada para ESCOMONTT
define('CLAUDE_API_KEY',    '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');
define('CLAUDE_MODEL',      'claude-sonnet-4-5');
define('OPENAI_API_KEY',    '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'); // Para transcripción de audios con Whisper
define('CONFIG_FILE',       __DIR__ . '/config_agente.json');

define('DB_HOST', 'localhost');
define('DB_NAME', 'escomont_agente');
define('DB_USER', 'escomont_agente');
define('DB_PASS', 'Escuela2020.');

define('SMTP_HOST', 'mail.escomontt.cl');
define('SMTP_PORT', 465);
define('SMTP_USER', 'info@escomontt.cl');
define('SMTP_PASS', 'Escuela2020.');
define('SMTP_FROM_NAME', 'Valentina ESCOMONTT');

require_once __DIR__ . '/PHPMailer/src/Exception.php';
require_once __DIR__ . '/PHPMailer/src/PHPMailer.php';
require_once __DIR__ . '/PHPMailer/src/SMTP.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception as PHPMailerException;

// Lógica de consulta de disponibilidad de horarios (tool de Valentina). La conexión
// a la BD del ERP se abre bajo demanda dentro de esta función (ver conectarERPSoloLectura()
// en disponibilidad_clases.php) — a propósito NO se hace require de config/database.php
// aquí arriba, porque ese archivo usa die() si falla la conexión, y eso tumbaría TODO
// el webhook (incluso conversaciones que no piden horarios) ante cualquier problema
// puntual con la base del ERP.
require_once __DIR__ . '/disponibilidad_clases.php';

/**
 * Envía un correo por SMTP autenticado usando PHPMailer.
 * $destinatarios puede ser un string o un array de strings.
 * Retorna true/false y deja registro en error_log.txt si falla.
 */
function enviarCorreo($destinatarios, $asunto, $cuerpo) {
    $mail = new PHPMailer(true);
    try {
        $mail->isSMTP();
        $mail->Host       = SMTP_HOST;
        $mail->SMTPAuth   = true;
        $mail->Username   = SMTP_USER;
        $mail->Password   = SMTP_PASS;
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
        $mail->Port       = SMTP_PORT;
        $mail->CharSet    = 'UTF-8';

        $mail->setFrom(SMTP_USER, SMTP_FROM_NAME);
        $mail->addReplyTo(SMTP_USER, SMTP_FROM_NAME);

        if (is_array($destinatarios)) {
            foreach ($destinatarios as $d) { $mail->addAddress($d); }
        } else {
            $mail->addAddress($destinatarios);
        }

        $mail->Subject = $asunto;
        $mail->Body    = $cuerpo;
        $mail->isHTML(false);

        $mail->send();
        return true;
    } catch (PHPMailerException $e) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " PHPMailer: " . $mail->ErrorInfo . "\n", FILE_APPEND);
        return false;
    }
}

function enviarWhatsApp($to, $texto) {
    // Envío vía proxy de Kapso (compatible con Graph API de Meta).
    // Kapso gestiona la conexión en modo Coexistencia, por lo que el envío
    // saliente pasa por su API en vez de graph.facebook.com directo.
    // NOTA: un wamid devuelto aquí NO garantiza entrega real — Meta puede
    // marcarlo 'failed' después vía webhook de status (ver bloque 'statuses'
    // más abajo), típicamente por la ventana de 24h cerrada (error 131047).
    $payload = ['messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'text', 'text' => ['body' => $texto]];
    $ch = curl_init('https://api.kapso.ai/meta/whatsapp/v24.0/' . WHATSAPP_PHONE_ID . '/messages');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload),
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-API-Key: ' . KAPSO_API_KEY]
    ]);
    $resp = curl_exec($ch);
    $err  = curl_error($ch);
    curl_close($ch);
    file_put_contents(__DIR__ . '/wa_log.txt', date('Y-m-d H:i:s') . " To: $to\n" . $resp . ($err ? " CURL_ERR: $err" : '') . "\n\n", FILE_APPEND);
    $respData = json_decode($resp, true);
    return [
        'raw'   => $resp,
        'wamid' => $respData['messages'][0]['id'] ?? null
    ];
}

/**
 * Envía un mensaje de ubicación nativo de WhatsApp (pin real, abre en Maps).
 * Se usa cuando Valentina incluye la marca [ENVIAR_UBICACION] en su respuesta,
 * en vez de describir la dirección con calles o referencias inventadas.
 */
function enviarWhatsAppUbicacion($to, $lat, $lng, $nombre, $direccion) {
    $payload = [
        'messaging_product' => 'whatsapp',
        'to' => $to,
        'type' => 'location',
        'location' => [
            'latitude'  => $lat,
            'longitude' => $lng,
            'name'      => $nombre,
            'address'   => $direccion
        ]
    ];
    $ch = curl_init('https://api.kapso.ai/meta/whatsapp/v24.0/' . WHATSAPP_PHONE_ID . '/messages');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload),
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-API-Key: ' . KAPSO_API_KEY]
    ]);
    $resp = curl_exec($ch);
    $err  = curl_error($ch);
    curl_close($ch);
    file_put_contents(__DIR__ . '/wa_log.txt', date('Y-m-d H:i:s') . " [UBICACION] To: $to\n" . $resp . ($err ? " CURL_ERR: $err" : '') . "\n\n", FILE_APPEND);
    $respData = json_decode($resp, true);
    return ['raw' => $resp, 'wamid' => $respData['messages'][0]['id'] ?? null];
}

/**
 * Descarga cualquier archivo multimedia de WhatsApp (imagen, audio, etc.) vía el proxy de Kapso.
 * Requiere phone_number_id como query param para que Kapso sepa qué configuración usar.
 * Devuelve ['bytes' => binario_crudo, 'mime' => mime_type] o null si falla en cualquier paso.
 */
function obtenerMediaKapso($media_id) {
    $ch = curl_init('https://api.kapso.ai/meta/whatsapp/v24.0/' . $media_id . '?phone_number_id=' . WHATSAPP_PHONE_ID);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['X-API-Key: ' . KAPSO_API_KEY],
        CURLOPT_TIMEOUT => 15
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);
    $info = json_decode($resp, true);

    // Se prioriza download_url (URL propia de Kapso, ya con el token embebido)
    $urlDescarga = $info['download_url'] ?? $info['url'] ?? null;
    if (!$urlDescarga) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Media URL fail (media_id=$media_id): " . $resp . "\n", FILE_APPEND);
        return null;
    }

    $ch2 = curl_init($urlDescarga);
    $opts = [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 25];
    if (!isset($info['download_url'])) {
        $opts[CURLOPT_HTTPHEADER] = ['X-API-Key: ' . KAPSO_API_KEY];
    }
    curl_setopt_array($ch2, $opts);
    $bytes = curl_exec($ch2);
    $httpCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE);
    curl_close($ch2);

    if ($httpCode !== 200 || empty($bytes)) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Media download fail HTTP $httpCode (media_id=$media_id)\n", FILE_APPEND);
        return null;
    }

    return ['bytes' => $bytes, 'mime' => $info['mime_type'] ?? 'application/octet-stream'];
}

/**
 * Descarga una imagen de WhatsApp y la deja lista en base64 para la visión de Claude.
 */
function obtenerImagenBase64($media_id) {
    $media = obtenerMediaKapso($media_id);
    if (!$media) return null;
    return ['base64' => base64_encode($media['bytes']), 'mime' => $media['mime']];
}

/**
 * Descarga un audio de WhatsApp y lo transcribe con Whisper (OpenAI).
 * Devuelve el texto transcrito o null si falla en cualquier paso.
 */
function transcribirAudioWhisper($media_id) {
    $media = obtenerMediaKapso($media_id);
    if (!$media) return null;

    $ext = (strpos($media['mime'], 'ogg') !== false) ? 'ogg' : 'mp3';
    $tmpFile = tempnam(sys_get_temp_dir(), 'wa_audio_') . '.' . $ext;
    file_put_contents($tmpFile, $media['bytes']);

    $ch = curl_init('https://api.openai.com/v1/audio/transcriptions');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . OPENAI_API_KEY],
        CURLOPT_POSTFIELDS     => [
            'file'     => new CURLFile($tmpFile, $media['mime'], 'audio.' . $ext),
            'model'    => 'whisper-1',
            'language' => 'es'
        ],
        CURLOPT_TIMEOUT => 40
    ]);
    $resp = curl_exec($ch);
    $err  = curl_error($ch);
    curl_close($ch);
    @unlink($tmpFile);

    file_put_contents(__DIR__ . '/whisper_log.txt', date('Y-m-d H:i:s') . "\n" . $resp . ($err ? " CURL_ERR: $err" : '') . "\n\n", FILE_APPEND);

    $data  = json_decode($resp, true);
    $texto = trim($data['text'] ?? '');
    return $texto !== '' ? $texto : null;
}

function getSystemPrompt() {
    $config_file = __DIR__ . '/config_agente.json';
    if (file_exists($config_file)) {
        $config = json_decode(file_get_contents($config_file), true);
        if (!empty($config['system_prompt'])) {
            return $config['system_prompt'];
        }
    }
    return 'Eres Valentina, asesora comercial de ESCOMONTT Capacitación. Responde consultas sobre cursos de conducción de manera amable y profesional.';
}

function cargarConfig() {
    if (file_exists(CONFIG_FILE)) {
        $c = json_decode(file_get_contents(CONFIG_FILE), true);
        return $c ?: [];
    }
    return [];
}

/**
 * Avisa al cliente que su conversación pasó a atención prioritaria/humana,
 * justo en el momento en que el análisis en background lo detecta (lead
 * CALIENTE o consulta de EMPRESA). Es el único mensaje que se envía antes
 * de que Valentina quede en silencio por el resto de la conversación
 * (ver $requiere_silencio), así el cliente no queda pensando que lo dejaron
 * en visto. Se envía UNA sola vez por contacto (columna handoff_avisado).
 */
function avisarHandoffUrgente($pdo, $from, $nombre_lead, $es_horario_laboral) {
    $chk = $pdo->prepare("SELECT handoff_avisado FROM contactos WHERE telefono=?");
    $chk->execute([$from]);
    $row = $chk->fetch(PDO::FETCH_ASSOC);
    if ($row && !empty($row['handoff_avisado'])) return; // ya avisado, no duplicar

    $config = cargarConfig();
    $h_ini = isset($config['hora_inicio']) ? (int)$config['hora_inicio'] : 9;
    $h_fin = isset($config['hora_fin'])    ? (int)$config['hora_fin']    : 19;

    if ($es_horario_laboral) {
        $msg = "¡Listo! ✅ Te dejé registrado con prioridad. Un ejecutivo de nuestro equipo te va a escribir en breve para ayudarte a cerrar los detalles 🙌";
    } else {
        $msg = "¡Listo! ✅ Te dejé registrado con prioridad. En este momento estamos fuera de nuestro horario de atención (Lunes a Viernes de {$h_ini}:00 a {$h_fin}:00 hrs), pero apenas abramos un ejecutivo de nuestro equipo te va a escribir para ayudarte a cerrar los detalles. ¡Gracias por tu paciencia! 🙌";
    }

    $envio = enviarWhatsApp($from, $msg);
    $pdo->prepare("INSERT INTO conversaciones (telefono, nombre, role, mensaje, wamid) VALUES (?, ?, 'assistant', ?, ?)")
        ->execute([$from, $nombre_lead, $msg, $envio['wamid']]);
    $pdo->prepare("UPDATE contactos SET handoff_avisado=1 WHERE telefono=?")->execute([$from]);
    file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [HANDOFF AVISADO AL CLIENTE] Tel: $from horario_laboral=" . ($es_horario_laboral?'SI':'NO') . "\n\n", FILE_APPEND);
}

function esFeriado($fecha) {
    $anio = date('Y', strtotime($fecha));
    $cache_file = __DIR__ . "/feriados_{$anio}.json";
    if (!file_exists($cache_file) || (time() - filemtime($cache_file)) > 86400) {
        $ch = curl_init("https://apis.digital.gob.cl/fl/feriados/{$anio}");
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 5,
            CURLOPT_HTTPHEADER     => ['Accept: application/json']
        ]);
        $response = curl_exec($ch);
        curl_close($ch);
        if ($response) {
            file_put_contents($cache_file, $response);
        }
    }
    if (file_exists($cache_file)) {
        $feriados = json_decode(file_get_contents($cache_file), true);
        if (is_array($feriados)) {
            foreach ($feriados as $f) {
                if (isset($f['fecha']) && substr($f['fecha'], 0, 10) === $fecha) {
                    return true;
                }
            }
        }
    }
    return false;
}

function valentinaDbeResponder() {
    date_default_timezone_set('America/Santiago');
    $config = cargarConfig();
    if (isset($config['valentina_activa']) && $config['valentina_activa'] === false) {
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [VALENTINA APAGADA MANUALMENTE]\n\n", FILE_APPEND);
        return false;
    }
    $dia   = (int)date('N');
    $hora  = (int)date('H');
    $min   = (int)date('i');
    $time  = $hora * 60 + $min;
    $hoy   = date('Y-m-d');
    if (esFeriado($hoy)) {
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [FERIADO - Valentina activa]\n\n", FILE_APPEND);
        return true;
    }
    if ($dia >= 6) return true;
    $hora_inicio      = isset($config['hora_inicio'])      ? (int)$config['hora_inicio']      : 9;
    $hora_fin         = isset($config['hora_fin'])         ? (int)$config['hora_fin']         : 19;
    $hora_fin_viernes = isset($config['hora_fin_viernes']) ? (int)$config['hora_fin_viernes'] : 18;
    if ($dia === 5) return !($time >= $hora_inicio * 60 && $time < $hora_fin_viernes * 60);
    return !($time >= $hora_inicio * 60 && $time < $hora_fin * 60);
}

header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    if (isset($_GET['hub_mode']) && $_GET['hub_mode'] === 'subscribe' &&
        isset($_GET['hub_verify_token']) && $_GET['hub_verify_token'] === VERIFY_TOKEN) {
        http_response_code(200);
        echo $_GET['hub_challenge'];
    } else {
        http_response_code(403);
        echo 'Forbidden';
    }
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = file_get_contents('php://input');
    $data  = json_decode($input, true);

    file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . "\n" . $input . "\n\n", FILE_APPEND);

    $value = $data['entry'][0]['changes'][0]['value'] ?? null;

    // ── ECOS DE MENSAJES ENVIADOS MANUALMENTE DESDE LA APP (Coexistence) ──
    // Cuando alguien del equipo escribe directo desde el celular, Kapso reenvía
    // el evento "smb_message_echoes" en vez de "messages". Los registramos como
    // role='equipo' para que valentinaDbeResponder / silencio_humano los detecten.
    if (isset($value['message_echoes']) && is_array($value['message_echoes'])) {
        try {
            $pdo_echo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
                DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
            foreach ($value['message_echoes'] as $echo) {
                $tel_cliente = $echo['to'] ?? '';
                if (!$tel_cliente) continue;
                $texto_echo = $echo['text']['body'] ?? ('[' . ($echo['type'] ?? 'mensaje') . ' enviado desde la app]');
                $pdo_echo->prepare("INSERT INTO conversaciones (telefono, role, mensaje) VALUES (?, 'equipo', ?)")
                    ->execute([$tel_cliente, $texto_echo]);
                $pdo_echo->prepare("UPDATE contactos SET ultimo_contacto=NOW() WHERE telefono=?")->execute([$tel_cliente]);
            }
            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [MESSAGE_ECHO registrado]\n\n", FILE_APPEND);
        } catch (Exception $e) {
            file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Echo DB: " . $e->getMessage() . "\n", FILE_APPEND);
        }
        http_response_code(200);
        echo json_encode(['status' => 'echo_registrado']);
        exit;
    }

    // ── ESTADOS DE ENTREGA (sent/delivered/read/failed) ──────────────────
    // Meta a veces acepta un mensaje (devuelve wamid) y RECIÉN DESPUÉS, de
    // forma asíncrona, informa que en realidad falló (ej. error 131047:
    // "Re-engagement message" = ventana de 24h cerrada). Sin este bloque,
    // el CRM quedaba mostrando esos mensajes como enviados con éxito aunque
    // nunca llegaron al cliente.
    if (isset($value['statuses']) && is_array($value['statuses'])) {
        try {
            $pdo_st = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
                DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
            try { $pdo_st->exec("ALTER TABLE conversaciones ADD COLUMN wamid VARCHAR(255) DEFAULT NULL"); } catch (Exception $e) {}
            try { $pdo_st->exec("ALTER TABLE conversaciones ADD COLUMN delivery_status VARCHAR(20) DEFAULT NULL"); } catch (Exception $e) {}
            foreach ($value['statuses'] as $st) {
                $wamid_st = $st['id'] ?? '';
                $status_st = $st['status'] ?? '';
                if (!$wamid_st || !$status_st) continue;
                $pdo_st->prepare("UPDATE conversaciones SET delivery_status=? WHERE wamid=?")
                    ->execute([$status_st, $wamid_st]);
                if ($status_st === 'failed') {
                    $err_detail = $st['errors'][0]['error_data']['details'] ?? ($st['errors'][0]['title'] ?? 'Error desconocido');
                    file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [ENTREGA FALLIDA] wamid=$wamid_st Detalle: $err_detail\n\n", FILE_APPEND);
                }
            }
        } catch (Exception $e) {
            file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Status DB: " . $e->getMessage() . "\n", FILE_APPEND);
        }
        http_response_code(200);
        echo json_encode(['status' => 'status_registrado']);
        exit;
    }

    if (!isset($value['messages'])) {
        http_response_code(200);
        echo json_encode(['status' => 'no_message']);
        exit;
    }

    $message    = $value['messages'][0];
    $from       = $message['from'];
    $msg_type   = $message['type'];
    $message_id = $message['id'] ?? '';

    if ($message_id) {
        try {
            $pdo_dedup = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
                DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
            $pdo_dedup->exec("CREATE TABLE IF NOT EXISTS mensajes_procesados (
                message_id VARCHAR(255) PRIMARY KEY,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )");
            $pdo_dedup->exec("DELETE FROM mensajes_procesados WHERE created_at < NOW() - INTERVAL 1 HOUR");
            try {
                $stmt_dedup = $pdo_dedup->prepare("INSERT INTO mensajes_procesados (message_id) VALUES (?)");
                $stmt_dedup->execute([$message_id]);
            } catch (PDOException $e) {
                file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [DUPLICADO IGNORADO] $message_id\n\n", FILE_APPEND);
                http_response_code(200);
                echo json_encode(['status' => 'duplicate']);
                exit;
            }
        } catch (Exception $e) {
            file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Dedup DB: " . $e->getMessage() . "\n", FILE_APPEND);
        }
    }

    $contact_name = $value['contacts'][0]['profile']['name'] ?? null;
    $user_text    = ($msg_type === 'text') ? ($message['text']['body'] ?? '') : ('[' . $msg_type . ' recibido]');

    $pdo_check = null;
    try {
        $pdo_check = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
            DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    } catch (Exception $e) {}

    $ejecutivo_activo = false;
    $estado_actual_contacto = null;
    if ($pdo_check) {
        try {
            $stmt_check = $pdo_check->prepare("SELECT COUNT(*) FROM conversaciones 
                WHERE telefono=? AND role='equipo' AND created_at >= NOW() - INTERVAL 2 HOUR");
            $stmt_check->execute([$from]);
            $ejecutivo_activo = ($stmt_check->fetchColumn() > 0);
        } catch (Exception $e) {}

        try {
            $stmt_estado_chk = $pdo_check->prepare("SELECT estado FROM contactos WHERE telefono=?");
            $stmt_estado_chk->execute([$from]);
            $estado_actual_contacto = $stmt_estado_chk->fetchColumn() ?: null;
        } catch (Exception $e) {}
    }

    // Estados donde se asume que un humano ya tomó el lead: Valentina se queda en silencio,
    // sin importar si alguien le escribió por el chat del CRM en las últimas 2 horas.
    $estados_gestion_humana = ['nuevo', 'en_gestion', 'listo', 'matriculado'];
    $requiere_silencio = $ejecutivo_activo || in_array($estado_actual_contacto, $estados_gestion_humana);

    if ($requiere_silencio) {
        $motivo = $ejecutivo_activo ? 'EJECUTIVO ACTIVO' : "ESTADO '$estado_actual_contacto' - requiere gestión humana";
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [$motivo - Valentina en silencio]\n\n", FILE_APPEND);
        if ($pdo_check) {
            try {
                $pdo_check->prepare("INSERT INTO conversaciones (telefono, nombre, role, mensaje) VALUES (?, ?, 'user', ?)")
                    ->execute([$from, $contact_name, $user_text]);
                $pdo_check->prepare("UPDATE contactos SET ultimo_contacto=NOW() WHERE telefono=?")
                    ->execute([$from]);
                // El cliente volvió a escribir: se resetea el contador de seguimientos del cron
                try { $pdo_check->exec("ALTER TABLE contactos ADD COLUMN seguimientos_enviados INT NOT NULL DEFAULT 0"); } catch (Exception $e2) {}
                $pdo_check->prepare("UPDATE contactos SET seguimientos_enviados=0 WHERE telefono=?")
                    ->execute([$from]);
            } catch (Exception $e) {}
        }
        http_response_code(200);
        echo json_encode(['status' => 'silencio_humano', 'motivo' => $motivo]);
        exit;
    }

    $mediaImagen = null;

    if ($msg_type === 'image') {
        $media_id = $message['image']['id'] ?? null;
        $caption  = $message['image']['caption'] ?? '';
        $mediaImagen = $media_id ? obtenerImagenBase64($media_id) : null;
        if ($mediaImagen) {
            $user_text = $caption !== '' ? $caption : '[Imagen recibida, sin texto adicional]';
        } else {
            enviarWhatsApp($from, 'Uy, no pude abrir esa imagen 😅 ¿Puedes intentar enviarla de nuevo o contarme tu consulta por texto?');
            http_response_code(200);
            echo json_encode(['status' => 'imagen_fallo']);
            exit;
        }
    } elseif ($msg_type === 'audio') {
        $media_id = $message['audio']['id'] ?? null;
        $audio_texto = $media_id ? transcribirAudioWhisper($media_id) : null;
        if ($audio_texto) {
            $user_text = $audio_texto;
        } else {
            enviarWhatsApp($from, 'No pude escuchar bien tu audio 🙈 ¿Me lo puedes escribir o volver a grabar?');
            http_response_code(200);
            echo json_encode(['status' => 'audio_fallo']);
            exit;
        }
    } elseif ($msg_type !== 'text') {
        enviarWhatsApp($from, '¡Hola! 😊 Por el momento solo puedo atenderte por texto, foto o audio. ¿Me cuentas tu consulta? 👍');
        http_response_code(200);
        echo json_encode(['status' => 'ignored_type']);
        exit;
    }

    $es_horario_laboral = !valentinaDbeResponder();
    $es_primera_vez = false;
    if ($es_horario_laboral && $pdo_check) {
        try {
            $chk = $pdo_check->prepare("SELECT COUNT(*) FROM contactos WHERE telefono=?");
            $chk->execute([$from]);
            $es_primera_vez = ($chk->fetchColumn() == 0);
            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [HORARIO LABORAL] primera_vez=" . ($es_primera_vez?'SI':'NO') . "\n\n", FILE_APPEND);
        } catch (Exception $e) {
            file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " DB chequeo primera_vez: " . $e->getMessage() . "\n", FILE_APPEND);
        }
    }

    $pdo = $pdo_check;
    if (!$pdo) {
        try {
            $pdo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
                DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
        } catch (Exception $e) {
            file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " DB: " . $e->getMessage() . "\n", FILE_APPEND);
            http_response_code(500); exit;
        }
    }

    $pdo->exec("CREATE TABLE IF NOT EXISTS conversaciones (
        id INT AUTO_INCREMENT PRIMARY KEY,
        telefono VARCHAR(30) NOT NULL,
        nombre VARCHAR(100),
        role ENUM('user','assistant','equipo') NOT NULL,
        mensaje TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )");

    $pdo->exec("CREATE TABLE IF NOT EXISTS contactos (
        id INT AUTO_INCREMENT PRIMARY KEY,
        telefono VARCHAR(30) UNIQUE NOT NULL,
        nombre VARCHAR(100),
        estado VARCHAR(50) DEFAULT 'valentina',
        temperatura VARCHAR(20) DEFAULT 'frio',
        primer_contacto TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        ultimo_contacto TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    )");
    try { $pdo->exec("ALTER TABLE contactos ADD COLUMN temperatura VARCHAR(20) DEFAULT 'frio'"); } catch(Exception $e) {}
    try { $pdo->exec("ALTER TABLE contactos ADD COLUMN empresa_notificada TINYINT DEFAULT 0"); } catch(Exception $e) {}
    try { $pdo->exec("ALTER TABLE contactos ADD COLUMN caliente_notificada TINYINT DEFAULT 0"); } catch(Exception $e) {}
    try { $pdo->exec("ALTER TABLE contactos ADD COLUMN seguimientos_enviados INT NOT NULL DEFAULT 0"); } catch(Exception $e) {}
    try { $pdo->exec("ALTER TABLE contactos ADD COLUMN handoff_avisado TINYINT DEFAULT 0"); } catch(Exception $e) {}

    $stmt = $pdo->prepare("INSERT INTO contactos (telefono, nombre, estado) VALUES (?, ?, 'valentina')
        ON DUPLICATE KEY UPDATE nombre = COALESCE(?, nombre), ultimo_contacto = NOW()");
    $stmt->execute([$from, $contact_name, $contact_name]);

    // Si el contacto estaba descartado y vuelve a escribir, se reabre automáticamente
    $stmt_reabrir = $pdo->prepare("UPDATE contactos SET estado='valentina' WHERE telefono=? AND estado='no_interesado'");
    $stmt_reabrir->execute([$from]);
    if ($stmt_reabrir->rowCount() > 0) {
        // Se resetean los flags de notificación para que, si vuelve a calentarse
        // o a consultar como empresa, el equipo reciba el aviso de nuevo.
        $pdo->prepare("UPDATE contactos SET empresa_notificada=0, caliente_notificada=0, handoff_avisado=0 WHERE telefono=?")->execute([$from]);
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [REABIERTO] Tel: $from volvió a escribir tras estar Descartado, vuelve a columna Valentina\n\n", FILE_APPEND);
    }

    $stmt = $pdo->prepare("INSERT INTO conversaciones (telefono, nombre, role, mensaje) VALUES (?, ?, 'user', ?)");
    $stmt->execute([$from, $contact_name, $user_text]);

    // El cliente volvió a escribir: se resetea el contador de seguimientos del cron
    $pdo->prepare("UPDATE contactos SET seguimientos_enviados=0 WHERE telefono=?")->execute([$from]);

    if ($es_primera_vez) {
        $bienvenida = "¡Hola! 👋 Soy Valentina, asesora de ESCOMONTT Capacitación.\n\nDesde 1998 somos la escuela de conductores más antigua de Puerto Montt 🚗\n\n¿Qué curso te interesa?\n\n1️⃣ Licencia Clase B\n2️⃣ Licencias A2 / A4 (Profesional)\n3️⃣ Licencias A3 / A5 (Profesional)\n4️⃣ Empresas (Franquicia SENCE)\n5️⃣ Servicio a Alumnos (Plataforma, horarios y dudas)";

        // Responder 200 a Kapso/Meta de inmediato, ANTES del curl bloqueante a
        // enviarWhatsApp(). Así el worker PHP no queda ocupado esperando esa
        // llamada, y si el cliente escribe un segundo mensaje (ej. "1") casi al
        // instante, esa petición nueva no tiene que hacer cola detrás de esta.
        http_response_code(200);
        echo json_encode(['status' => 'bienvenida_enviada']);
        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }

        enviarWhatsApp($from, $bienvenida);
        $pdo->prepare("INSERT INTO conversaciones (telefono, nombre, role, mensaje) VALUES (?, ?, 'assistant', ?)")
            ->execute([$from, $contact_name, $bienvenida]);

        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [PRIMERA VEZ - Bienvenida enviada, Claude omitido]\n\n", FILE_APPEND);
        exit;
    }

    $stmt = $pdo->prepare("SELECT role, mensaje FROM conversaciones WHERE telefono=? ORDER BY created_at DESC LIMIT 20");
    $stmt->execute([$from]);
    $historial = array_reverse($stmt->fetchAll(PDO::FETCH_ASSOC));

    $messages = [];
    foreach ($historial as $row) {
        $role = $row['role'] === 'equipo' ? 'assistant' : $row['role'];
        $content = $row['mensaje'];
        if (empty($messages)) {
            if ($role === 'user') $messages[] = ['role' => $role, 'content' => $content];
            continue;
        }
        $last = count($messages) - 1;
        if ($messages[$last]['role'] === $role) {
            $messages[$last]['content'] .= "\n" . $content;
        } else {
            $messages[] = ['role' => $role, 'content' => $content];
        }
    }
    if (empty($messages)) $messages = [['role' => 'user', 'content' => $user_text]];

    // Si este turno trae una imagen real, se reemplaza el texto del último mensaje del usuario
    // por un bloque de contenido con la imagen + el texto/caption, para que Claude la vea de verdad.
    if ($mediaImagen && !empty($messages)) {
        $lastIdx = count($messages) - 1;
        if ($messages[$lastIdx]['role'] === 'user') {
            $messages[$lastIdx]['content'] = [
                ['type' => 'image', 'source' => ['type' => 'base64', 'media_type' => $mediaImagen['mime'], 'data' => $mediaImagen['base64']]],
                ['type' => 'text', 'text' => $user_text]
            ];
        }
    }

    date_default_timezone_set('America/Santiago');
    $dias_nombres = [1=>'Lunes',2=>'Martes',3=>'Miércoles',4=>'Jueves',5=>'Viernes',6=>'Sábado',7=>'Domingo'];
    $dia_semana   = $dias_nombres[(int)date('N')];
    $contexto_horario = "CONTEXTO ACTUAL (uso interno, nunca lo menciones ni lo repitas al cliente):\n"
        . "Hoy es $dia_semana " . date('d-m-Y') . ", hora actual en Puerto Montt: " . date('H:i') . " hrs.\n"
        . "¿Estamos en horario de oficina ahora mismo? " . ($es_horario_laboral ? "SÍ" : "NO") . ".\n"
        . "Si la respuesta es NO, aplica la instrucción de 'Fuera de horario laboral' del prompt (indicar que un ejecutivo contactará en horario de oficina). Si es SÍ, responde con normalidad.\n\n";

    // Interruptor propio para la tool de disponibilidad, independiente del interruptor
    // general de Valentina (config['valentina_activa']). Default APAGADO a propósito:
    // subir este código no activa nada por sí solo — hay que prenderlo explícitamente
    // agregando "tool_disponibilidad_activa": true en config_agente.json. Si algo falla
    // en producción, se apaga solo esto (poniéndolo en false) sin tocar el resto de Valentina.
    $config_tool = cargarConfig();
    $toolDisponibilidadActiva = ($config_tool['tool_disponibilidad_activa'] ?? false) === true;

    $claude_payload = [
        'model'      => CLAUDE_MODEL,
        'max_tokens' => 700,
        'system'     => [
            // Bloque estático (prompt de Valentina, cursos, precios, personalidad):
            // va PRIMERO y con cache_control para que Anthropic lo cachee y no
            // se cobre/reprocese completo en cada mensaje. Válido ~5 min desde
            // la última vez que se usó; con tráfico normal se mantiene caliente.
            ['type' => 'text', 'text' => getSystemPrompt(), 'cache_control' => ['type' => 'ephemeral']],
            // Bloque dinámico (fecha/hora actual): va DESPUÉS, sin cache, porque
            // cambia en cada request y rompería el cache si estuviera primero.
            ['type' => 'text', 'text' => $contexto_horario],
        ],
        'messages'   => $messages,
    ];

    if ($toolDisponibilidadActiva) {
        $claude_payload['tools'] = [
            [
                'name' => 'consultar_disponibilidad_clase_b',
                'description' => "Consulta la fecha real más próxima con cupo disponible para iniciar el curso Clase B (verifica 12 clases prácticas consecutivas libres en el mismo horario, no un cupo suelto). Es información referencial, no una reserva. Úsala SOLO cuando el cliente pide un horario fijo estándar y repetido de lunes a viernes (una hora puntual, o 'mañana'/'tarde'). NO la uses para horarios irregulares, mixtos o adaptados a disponibilidad laboral del cliente — esos se derivan a la oficina. La respuesta trae 'fecha' y una lista 'opciones' (cada una con hora_inicio/hora_fin): para una hora puntual normalmente viene una sola opción; para 'mañana'/'tarde' pueden venir varias horas distintas que comparten esa misma fecha de inicio — en ese caso, menciónalas TODAS al cliente (con su precio correspondiente según el bloque) para que elija la que más le acomode, en vez de ofrecer solo la primera. Si la respuesta trae 'fuera_de_plazo': true, significa que ese horario específico no tiene cupo dentro de las próximas 2 semanas — en ese caso NO menciones ninguna fecha lejana; en vez de eso sugiere al cliente probar otro horario (la otra franja, mañana o tarde, u otra hora) y ofrécete a consultar esa alternativa.",
                'input_schema' => [
                    'type' => 'object',
                    'properties' => [
                        'tipo' => [
                            'type' => 'string',
                            'enum' => ['hora_exacta', 'rango'],
                            'description' => "'hora_exacta' si el cliente dio una hora puntual (ej. 'a las 16:00', 'como a las 8'). 'rango' si dijo 'mañana' o 'tarde' sin hora precisa.",
                        ],
                        'valor' => [
                            'type' => 'string',
                            'description' => "Si tipo='hora_exacta': la hora en formato HH:MM (ej. '16:00'). Si tipo='rango': 'manana' o 'tarde'.",
                        ],
                    ],
                    'required' => ['tipo', 'valor'],
                ],
            ],
        ];
    }

    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($claude_payload),
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'x-api-key: '.CLAUDE_API_KEY, 'anthropic-version: 2023-06-01'],
        CURLOPT_TIMEOUT        => 30
    ]);
    $claude_response = curl_exec($ch);
    $curl_error = curl_error($ch);
    curl_close($ch);

    file_put_contents(__DIR__ . '/claude_log.txt', date('Y-m-d H:i:s') . "\n" . $claude_response . "\n\n", FILE_APPEND);

    if ($curl_error) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " CURL: " . $curl_error . "\n", FILE_APPEND);
        http_response_code(500); exit;
    }

    $claude_data = json_decode($claude_response, true);
    if (isset($claude_data['error'])) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Claude: " . json_encode($claude_data['error']) . "\n", FILE_APPEND);
    }

    // Si Claude pidió usar la tool de disponibilidad, la ejecutamos (solo lectura sobre
    // la BD del ERP) y le devolvemos el resultado para que redacte la respuesta final.
    if (($claude_data['stop_reason'] ?? '') === 'tool_use') {
        $toolUseBlock = null;
        foreach (($claude_data['content'] ?? []) as $block) {
            if (($block['type'] ?? '') === 'tool_use') { $toolUseBlock = $block; break; }
        }

        if ($toolUseBlock && $toolUseBlock['name'] === 'consultar_disponibilidad_clase_b') {
            $tipo  = $toolUseBlock['input']['tipo']  ?? '';
            $valor = $toolUseBlock['input']['valor'] ?? '';

            // Conexión dedicada, abierta solo aquí y envuelta en try/catch: si la BD del
            // ERP falla o está lenta, esta consulta puntual se degrada (Valentina sigue
            // la conversación sin el dato), en vez de tumbar todo el webhook.
            $resultado = null;
            $fallaConexion = false;
            try {
                $connERP = conectarERPSoloLectura();
                $resultado = buscarProximaDisponibilidad($connERP, $tipo, $valor);
                $connERP->close();
            } catch (Throwable $e) {
                $fallaConexion = true;
                file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Tool disponibilidad - error DB: " . $e->getMessage() . "\n", FILE_APPEND);
            }

            if ($resultado) {
                $toolResultContent = json_encode($resultado, JSON_UNESCAPED_UNICODE);
            } elseif ($fallaConexion) {
                $toolResultContent = json_encode(['error' => 'No se pudo consultar la disponibilidad en este momento, intenta más tarde o deriva al cliente.'], JSON_UNESCAPED_UNICODE);
            } else {
                $toolResultContent = json_encode(['error' => 'No se encontró disponibilidad dentro del horizonte de búsqueda para ese horario.'], JSON_UNESCAPED_UNICODE);
            }

            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [TOOL disponibilidad] input=" . json_encode($toolUseBlock['input']) . " result=$toolResultContent\n\n", FILE_APPEND);

            // Reconstruimos el turno: assistant (con el tool_use) + user (con el tool_result)
            $messages[] = ['role' => 'assistant', 'content' => $claude_data['content']];
            $messages[] = ['role' => 'user', 'content' => [[
                'type' => 'tool_result',
                'tool_use_id' => $toolUseBlock['id'],
                'content' => $toolResultContent,
            ]]];

            $claude_payload['messages'] = $messages;

            $ch2 = curl_init('https://api.anthropic.com/v1/messages');
            curl_setopt_array($ch2, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => json_encode($claude_payload),
                CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'x-api-key: '.CLAUDE_API_KEY, 'anthropic-version: 2023-06-01'],
                CURLOPT_TIMEOUT        => 30
            ]);
            $claude_response2 = curl_exec($ch2);
            curl_close($ch2);
            file_put_contents(__DIR__ . '/claude_log.txt', date('Y-m-d H:i:s') . " [POST-TOOL]\n" . $claude_response2 . "\n\n", FILE_APPEND);

            $claude_data = json_decode($claude_response2, true);
            if (isset($claude_data['error'])) {
                file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Claude POST-TOOL: " . json_encode($claude_data['error']) . "\n", FILE_APPEND);
            }
        }
    }

    $agent_reply = $claude_data['content'][0]['text'] ?? '';

    if (empty(trim($agent_reply))) {
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [RESPUESTA VACIA - reintentando]\n\n", FILE_APPEND);
        $ch_retry = curl_init('https://api.anthropic.com/v1/messages');
        curl_setopt_array($ch_retry, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($claude_payload),
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'x-api-key: '.CLAUDE_API_KEY, 'anthropic-version: 2023-06-01'],
            CURLOPT_TIMEOUT        => 30
        ]);
        $claude_response_retry = curl_exec($ch_retry);
        curl_close($ch_retry);
        file_put_contents(__DIR__ . '/claude_log.txt', date('Y-m-d H:i:s') . " RETRY\n" . $claude_response_retry . "\n\n", FILE_APPEND);
        $claude_data_retry = json_decode($claude_response_retry, true);
        $agent_reply = $claude_data_retry['content'][0]['text'] ?? '';
    }

    if (empty(trim($agent_reply))) {
        $agent_reply = 'Disculpa, tuve un problema técnico. ¿Puedes repetir tu consulta?';
    }

    // Si Valentina decidió que corresponde mandar ubicación, se envía el pin real
    // ANTES del texto, y se limpia la marca para que no aparezca en el mensaje.
    if (strpos($agent_reply, '[ENVIAR_UBICACION]') !== false) {
        $agent_reply = trim(str_replace('[ENVIAR_UBICACION]', '', $agent_reply));
        enviarWhatsAppUbicacion(
            $from,
            -41.4683784,
            -72.9266554,
            'ESCOMONTT Capacitación',
            'Regimiento 645, Puerto Montt'
        );
    }

    // ENVIAR RESPUESTA A WHATSAPP PRIMERO (antes de análisis) para no demorar al cliente
    $envio = enviarWhatsApp($from, $agent_reply);

    try { $pdo->exec("ALTER TABLE conversaciones ADD COLUMN wamid VARCHAR(255) DEFAULT NULL"); } catch (Exception $e) {}
    $stmt = $pdo->prepare("INSERT INTO conversaciones (telefono, nombre, role, mensaje, wamid) VALUES (?, ?, 'assistant', ?, ?)");
    $stmt->execute([$from, $contact_name, $agent_reply, $envio['wamid']]);

    // Responder HTTP 200 al webhook de Meta inmediatamente
    http_response_code(200);
    echo json_encode(['status' => 'ok']);

    // Cerrar la conexión con el cliente HTTP pero seguir ejecutando el script en background
    if (function_exists('fastcgi_finish_request')) {
        fastcgi_finish_request();
    }

    // ===== ANÁLISIS EN BACKGROUND (no bloquea la respuesta al cliente) =====
    try {
        $conv_resumen = "";
        $msgs_para_temp = array_slice($messages, -10);
        foreach ($msgs_para_temp as $m) {
            $rol = $m['role'] === 'user' ? 'Cliente' : 'Valentina';
            // $m['content'] normalmente es texto, pero cuando el turno usó la tool de
            // disponibilidad puede ser un arreglo (bloques tool_use / tool_result) —
            // en ese caso se resume como texto plano para no romper substr().
            if (is_array($m['content'])) {
                $contenido_txt = '[consulta de disponibilidad de horarios]';
            } else {
                $contenido_txt = $m['content'];
            }
            $conv_resumen .= $rol . ": " . substr($contenido_txt, 0, 500) . "\n";
        }
        $conv_resumen .= "Valentina: " . substr($agent_reply, 0, 500) . "\n";

        // UNA SOLA LLAMADA combinando temperatura + empresa para reducir tiempo total
        $analisis_payload = [
            'model'      => CLAUDE_MODEL,
            // Antes en 25: muy ajustado para 4 campos separados por "|" -- se vieron
            // respuestas truncadas en el log (campos faltantes), lo que el código
            // interpreta como "NO" por defecto aunque el modelo nunca llegó a decirlo.
            'max_tokens' => 40,
            'messages'   => [[
                'role'    => 'user',
                'content' => "Analiza esta conversacion de una escuela de conductores.\n\nConversacion:\n" . $conv_resumen . "\n\nResponde EXACTAMENTE en este formato de una linea, sin texto adicional, SIEMPRE con los 4 campos (nunca omitas ninguno aunque sean NO):\nTEMPERATURA|ESEMPRESA|DESCARTA|ESALUMNO\n\nDonde TEMPERATURA es CALIENTE, TIBIO o FRIO segun:\n- CALIENTE: el CLIENTE (no Valentina) confirmo o eligio un bloque horario especifico para sus clases, pregunto por el pago, dio su RUT, confirmo que quiere matricularse, O PIDIO EXPLICITAMENTE hablar con alguien/un ejecutivo/una persona, pidio que lo llamen o contacten, o dejo su telefono para que lo contacten. IMPORTANTE: que Valentina haya mostrado horarios disponibles (por ejemplo tras una consulta de disponibilidad) NO cuenta como CALIENTE por si solo -- se necesita que el CLIENTE haya elegido o confirmado uno de esos horarios, o haya dado alguno de los otros datos mencionados arriba\n- TIBIO: pregunto que incluye, requisitos, horarios o precios\n- FRIO: solo saludo o info general\n\nY ESEMPRESA es EMPRESA si consulta en nombre de una empresa o Franquicia SENCE, sino NO.\n\nY DESCARTA es SI si el cliente rechazo la oferta de forma clara y directa EN CUALQUIER PARTE de la conversacion (no solo en el ultimo mensaje), incluso si lo dice de forma cordial o agradecida. Frases como \"no me interesa\", \"no me interesa por ahora\", \"no por ahora\", \"no gracias\", \"no por el momento\", \"no quiero\", o equivalentes, SIEMPRE son DESCARTA=SI -- un \"gracias\" junto a la frase NO anula el rechazo, sigue siendo un descarte cortes. Si el cliente solo esta dudando, pidiendo tiempo para pensarlo SIN rechazar, o no ha dicho nada negativo, DESCARTA es NO.\n\nY ESALUMNO es SI unicamente si quien escribe YA es alumno matriculado y consulta por temas de soporte/administrativos de su propio curso: no puede asistir a una clase, aviso de inasistencia, psicotecnico, certificado, licencia, examen, avance o horario de SUS clases ya inscritas, documentos, plataforma/Aula Virtual. ESALUMNO es NO si la persona esta recien preguntando por cursos, precios, requisitos o informacion general para decidir inscribirse (eso es una consulta de venta, no de alumno).\n\nEjemplo de respuesta: CALIENTE|EMPRESA|NO|NO\nOtro ejemplo (alumno avisando inasistencia): FRIO|NO|NO|SI\nOtro ejemplo (pidio que lo contacten): CALIENTE|NO|NO|NO\nOtro ejemplo (rechazo cortes: \"no me interesa por ahora gracias\"): FRIO|NO|SI|NO"
            ]]
        ];
        $ch_an = curl_init('https://api.anthropic.com/v1/messages');
        curl_setopt_array($ch_an, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($analisis_payload),
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'x-api-key: '.CLAUDE_API_KEY, 'anthropic-version: 2023-06-01'],
            CURLOPT_TIMEOUT        => 15
        ]);
        $analisis_response = curl_exec($ch_an);
        curl_close($ch_an);

        $analisis_data = json_decode($analisis_response, true);
        $analisis_raw = trim($analisis_data['content'][0]['text'] ?? 'FRIO|NO|NO');
        file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [ANALISIS RAW] $analisis_raw\n\n", FILE_APPEND);

        $partes = explode('|', strtoupper($analisis_raw));
        $temperatura_raw = trim($partes[0] ?? 'FRIO');
        $es_empresa_raw  = trim($partes[1] ?? 'NO');
        $descarta_raw    = trim($partes[2] ?? 'NO');
        $es_alumno_raw   = trim($partes[3] ?? 'NO');

        $temperatura = in_array($temperatura_raw, ['CALIENTE', 'TIBIO', 'FRIO']) ? strtolower($temperatura_raw) : 'frio';
        $es_empresa  = (strpos($es_empresa_raw, 'EMPRESA') !== false);
        $descarta    = (strpos($descarta_raw, 'SI') !== false);
        $es_alumno   = (strpos($es_alumno_raw, 'SI') !== false);

        $pdo->prepare("UPDATE contactos SET temperatura=? WHERE telefono=?")->execute([$temperatura, $from]);

        $nombre_lead = $contact_name ?: $from;
        $stmt_nombre = $pdo->prepare("SELECT nombre FROM contactos WHERE telefono=?");
        $stmt_nombre->execute([$from]);
        $row_nombre = $stmt_nombre->fetch(PDO::FETCH_ASSOC);
        if ($row_nombre && $row_nombre['nombre']) $nombre_lead = $row_nombre['nombre'];

        if ($es_alumno) {
            // Consulta de soporte de un alumno (psicotécnico, certificado, inasistencia, horario de
            // clases, plataforma, etc.), no una consulta comercial. Se separa de las columnas de venta
            // sin importar en qué estado estaba antes, y no se aplica la lógica de descarte/caliente/empresa.
            $stmt_alu = $pdo->prepare("UPDATE contactos SET estado='alumno' WHERE telefono=?");
            $stmt_alu->execute([$from]);
            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [ALUMNO DETECTADO - consulta de soporte, movido a columna Alumnos] Tel: $from\n\n", FILE_APPEND);
        } elseif ($descarta) {
            $stmt_desc = $pdo->prepare("UPDATE contactos SET estado='no_interesado' WHERE telefono=?");
            $stmt_desc->execute([$from]);
            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [DESCARTADO AUTOMÁTICO - cliente rechazó la oferta] Tel: $from\n\n", FILE_APPEND);
        } elseif ($temperatura === 'caliente') {
            $stmt_move = $pdo->prepare("UPDATE contactos SET estado='nuevo' WHERE telefono=? AND estado='valentina'");
            $stmt_move->execute([$from]);
            $filas_afectadas = $stmt_move->rowCount();
            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [CALIENTE] Tel: $from Filas movidas: $filas_afectadas\n\n", FILE_APPEND);

            // Aviso por correo al equipo, una sola vez por lead mientras siga caliente.
            $chk_cal = $pdo->prepare("SELECT caliente_notificada FROM contactos WHERE telefono=?");
            $chk_cal->execute([$from]);
            $row_cal = $chk_cal->fetch(PDO::FETCH_ASSOC);
            $ya_notificado_caliente = $row_cal && !empty($row_cal['caliente_notificada']);

            if (!$ya_notificado_caliente) {
                $asunto_cal = "Lead CALIENTE: $nombre_lead listo para matricular";
                $cuerpo_cal = "Hola equipo ESCOMONTT,\n\nValentina detecto un lead CALIENTE (dio horario especifico, pregunto pago, dio RUT o confirmo matricularse):\n\nNombre/Contacto: $nombre_lead\nTelefono: +$from\nUltimo mensaje: " . substr($user_text, 0, 200) . "\n\nLa tarjeta ya se movio a la columna 'Atender Urgente' en el CRM. Ingresa a revisar y contactar cuanto antes:\nhttps://escomontt.cl/agente/crm.php\n\n-- Valentina IA, ESCOMONTT Capacitacion";
                $rc1 = enviarCorreo('ventas@escomontt.cl', $asunto_cal, $cuerpo_cal);
                $rc2 = enviarCorreo('contacto@escomontt.cl', $asunto_cal, $cuerpo_cal);
                $pdo->prepare("UPDATE contactos SET caliente_notificada=1 WHERE telefono=?")->execute([$from]);
                file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [CALIENTE - Email ventas:" . ($rc1?'OK':'FAIL') . " contacto:" . ($rc2?'OK':'FAIL') . "]\n\n", FILE_APPEND);
            }

            if ($filas_afectadas > 0) {
                avisarHandoffUrgente($pdo, $from, $nombre_lead, $es_horario_laboral);
            }
        }

        if ($es_empresa && !$descarta && !$es_alumno) {
            // Toda consulta de empresa amerita seguimiento humano, así esté fría en temperatura
            // (ej. "necesito hablar con alguien" sin dar RUT/horario/pago). Se mueve a Atender
            // Urgente igual que un lead CALIENTE, solo si sigue en la columna Valentina.
            $stmt_move_emp = $pdo->prepare("UPDATE contactos SET estado='nuevo' WHERE telefono=? AND estado='valentina'");
            $stmt_move_emp->execute([$from]);
            $filas_afectadas_emp = $stmt_move_emp->rowCount();
            if ($filas_afectadas_emp > 0) {
                file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [EMPRESA - movido a Atender Urgente] Tel: $from\n\n", FILE_APPEND);
                avisarHandoffUrgente($pdo, $from, $nombre_lead, $es_horario_laboral);
            }

            $chk_emp = $pdo->prepare("SELECT empresa_notificada FROM contactos WHERE telefono=?");
            $chk_emp->execute([$from]);
            $row_emp = $chk_emp->fetch(PDO::FETCH_ASSOC);
            $ya_notificado = $row_emp && !empty($row_emp['empresa_notificada']);

            file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [CHEQUEO EMPRESA] ya_notificado=" . ($ya_notificado?'SI':'NO') . "\n\n", FILE_APPEND);

            if (!$ya_notificado) {
                $asunto_emp = "Cliente EMPRESA: $nombre_lead consulta por capacitacion";
                $cuerpo_emp = "Hola equipo ESCOMONTT,\n\nValentina detecto una consulta de EMPRESA o Franquicia Tributaria SENCE:\n\nNombre/Contacto: $nombre_lead\nTelefono: +$from\nUltimo mensaje: " . substr($user_text, 0, 200) . "\n\nLa tarjeta se movio a la columna 'Atender Urgente' en el CRM. Ingresa a revisar la conversacion completa y preparar cotizacion:\nhttps://escomontt.cl/agente/crm.php\n\n-- Valentina IA, ESCOMONTT Capacitacion";
                $re1 = enviarCorreo('ventas@escomontt.cl', $asunto_emp, $cuerpo_emp);
                $re2 = enviarCorreo('contacto@escomontt.cl', $asunto_emp, $cuerpo_emp);
                $pdo->prepare("UPDATE contactos SET empresa_notificada=1 WHERE telefono=?")->execute([$from]);
                file_put_contents(__DIR__ . '/webhook_log.txt', date('Y-m-d H:i:s') . " [EMPRESA DETECTADA - Email ventas:" . ($re1?'OK':'FAIL') . " contacto:" . ($re2?'OK':'FAIL') . "]\n\n", FILE_APPEND);
            }
        }

    } catch (Exception $e) {
        file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . " Analisis: " . $e->getMessage() . "\n", FILE_APPEND);
    }

    exit;
}

http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);