<?php
/**
 * modules/turnero/views/chat.php
 * Inbox del número WhatsApp del Turnero.
 * Permite ver y responder mensajes de pacientes que escriben al número turnero.
 */

require_once __DIR__ . '/../../../config/config.php';
if (!isUserLoggedIn()) { header('Location: ' . BASE_URL . 'login.php'); exit; }

$turneroPhoneId = getConfigFromDB('whatsapp_phone_number_id_turnero', '');

// URL de regreso: recepcionista → su desk por IP o primer desk; resto → dashboard
$_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=dashboard';
if (($_SESSION['admin_user']['role'] ?? '') === 'recepcionista') {
    try {
        $pdo    = Database::getInstance()->getConnection();
        $chatIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);

        // 1. Buscar desk por IP
        $st = $pdo->prepare(
            "SELECT d.lugar_id FROM turnero_dispositivos d
             JOIN turnero_lugares l ON l.id = d.lugar_id
             WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
        );
        $st->execute([$chatIp]);
        $lugId = $st->fetchColumn();

        // 2. Fallback: primer desk de recepción activo
        if (!$lugId) {
            $lugId = $pdo->query(
                "SELECT id FROM turnero_lugares WHERE activo=1 AND tipo='recepcion' ORDER BY sort_order LIMIT 1"
            )->fetchColumn();
        }

        if ($lugId) {
            $_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=recepcion&desk_id=' . (int)$lugId;
        }
    } catch (\Throwable $_) {}
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content">
<title>Chat Turnero — WhatsApp</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f0f2f5; }

.chat-shell   { display: flex; height: 100%; }

/* ── Sidebar ── */
.sidebar {
    width: 340px; min-width: 280px; max-width: 380px;
    background: #fff; border-right: 1px solid #e9ecef;
    display: flex; flex-direction: column;
}
.sidebar-header {
    padding: 16px; background: #25d366; color: #fff;
    display: flex; align-items: center; gap: 10px;
}
.sidebar-header h2 { font-size: 1rem; flex: 1; }
.sidebar-header .badge-config {
    font-size: .7rem; background: rgba(0,0,0,.2); padding: 3px 8px; border-radius: 12px;
}
.btn-nuevo-chat {
    background: rgba(255,255,255,.2); border: none; color: #fff; border-radius: 8px;
    padding: 5px 10px; cursor: pointer; font-size: .82rem; font-weight: 700;
    display: flex; align-items: center; gap: 4px; transition: background .15s; flex-shrink: 0;
}
.btn-nuevo-chat:hover { background: rgba(255,255,255,.35); }
.search-box { padding: 8px 12px; border-bottom: 1px solid #f0f0f0; }
.search-box input {
    width: 100%; padding: 8px 12px; border: 1px solid #e0e0e0;
    border-radius: 20px; font-size: .85rem; outline: none;
    background: #f5f5f5;
}
.search-box input:focus { border-color: #25d366; background: #fff; }

.contact-list { flex: 1; overflow-y: auto; }
.contact-item {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 16px; cursor: pointer; border-bottom: 1px solid #f7f7f7;
    transition: background .15s;
}
.contact-item:hover   { background: #f5f5f5; }
.contact-item.active  { background: #e7fce8; }
.contact-avatar {
    width: 44px; height: 44px; border-radius: 50%;
    background: #25d366; color: #fff; display: flex; align-items: center;
    justify-content: center; font-weight: 700; font-size: 1.1rem; flex-shrink: 0;
}
.contact-info  { flex: 1; min-width: 0; }
.contact-name  { font-weight: 600; font-size: .9rem; color: #111; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contact-last  { font-size: .78rem; color: #666; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
.contact-meta  { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.contact-time  { font-size: .7rem; color: #aaa; }
.badge-unread  {
    background: #25d366; color: #fff; font-size: .65rem; font-weight: 700;
    padding: 2px 6px; border-radius: 10px; min-width: 18px; text-align: center;
}
.contact-empty { text-align: center; padding: 40px 16px; color: #aaa; font-size: .85rem; }

/* ── Chat window ── */
.chat-window {
    flex: 1; display: flex; flex-direction: column;
    background: #efeae2;
}
.chat-empty-state {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    justify-content: center; color: #aaa; gap: 12px;
}
.chat-empty-state i { font-size: 3rem; color: #25d366; }
.chat-empty-state p { font-size: .95rem; }

.chat-topbar {
    background: #fff; padding: 12px 16px;
    display: flex; align-items: center; gap: 12px;
    border-bottom: 1px solid #e9ecef; min-height: 60px;
    position: sticky; top: 0; z-index: 10; flex-shrink: 0;
}
.chat-topbar-avatar {
    width: 38px; height: 38px; border-radius: 50%;
    background: #25d366; color: #fff; display: flex;
    align-items: center; justify-content: center; font-weight: 700;
}
.chat-topbar-info h3   { font-size: .95rem; font-weight: 600; }
.chat-topbar-info span { font-size: .78rem; color: #888; }

.messages-area {
    flex: 1; overflow-y: auto; padding: 12px 16px;
    display: flex; flex-direction: column; gap: 6px;
    min-height: 0; -webkit-overflow-scrolling: touch;
}

.msg-bubble {
    max-width: 65%; padding: 8px 12px; border-radius: 8px;
    font-size: .88rem; line-height: 1.4; position: relative;
    word-break: break-word;
}
.msg-bubble.incoming {
    background: #fff; align-self: flex-start;
    border-bottom-left-radius: 2px; box-shadow: 0 1px 2px rgba(0,0,0,.08);
}
.msg-bubble.outgoing {
    background: #d9fdd3; align-self: flex-end;
    border-bottom-right-radius: 2px; box-shadow: 0 1px 2px rgba(0,0,0,.08);
}
.msg-time {
    font-size: .68rem; color: #aaa; text-align: right; margin-top: 3px;
}
.date-sep {
    align-self: center; background: #e9ecef; color: #6c757d;
    font-size: .73rem; font-weight: 600; padding: 3px 12px;
    border-radius: 99px; margin: 10px 0; user-select: none;
}
.msg-type-badge {
    font-size: .7rem; color: #888; font-style: italic;
}
.msg-media-thumb {
    max-width: 220px; max-height: 180px; border-radius: 6px;
    display: block; margin-bottom: 4px; cursor: pointer;
}
.msg-media-link {
    display: flex; align-items: center; gap: 6px; font-size: .82rem; color: #1a73e8;
    text-decoration: none; padding: 4px 0;
}
.msg-media-link:hover { text-decoration: underline; }

.load-more-btn {
    align-self: center; padding: 6px 18px; font-size: .8rem;
    background: #fff; border: 1px solid #ddd; border-radius: 16px;
    cursor: pointer; color: #555;
}
.load-more-btn:hover { background: #f5f5f5; }

/* ── Input area ── */
.chat-input-area {
    background: #f0f2f5; padding: 10px 16px;
    padding-bottom: max(10px, env(safe-area-inset-bottom));
    display: flex; align-items: flex-end; gap: 10px;
    border-top: 1px solid #e0e0e0; flex-shrink: 0;
}
.chat-input-area textarea {
    flex: 1; padding: 10px 14px; border: none; border-radius: 22px;
    background: #fff; font-size: .9rem; resize: none; outline: none;
    max-height: 120px; line-height: 1.4;
    box-shadow: 0 1px 3px rgba(0,0,0,.08);
}
.btn-send {
    width: 42px; height: 42px; border-radius: 50%; border: none;
    background: #25d366; color: #fff; font-size: 1.1rem;
    cursor: pointer; display: flex; align-items: center; justify-content: center;
    flex-shrink: 0; transition: background .2s;
}
.btn-send:hover    { background: #1ebe5c; }
.btn-send:disabled { background: #ccc; cursor: default; }

/* ── Alertas ── */
.alert-no-config {
    background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px;
    padding: 12px 16px; margin: 16px; font-size: .85rem; color: #856404;
    display: flex; align-items: center; gap: 8px;
}
.typing-indicator { font-size: .75rem; color: #888; padding: 2px 8px; }

/* ── Botones extra en input ── */
.input-extra-btn {
    background: none; border: none; color: #888; font-size: 1.15rem;
    cursor: pointer; padding: 6px; border-radius: 50%; transition: background .15s, color .15s;
    flex-shrink: 0;
}
.input-extra-btn:hover { background: rgba(0,0,0,.06); color: #555; }

/* ── Panel emoji ── */
.emoji-panel {
    position: absolute; bottom: 64px; left: 0;
    background: #fff; border: 1px solid #e0e0e0; border-radius: 12px;
    box-shadow: 0 6px 24px rgba(0,0,0,.15); padding: 10px;
    display: none; flex-wrap: wrap; gap: 2px; width: 280px; z-index: 50;
}
.emoji-panel.show { display: flex; }
.emoji-panel span { font-size: 1.3rem; cursor: pointer; padding: 4px; border-radius: 6px; }
.emoji-panel span:hover { background: #f0f2f5; }

/* ── Reaction picker ── */
#reaction-picker {
    position: fixed; display: none; z-index: 9000;
    background: #fff; border-radius: 30px; padding: 6px 10px;
    box-shadow: 0 4px 20px rgba(0,0,0,.18);
    gap: 4px; align-items: center;
}
#reaction-picker.show { display: flex; }
#reaction-picker span { font-size: 1.4rem; cursor: pointer; padding: 4px 6px; border-radius: 50%; transition: transform .1s; }
#reaction-picker span:hover { transform: scale(1.3); }
.msg-bubble { position: relative; }
.msg-bubble:hover .react-btn { opacity: 1; }
.react-btn {
    position: absolute; top: 4px; opacity: 0; transition: opacity .15s;
    background: #fff; border: 1px solid #e0e0e0; border-radius: 50%;
    width: 24px; height: 24px; font-size: .75rem; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 1px 4px rgba(0,0,0,.12);
}
.msg-bubble.incoming .react-btn { right: -28px; }
.msg-bubble.outgoing .react-btn { left: -28px; }
.msg-reaction { font-size: .95rem; margin-top: 2px; }

/* ── Preview adjunto ── */
#attach-preview {
    display: none; align-items: center; gap: 8px;
    padding: 8px 12px; background: #e9f5fe;
    border-top: 1px solid #bee3f8; font-size: .85rem;
}
#attach-preview img { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; }
#attach-preview .attach-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#attach-preview .attach-remove { background: none; border: none; color: #e53e3e; cursor: pointer; font-size: 1rem; }

/* ── Recording indicator ── */
#recording-bar {
    display: none; align-items: center; gap: 10px;
    padding: 8px 14px; background: #fff0f0; border-top: 1px solid #fed7d7;
    font-size: .85rem; color: #e53e3e;
}
#recording-bar .rec-dot { width: 10px; height: 10px; border-radius: 50%; background: #e53e3e; animation: blink 1s infinite; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:.2} }

/* ── Modal plantilla (2 pasos) ── */
#tpl-modal {
    display: none; position: fixed; inset: 0; z-index: 1000;
    background: rgba(0,0,0,.45); align-items: center; justify-content: center; padding: 16px;
}
#tpl-modal.show { display: flex; }
.tpl-dialog { background: #fff; border-radius: 14px; width: 100%; max-width: 400px; box-shadow: 0 8px 32px rgba(0,0,0,.22); overflow: hidden; }
.tpl-hdr { display: flex; align-items: center; gap: 8px; padding: 13px 16px; border-bottom: 1px solid #f1f5f9; font-size: .92rem; font-weight: 700; color: #1e293b; }
.tpl-hdr > span { flex: 1; }
.tpl-close-btn { background: none; border: none; cursor: pointer; color: #94a3b8; font-size: .95rem; padding: 2px 7px; border-radius: 4px; }
.tpl-close-btn:hover { color: #475569; background: #f1f5f9; }
.tpl-back-btn { background: none; border: none; cursor: pointer; color: #64748b; font-size: .85rem; padding: 2px 7px; border-radius: 4px; flex-shrink: 0; }
.tpl-back-btn:hover { background: #f1f5f9; }
.tpl-item { display: flex; flex-direction: column; gap: 2px; padding: 9px 12px; cursor: pointer; border-radius: 8px; margin: 0 6px; transition: background .12s; }
.tpl-item:hover { background: #f0fdf4; }
.tpl-item-name { font-size: .87rem; font-weight: 600; color: #1e293b; }
.tpl-item-meta { font-size: .74rem; color: #64748b; }
.tpl-item-body { font-size: .74rem; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tpl-preview-box { background: #f0fdf4; border-left: 3px solid #25d366; border-radius: 0 6px 6px 0; padding: 8px 12px; font-size: .82rem; color: #374151; margin: 10px 16px 2px; white-space: pre-wrap; word-break: break-word; }
.tpl-var-row { padding: 0 16px; }
.tpl-var-row label { font-size: .78rem; font-weight: 600; color: #475569; display: block; margin-bottom: 3px; }
.tpl-var-row input { width: 100%; padding: 7px 10px; border: 1.5px solid #e2e8f0; border-radius: 8px; font-size: .87rem; outline: none; box-sizing: border-box; }
.tpl-var-row input:focus { border-color: #25d366; }
.tpl-actions { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 16px; border-top: 1px solid #f1f5f9; }
.btn-tpl-cancel { padding: 8px 16px; background: #f5f5f5; border: none; border-radius: 8px; cursor: pointer; font-size: .88rem; }
.btn-tpl-send { padding: 8px 16px; background: #25d366; color: #fff; border: none; border-radius: 8px; cursor: pointer; font-size: .88rem; font-weight: 600; }

/* ── Media en burbuja ── */
.msg-img { max-width: 220px; max-height: 200px; border-radius: 8px; cursor: pointer; display: block; margin-bottom: 3px; }
.msg-video { max-width: 220px; border-radius: 8px; display: block; margin-bottom: 3px; }
.msg-audio { width: 200px; margin-bottom: 3px; }
.msg-doc-link { display: flex; align-items: center; gap: 7px; font-size: .83rem; color: #1a73e8; text-decoration: none; padding: 4px 0; }
.msg-doc-link:hover { text-decoration: underline; }

.back-btn { display: none; background: none; border: none; color: #fff; font-size: 1.1rem; cursor: pointer; padding: 4px 8px; }

/* ── Modal nuevo mensaje ── */
#nuevo-modal { display:none; position:fixed; inset:0; z-index:1100;
    background:rgba(0,0,0,.45); align-items:center; justify-content:center; padding:16px; }
#nuevo-modal.show { display:flex; }
.nv-dialog { background:#fff; border-radius:14px; width:100%; max-width:420px;
    box-shadow:0 8px 32px rgba(0,0,0,.22); overflow:hidden; display:flex; flex-direction:column; max-height:90vh; }
.nv-hdr { display:flex; align-items:center; gap:8px; padding:13px 16px;
    border-bottom:1px solid #f1f5f9; font-size:.92rem; font-weight:700; color:#1e293b; flex-shrink:0; }
.nv-hdr > span { flex:1; }
.nv-body { padding:14px 16px; overflow-y:auto; flex:1; }
.nv-footer { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px;
    border-top:1px solid #f1f5f9; flex-shrink:0; }
.nv-search { width:100%; padding:8px 12px; border:1.5px solid #e2e8f0; border-radius:8px;
    font-size:.87rem; outline:none; box-sizing:border-box; margin-bottom:10px; }
.nv-search:focus { border-color:#25d366; }
.nv-pac-item { display:flex; flex-direction:column; gap:1px; padding:9px 10px; cursor:pointer;
    border-radius:8px; transition:background .12s; border:1px solid transparent; }
.nv-pac-item:hover { background:#f0fdf4; border-color:#bbf7d0; }
.nv-pac-name { font-size:.87rem; font-weight:600; color:#1e293b; }
.nv-pac-sub { font-size:.75rem; color:#64748b; }
.nv-divider { display:flex; align-items:center; gap:8px; margin:10px 0;
    font-size:.75rem; color:#94a3b8; }
.nv-divider::before,.nv-divider::after { content:''; flex:1; height:1px; background:#e2e8f0; }
.nv-destinatario { display:flex; align-items:center; gap:8px; padding:8px 10px;
    background:#f0fdf4; border:1px solid #bbf7d0; border-radius:8px; margin-bottom:10px; font-size:.85rem; }
.nv-destinatario strong { flex:1; color:#15803d; }
.nv-back-btn { background:none; border:none; cursor:pointer; color:#64748b;
    font-size:.85rem; padding:2px 7px; border-radius:4px; }
.nv-back-btn:hover { background:#f1f5f9; }
.nv-close-btn { background:none; border:none; cursor:pointer; color:#94a3b8;
    font-size:.95rem; padding:2px 7px; border-radius:4px; }
.nv-close-btn:hover { color:#475569; background:#f1f5f9; }

@media (max-width: 680px) {
    .chat-shell { position: relative; overflow: hidden; }
    .sidebar { position: absolute; inset: 0; z-index: 10; transition: transform .2s ease; }
    .sidebar.mobile-hidden { transform: translateX(-100%); pointer-events: none; }
    .chat-window { position: absolute; inset: 0; z-index: 5; transform: translateX(100%); height: 100%; transition: transform .2s ease; }
    .chat-window.mobile-active { transform: translateX(0); }
    .back-btn { display: flex; }
}
</style>
</head>
<body>
<div class="chat-shell">

    <!-- ── Sidebar: lista de contactos ── -->
    <div class="sidebar" id="sidebar">
        <div class="sidebar-header">
            <a href="<?= htmlspecialchars($_chatBackUrl) ?>"
               style="color:rgba(255,255,255,.7);font-size:.85rem;text-decoration:none;display:flex;align-items:center;gap:5px;margin-bottom:8px">
                <i class="fas fa-arrow-left"></i> Menú
            </a>
            <i class="fab fa-whatsapp" style="font-size:1.4rem"></i>
            <h2>Chat Turnero</h2>
            <?php if ($turneroPhoneId): ?>
                <span class="badge-config" title="Número turnero configurado">
                    <i class="fas fa-check-circle"></i> Activo
                </span>
            <?php else: ?>
                <span class="badge-config" style="background:rgba(220,53,69,.6)" title="Sin configurar">
                    <i class="fas fa-exclamation-circle"></i> Sin config
                </span>
            <?php endif; ?>
            <button class="btn-nuevo-chat" onclick="nuevoMensaje.abrir()" title="Nuevo mensaje">
                <i class="fas fa-plus"></i> Nuevo
            </button>
        </div>
        <div class="search-box">
            <input type="text" id="searchInput" placeholder="Buscar contacto…" autocomplete="off">
        </div>
        <div class="contact-list" id="contactList">
            <div class="contact-empty"><i class="fas fa-spinner fa-spin"></i><br>Cargando…</div>
        </div>
    </div>

    <!-- ── Ventana de chat ── -->
    <div class="chat-window" id="chatWindow">

        <?php if (!$turneroPhoneId): ?>
        <div class="alert-no-config">
            <i class="fas fa-exclamation-triangle"></i>
            El número de WhatsApp del Turnero no está configurado.
            <a href="/erp.php?m=turnero&v=configuracion" style="margin-left:8px;color:#856404;font-weight:600;">Configurar ahora</a>
        </div>
        <?php endif; ?>

        <div class="chat-empty-state" id="emptyState">
            <i class="fab fa-whatsapp"></i>
            <p>Selecciona un contacto para ver la conversación</p>
        </div>

        <div id="activeChat" style="display:none; flex:1; flex-direction:column; overflow:hidden;">
            <div class="chat-topbar" id="chatTopbar">
                <button class="back-btn" onclick="closeChatMobile()"><i class="fas fa-arrow-left"></i></button>
                <div class="chat-topbar-avatar" id="topbarAvatar">?</div>
                <div class="chat-topbar-info">
                    <h3 id="topbarName">—</h3>
                    <span id="topbarPhone">—</span>
                </div>
            </div>

            <div class="messages-area" id="messagesArea">
                <button class="load-more-btn" id="loadMoreBtn" style="display:none" onclick="loadMoreMessages()">
                    <i class="fas fa-chevron-up"></i> Ver mensajes anteriores
                </button>
            </div>

            <div id="typingIndicator" class="typing-indicator" style="display:none">Escribiendo…</div>

            <!-- Preview adjunto -->
            <div id="attach-preview">
                <img id="attachThumb" src="" alt="" style="display:none">
                <span class="attach-name" id="attachName"></span>
                <button class="attach-remove" onclick="clearAttach()">✕</button>
            </div>

            <!-- Barra de grabación -->
            <div id="recording-bar">
                <div class="rec-dot"></div>
                <span>Grabando…</span>
                <span id="recTimer" style="font-weight:700;min-width:32px">0:00</span>
                <button onclick="stopRecording()" style="margin-left:auto;background:#e53e3e;color:#fff;border:none;border-radius:6px;padding:4px 12px;cursor:pointer;font-size:.82rem">Enviar</button>
                <button onclick="cancelRecording()" style="background:#f5f5f5;border:none;border-radius:6px;padding:4px 10px;cursor:pointer;font-size:.82rem">Cancelar</button>
            </div>

            <input type="file" id="fileInput" style="display:none"
                accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx,.csv"
                onchange="onFileSelected(event)">

            <div class="chat-input-area" style="position:relative">
                <div class="emoji-panel" id="emojiPanel"></div>
                <button class="input-extra-btn" onclick="toggleEmojiPanel()" title="Emoji"><i class="fas fa-smile"></i></button>
                <button class="input-extra-btn" onclick="document.getElementById('fileInput').click()" title="Adjuntar"><i class="fas fa-paperclip"></i></button>
                <button class="input-extra-btn" id="micBtn" onclick="toggleRecording()" title="Grabar audio"><i class="fas fa-microphone"></i></button>
                <textarea id="msgInput" rows="1" placeholder="Escribe un mensaje…"
                    onkeydown="handleKey(event)" oninput="autoResize(this)"></textarea>
                <button class="input-extra-btn" onclick="openTemplatePicker()" title="Plantilla"><i class="fas fa-file-alt"></i></button>
                <button class="btn-send" id="btnSend" onclick="sendMessage()" <?= $turneroPhoneId ? '' : 'disabled' ?>>
                    <i class="fas fa-paper-plane"></i>
                </button>
            </div>
        </div>

    </div><!-- /chat-window -->
</div><!-- /chat-shell -->

<!-- ── Reaction picker ── -->
<div id="reaction-picker">
    <?php foreach (['👍','❤️','😂','😮','😢','🙏','🔥','👏'] as $em): ?>
    <span onclick="sendReactionEmoji('<?= $em ?>')"><?= $em ?></span>
    <?php endforeach; ?>
</div>

<!-- ── Modal nuevo mensaje (3 pasos) ── -->
<div id="nuevo-modal">
    <!-- Paso 1: destinatario -->
    <div class="nv-dialog" id="nv-step-dest">
        <div class="nv-hdr">
            <span><i class="fas fa-user-plus me-2" style="color:#25d366"></i>Nuevo mensaje</span>
            <button class="nv-close-btn" onclick="nuevoMensaje.cerrar()">✕</button>
        </div>
        <div class="nv-body">
            <input class="nv-search" id="nv-pac-search" placeholder="🔍 Buscar paciente por nombre o cédula…"
                   autocomplete="off" oninput="nuevoMensaje.buscarPaciente(this.value)">
            <div id="nv-pac-results" style="display:flex;flex-direction:column;gap:4px;min-height:40px"></div>
            <div class="nv-divider">o ingresa el número directamente</div>
            <input class="nv-search" id="nv-phone-input" placeholder="📱 Número de WhatsApp (ej: 3001234567)"
                   autocomplete="off" inputmode="tel"
                   oninput="nuevoMensaje.onPhoneInput(this.value)">
            <div id="nv-phone-hint" style="font-size:.76rem;color:#94a3b8;margin-top:-6px;margin-bottom:4px"></div>
        </div>
        <div class="nv-footer">
            <button class="btn btn-outline-secondary btn-sm" onclick="nuevoMensaje.cerrar()">Cancelar</button>
            <button class="btn btn-success btn-sm fw-semibold" id="nv-btn-siguiente"
                    onclick="nuevoMensaje.irAPlantilla()" disabled>
                Siguiente <i class="fas fa-arrow-right ms-1"></i>
            </button>
        </div>
    </div>
    <!-- Paso 2: plantilla -->
    <div class="nv-dialog" id="nv-step-tpl" style="display:none">
        <div class="nv-hdr">
            <button class="nv-back-btn" onclick="nuevoMensaje.volverADest()"><i class="fas fa-arrow-left"></i></button>
            <span>Seleccionar plantilla</span>
            <button class="nv-close-btn" onclick="nuevoMensaje.cerrar()">✕</button>
        </div>
        <div class="nv-body">
            <div class="nv-destinatario">
                <i class="fas fa-user-circle" style="color:#25d366;font-size:1.1rem"></i>
                <strong id="nv-dest-label">—</strong>
            </div>
            <div id="nv-tpl-list" style="display:flex;flex-direction:column;gap:4px;overflow-y:auto;max-height:300px"></div>
        </div>
    </div>
    <!-- Paso 3: variables -->
    <div class="nv-dialog" id="nv-step-vars" style="display:none">
        <div class="nv-hdr">
            <button class="nv-back-btn" onclick="nuevoMensaje.volverAPlantilla()"><i class="fas fa-arrow-left"></i></button>
            <span id="nv-vars-title">Completar variables</span>
            <button class="nv-close-btn" onclick="nuevoMensaje.cerrar()">✕</button>
        </div>
        <div class="nv-body">
            <div id="nv-preview-box" style="display:none;background:#f0fdf4;border-left:3px solid #25d366;
                border-radius:0 6px 6px 0;padding:8px 12px;font-size:.82rem;color:#374151;
                margin-bottom:10px;white-space:pre-wrap;word-break:break-word"></div>
            <div id="nv-vars-body" style="display:flex;flex-direction:column;gap:.6rem"></div>
        </div>
        <div class="nv-footer">
            <button class="btn btn-outline-secondary btn-sm" onclick="nuevoMensaje.volverAPlantilla()">Atrás</button>
            <button class="btn btn-success btn-sm fw-semibold" onclick="nuevoMensaje.enviar()">
                <i class="fas fa-paper-plane me-1"></i>Enviar
            </button>
        </div>
    </div>
</div>

<!-- ── Modal plantilla (2 pasos) ── -->
<div id="tpl-modal">
    <!-- Paso 1: lista -->
    <div class="tpl-dialog" id="tpl-step-list">
        <div class="tpl-hdr">
            <span><i class="fas fa-file-alt" style="color:#25d366;margin-right:6px"></i>Seleccionar plantilla</span>
            <button class="tpl-close-btn" onclick="closeTemplatePicker()">✕</button>
        </div>
        <div id="tpl-list-body" style="overflow-y:auto;max-height:360px;display:flex;flex-direction:column;gap:.25rem;padding:.5rem 0"></div>
    </div>
    <!-- Paso 2: variables -->
    <div class="tpl-dialog" id="tpl-step-vars" style="display:none">
        <div class="tpl-hdr">
            <button class="tpl-back-btn" onclick="tplGoBack()"><i class="fas fa-arrow-left"></i></button>
            <span id="tpl-vars-title">Completar variables</span>
            <button class="tpl-close-btn" onclick="closeTemplatePicker()">✕</button>
        </div>
        <div id="tpl-preview-box" style="display:none" class="tpl-preview-box"></div>
        <div id="tpl-vars-body" style="display:flex;flex-direction:column;gap:.6rem;padding:.6rem 0"></div>
        <div class="tpl-actions">
            <button class="btn-tpl-cancel" onclick="tplGoBack()">Atrás</button>
            <button class="btn-tpl-send" onclick="sendTemplate()"><i class="fas fa-paper-plane me-1"></i>Enviar</button>
        </div>
    </div>
</div>

<script>
const API_LIST       = 'modules/turnero/api/chat_get_list.php';
const API_MESSAGES   = 'modules/turnero/api/chat_get_messages.php';
const API_SEND       = 'modules/turnero/api/chat_send_message.php';
const API_READ       = 'modules/turnero/api/chat_mark_read.php';
const API_MEDIA      = 'modules/turnero/api/chat_upload_media.php';
const API_REACT      = 'modules/turnero/api/chat_react.php';
const API_PLANTILLAS  = 'modules/turnero/api/chat_get_plantillas.php';
const API_BUSCAR_PAC  = 'modules/turnero/api/chat_buscar_paciente.php';
const API_START_CONV  = 'modules/turnero/api/chat_start_conversation.php';
let _tplSelected = null;
const BASE         = (function() {
    const s = window.location.pathname;
    return s.replace(/\/erp\.php.*$/, '/') || '/';
})();

let state = {
    contacts: [],
    activeUserId: null,
    activeUser: null,
    messages: [],
    earliest: null,
    earliestId: null,
    hasMore: false,
    polling: null,
    lastPollTime: null,
};

// ── Init ─────────────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', () => {
    loadContacts();
    // Permite llegar directo a una conversación desde otra pantalla, por ejemplo
    // desde la bitácora del turno en el historial: ?v=chat&user_id=123
    const _uid = parseInt(new URLSearchParams(location.search).get('user_id') || '', 10);
    if (_uid > 0) openChat(_uid);
    document.getElementById('searchInput').addEventListener('input', debounce(() => {
        loadContacts(document.getElementById('searchInput').value.trim());
    }, 300));
    setInterval(pollMessages, 4000);
    setInterval(() => loadContacts(document.getElementById('searchInput').value.trim(), true), 12000);
});

function debounce(fn, ms) {
    let t;
    return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}

// ── Contactos ─────────────────────────────────────────────────────────────────
async function loadContacts(search = '', silent = false) {
    const url = BASE + API_LIST + '?limit=100' + (search ? '&search=' + encodeURIComponent(search) : '');
    try {
        const res  = await fetch(url);
        const json = await res.json();
        if (!json.success) return;
        state.contacts = json.data;
        renderContacts(json.data);
    } catch(e) {
        if (!silent) console.error('loadContacts:', e);
    }
}

function renderContacts(contacts) {
    const el = document.getElementById('contactList');
    if (!contacts.length) {
        el.innerHTML = '<div class="contact-empty"><i class="fas fa-comment-slash"></i><br>Sin conversaciones aún</div>';
        return;
    }
    el.innerHTML = contacts.map(c => {
        const initials = (c.name || c.phone_number).charAt(0).toUpperCase();
        const preview  = escHtml(c.last_message || '—');
        const time     = c.last_time ? formatTime(c.last_time) : '';
        const unread   = c.unread_count > 0 ? `<span class="badge-unread">${c.unread_count}</span>` : '';
        const active   = state.activeUserId === c.user_id ? ' active' : '';
        return `<div class="contact-item${active}" onclick="openChat(${c.user_id})">
            <div class="contact-avatar">${initials}</div>
            <div class="contact-info">
                <div class="contact-name">${escHtml(c.name || c.phone_number)}</div>
                <div class="contact-last">${preview}</div>
            </div>
            <div class="contact-meta">
                <span class="contact-time">${time}</span>
                ${unread}
            </div>
        </div>`;
    }).join('');
}

// ── Abrir chat ────────────────────────────────────────────────────────────────
async function openChat(userId) {
    state.activeUserId = userId;
    state.messages     = [];
    state.earliest     = null;
    state.earliestId   = null;
    state.hasMore      = false;
    state.lastPollTime = null;

    const contact = state.contacts.find(c => c.user_id === userId);
    state.activeUser = contact;

    document.getElementById('emptyState').style.display  = 'none';
    const activeEl = document.getElementById('activeChat');
    activeEl.style.display    = 'flex';
    activeEl.style.flexDirection = 'column';
    activeEl.style.overflow   = 'hidden';
    activeEl.style.flex       = '1';

    const initials = contact ? (contact.name || contact.phone_number).charAt(0).toUpperCase() : '?';
    document.getElementById('topbarAvatar').textContent = initials;
    document.getElementById('topbarName').textContent   = contact ? (contact.name || contact.phone_number) : '…';
    document.getElementById('topbarPhone').textContent  = contact ? contact.phone_number : '';

    document.querySelectorAll('.contact-item').forEach(el => el.classList.remove('active'));
    const items = document.querySelectorAll('.contact-item');
    items.forEach(el => { if (el.onclick.toString().includes(`openChat(${userId})`)) el.classList.add('active'); });

    const area = document.getElementById('messagesArea');
    area.innerHTML = '<div style="text-align:center;padding:20px;color:#aaa"><i class="fas fa-spinner fa-spin"></i></div>';

    await fetchMessages();
    markRead(userId);
    loadContacts(document.getElementById('searchInput').value.trim(), true);
    openChatMobile();
}

function isMobile() { return window.innerWidth <= 680; }
function openChatMobile() {
    if (!isMobile()) return;
    document.getElementById('sidebar').classList.add('mobile-hidden');
    document.getElementById('chatWindow').classList.add('mobile-active');
}
function closeChatMobile() {
    if (!isMobile()) return;
    document.getElementById('sidebar').classList.remove('mobile-hidden');
    document.getElementById('chatWindow').classList.remove('mobile-active');
}

// ── Mensajes ──────────────────────────────────────────────────────────────────
async function fetchMessages() {
    if (!state.activeUserId) return;
    const forUser = state.activeUserId; // captura antes del await
    const url = BASE + API_MESSAGES + '?user_id=' + forUser + '&limit=50';
    try {
        const res  = await fetch(url);
        const json = await res.json();
        if (forUser !== state.activeUserId) return; // usuario cambió mientras esperaba
        if (!json.success) return;
        state.messages     = json.data;
        state.hasMore      = json.has_more;
        state.earliest     = json.earliest;
        state.earliestId   = json.earliest_id;
        state.lastPollTime = json.data.length ? json.data[json.data.length - 1].created_at : null;
        renderMessages(json.data, false);
    } catch(e) { console.error('fetchMessages:', e); }
}

async function loadMoreMessages() {
    if (!state.activeUserId || !state.earliest) return;
    const forUser = state.activeUserId;
    const url = BASE + API_MESSAGES
        + '?user_id=' + forUser
        + '&limit=50'
        + '&before=' + encodeURIComponent(state.earliest)
        + (state.earliestId ? '&before_id=' + state.earliestId : '');
    try {
        const res  = await fetch(url);
        const json = await res.json();
        if (forUser !== state.activeUserId) return;
        if (!json.success || !json.data.length) {
            document.getElementById('loadMoreBtn').style.display = 'none';
            return;
        }
        state.messages    = [...json.data, ...state.messages];
        state.hasMore     = json.has_more;
        state.earliest    = json.earliest;
        state.earliestId  = json.earliest_id;
        prependMessages(json.data);
    } catch(e) { console.error('loadMoreMessages:', e); }
}

async function pollMessages() {
    if (!state.activeUserId || !state.lastPollTime) return;
    const forUser    = state.activeUserId;
    const sinceTime  = state.lastPollTime;
    const url = BASE + API_MESSAGES
        + '?user_id=' + forUser
        + '&limit=50'
        + '&since=' + encodeURIComponent(sinceTime);
    try {
        const res  = await fetch(url);
        const json = await res.json();
        if (forUser !== state.activeUserId) return; // usuario cambió mientras esperaba
        if (!json.success || !json.data.length) return;
        json.data.forEach(m => {
            if (!state.messages.find(x => x.id === m.id)) {
                state.messages.push(m);
                appendMessage(m);
            }
        });
        state.lastPollTime = json.data[json.data.length - 1].created_at;
        markRead(forUser);
        loadContacts(document.getElementById('searchInput').value.trim(), true);
    } catch(e) {}
}

// ── Render mensajes ───────────────────────────────────────────────────────────
let _lastRenderedDay = null;

function renderMessages(msgs, prepend) {
    const area = document.getElementById('messagesArea');
    area.innerHTML = '';

    const loadBtn = document.createElement('button');
    loadBtn.className  = 'load-more-btn';
    loadBtn.id         = 'loadMoreBtn';
    loadBtn.style.display = state.hasMore ? 'block' : 'none';
    loadBtn.onclick    = loadMoreMessages;
    loadBtn.innerHTML  = '<i class="fas fa-chevron-up"></i> Ver mensajes anteriores';
    area.appendChild(loadBtn);

    let lastDay = null;
    msgs.forEach(m => {
        const day = m.created_at ? msgDay(m.created_at) : null;
        if (day && day !== lastDay) {
            area.appendChild(buildDateSep(dateLabel(m.created_at)));
            lastDay = day;
        }
        area.appendChild(buildBubble(m));
    });
    _lastRenderedDay = lastDay;
    area.scrollTop = area.scrollHeight;
}

function prependMessages(msgs) {
    const area    = document.getElementById('messagesArea');
    const oldTop  = area.scrollHeight - area.scrollTop;
    const loadBtn = document.getElementById('loadMoreBtn');
    // Día del primer mensaje que ya estaba en pantalla (para no duplicar separador)
    const firstBubble = loadBtn.nextSibling;
    const firstExistingDay = firstBubble?.dataset?.msgId
        ? msgDay(msgs[msgs.length - 1]?.created_at || '')  // comparar con el último del lote nuevo
        : null;
    let lastDay = null;
    let insertRef = loadBtn.nextSibling;
    msgs.forEach(m => {
        const day = m.created_at ? msgDay(m.created_at) : null;
        if (day && day !== lastDay) {
            area.insertBefore(buildDateSep(dateLabel(m.created_at)), insertRef);
            lastDay = day;
        }
        area.insertBefore(buildBubble(m), insertRef);
        insertRef = area.querySelector(`[data-msg-id="${m.id}"]`)?.nextSibling || insertRef;
    });
    // Eliminar separador duplicado si el último día del lote coincide con el primero ya visible
    const seps = area.querySelectorAll('.date-sep');
    for (let i = 0; i < seps.length - 1; i++) {
        if (seps[i].textContent === seps[i+1].textContent) seps[i+1].remove();
    }
    if (!state.hasMore) loadBtn.style.display = 'none';
    area.scrollTop = area.scrollHeight - oldTop;
}

function appendMessage(msg) {
    const area = document.getElementById('messagesArea');
    const day  = msg.created_at ? msgDay(msg.created_at) : null;
    if (day && day !== _lastRenderedDay) {
        area.appendChild(buildDateSep(dateLabel(msg.created_at)));
        _lastRenderedDay = day;
    }
    area.appendChild(buildBubble(msg));
    area.scrollTop = area.scrollHeight;
}

function buildBubble(msg) {
    const div = document.createElement('div');
    div.className = 'msg-bubble ' + (msg.direction === 'outgoing' ? 'outgoing' : 'incoming');
    div.dataset.msgId    = msg.id;
    div.dataset.waId     = msg.message_id || '';

    const _base = window.location.origin;
    const _toAbs = p => p ? (p.startsWith('http') ? p : `${_base}/${p.replace(/^\//, '')}`) : null;
    const mediaSrc = msg.local_file ? _toAbs(msg.local_file) : (msg.media_url_external ? _toAbs(msg.media_url_external) : null);
    let content = '';

    if (msg.message_type === 'image' && mediaSrc) {
        content = `<img class="msg-img" src="${escHtml(mediaSrc)}" loading="lazy"
            onclick="window.open('${escHtml(mediaSrc)}','_blank')" alt="imagen">`;
        if (msg.content) content += `<div style="font-size:.82rem;margin-top:2px">${escHtml(msg.content)}</div>`;
    } else if (msg.message_type === 'video' && mediaSrc) {
        content = `<video class="msg-video" src="${escHtml(mediaSrc)}" controls preload="metadata"></video>`;
        if (msg.content) content += `<div style="font-size:.82rem;margin-top:2px">${escHtml(msg.content)}</div>`;
    } else if (msg.message_type === 'audio') {
        if (mediaSrc) {
            content = `<audio class="msg-audio" controls preload="metadata"
                onerror="this.outerHTML='<div style=\\'font-size:.78rem;color:#e53e3e;padding:4px 0\\'><i class=\\'fas fa-exclamation-circle\\'></i> Audio no disponible</div>'">
                <source src="${escHtml(mediaSrc)}" type="audio/ogg">
                <source src="${escHtml(mediaSrc)}" type="audio/mpeg">
                <source src="${escHtml(mediaSrc)}" type="audio/wav">
                <source src="${escHtml(mediaSrc)}" type="audio/webm">
            </audio>`;
        } else {
            content = `<div style="font-size:.78rem;color:#aaa;padding:4px 0"><i class="fas fa-microphone-slash"></i> Audio pendiente de descarga</div>`;
        }
    } else if (msg.message_type === 'document' && mediaSrc) {
        const icon = (msg.mime_type || '').includes('pdf') ? 'fa-file-pdf' :
                     (msg.mime_type || '').includes('spreadsheet') || (msg.mime_type || '').includes('excel') ? 'fa-file-excel' :
                     (msg.mime_type || '').includes('word') ? 'fa-file-word' : 'fa-file-alt';
        content = `<a class="msg-doc-link" href="${escHtml(mediaSrc)}" target="_blank">
            <i class="fas ${icon}" style="font-size:1.3rem"></i>
            <span>${escHtml(msg.filename || 'Documento')}</span>
        </a>`;
    } else {
        content = nl2br(escHtml(msg.content || ''));
    }

    const time = msg.created_at ? formatTimeFull(msg.created_at) : '';
    const statusIcon = msg.direction === 'outgoing'
        ? (msg.status === 'read' ? ' ✓✓' : msg.status === 'delivered' ? ' ✓✓' : ' ✓')
        : '';

    const reactBtn = msg.message_id
        ? `<button class="react-btn" onclick="showReactionPicker(event, '${escHtml(msg.message_id)}')">😊</button>`
        : '';

    div.innerHTML = content
        + `<div class="msg-time">${time}<span style="opacity:.7;font-size:.7rem">${statusIcon}</span></div>`
        + (msg.reaction_emoji ? `<div class="msg-reaction">${escHtml(msg.reaction_emoji)}</div>` : '')
        + reactBtn;
    return div;
}

// ── Enviar texto ─────────────────────────────────────────────────────────────
async function sendMessage() {
    // Si hay archivo adjunto, enviar como media
    if (attachFile) { await sendMedia(); return; }

    const input   = document.getElementById('msgInput');
    const message = input.value.trim();
    if (!message || !state.activeUserId) return;

    const btn = document.getElementById('btnSend');
    btn.disabled = true;
    input.value  = '';
    autoResize(input);

    try {
        const res  = await fetch(BASE + API_SEND, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ user_id: state.activeUserId, message })
        });
        const json = await res.json();
        if (!json.success) {
            alert('Error al enviar: ' + (json.error || 'desconocido'));
            input.value = message;
        } else {
            await pollMessages();
        }
    } catch(e) {
        alert('Error de red al enviar el mensaje');
        input.value = message;
    } finally {
        btn.disabled = false;
        input.focus();
    }
}

// ── Adjuntar archivo ──────────────────────────────────────────────────────────
let attachFile = null;

function onFileSelected(e) {
    const file = e.target.files[0];
    if (!file) return;
    attachFile = file;
    const prev = document.getElementById('attach-preview');
    const thumb = document.getElementById('attachThumb');
    const name  = document.getElementById('attachName');
    name.textContent = file.name + ' (' + (file.size > 1048576 ? (file.size/1048576).toFixed(1) + ' MB' : Math.round(file.size/1024) + ' KB') + ')';
    if (file.type.startsWith('image/')) {
        thumb.src = URL.createObjectURL(file);
        thumb.style.display = 'block';
    } else {
        thumb.style.display = 'none';
    }
    prev.style.display = 'flex';
    e.target.value = '';
}

function clearAttach() {
    attachFile = null;
    document.getElementById('attach-preview').style.display = 'none';
    document.getElementById('attachThumb').src = '';
}

async function sendMedia() {
    if (!attachFile || !state.activeUserId) return;
    const btn = document.getElementById('btnSend');
    btn.disabled = true;
    const caption = document.getElementById('msgInput').value.trim();

    const fd = new FormData();
    fd.append('user_id', state.activeUserId);
    fd.append('file', attachFile);
    if (caption) fd.append('caption', caption);

    try {
        const res  = await fetch(BASE + API_MEDIA, { method: 'POST', body: fd });
        const json = await res.json();
        if (!json.success) {
            alert('Error al enviar archivo: ' + (json.error || 'desconocido'));
        } else {
            clearAttach();
            document.getElementById('msgInput').value = '';
            autoResize(document.getElementById('msgInput'));
            await pollMessages();
        }
    } catch(e) {
        alert('Error de red al enviar el archivo');
    } finally {
        btn.disabled = false;
    }
}

// ── Emoji picker ──────────────────────────────────────────────────────────────
const EMOJI_LIST = ['😀','😂','😍','😎','😢','😡','🤔','👍','👎','❤️','🔥','✅','⭐','🎉','🙏','💪','🤣','😅','😊','🥰','😘','🤗','😴','🤒','😷','💊','🏥','👨‍⚕️','👩‍⚕️','📋','📞','📅','⏰','💉','🩺'];

function toggleEmojiPanel() {
    const panel = document.getElementById('emojiPanel');
    if (!panel.classList.contains('show')) {
        panel.innerHTML = EMOJI_LIST.map(e => `<span onclick="insertEmoji('${e}')">${e}</span>`).join('');
    }
    panel.classList.toggle('show');
}

function insertEmoji(em) {
    const inp = document.getElementById('msgInput');
    const pos = inp.selectionStart;
    inp.value = inp.value.slice(0, pos) + em + inp.value.slice(pos);
    inp.selectionStart = inp.selectionEnd = pos + em.length;
    inp.focus();
    autoResize(inp);
    document.getElementById('emojiPanel').classList.remove('show');
}

document.addEventListener('click', e => {
    const panel = document.getElementById('emojiPanel');
    if (panel.classList.contains('show') && !e.target.closest('.chat-input-area')) {
        panel.classList.remove('show');
    }
    const rp = document.getElementById('reaction-picker');
    if (rp.classList.contains('show') && !e.target.closest('#reaction-picker') && !e.target.closest('.react-btn')) {
        rp.classList.remove('show');
    }
});

// ── Reacciones ────────────────────────────────────────────────────────────────
let _reactionTarget = null;

function showReactionPicker(e, waId) {
    e.stopPropagation();
    _reactionTarget = waId;
    const rp = document.getElementById('reaction-picker');
    rp.classList.add('show');
    const rect = e.target.getBoundingClientRect();
    rp.style.top  = (rect.top - 60) + 'px';
    rp.style.left = Math.max(4, rect.left - 80) + 'px';
}

async function sendReactionEmoji(emoji) {
    document.getElementById('reaction-picker').classList.remove('show');
    if (!_reactionTarget || !state.activeUserId) return;
    const waId = _reactionTarget;
    try {
        const res  = await fetch(BASE + API_REACT, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ user_id: state.activeUserId, message_id: waId, emoji })
        });
        const json = await res.json();
        if (json.success) {
            // Actualizar burbuja en el DOM inmediatamente
            const bubble = document.querySelector(`[data-wa-id="${waId}"]`);
            if (bubble) {
                let reEl = bubble.querySelector('.msg-reaction');
                if (!reEl) {
                    reEl = document.createElement('div');
                    reEl.className = 'msg-reaction';
                    bubble.appendChild(reEl);
                }
                reEl.textContent = emoji;
            }
            // Actualizar estado en memoria para consistencia
            const m = state.messages.find(x => x.message_id === waId);
            if (m) m.reaction_emoji = emoji;
        }
    } catch(e) { console.error('reaction:', e); }
}

// ── Grabación de audio ────────────────────────────────────────────────────────
let mediaRecorder = null, audioChunks = [], recInterval = null, recSeconds = 0;

async function toggleRecording() {
    if (mediaRecorder && mediaRecorder.state === 'recording') {
        stopRecording();
    } else {
        await startRecording();
    }
}

async function startRecording() {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        audioChunks = [];
        mediaRecorder = new MediaRecorder(stream, { mimeType: MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm' : 'audio/ogg' });
        mediaRecorder.ondataavailable = e => { if (e.data.size > 0) audioChunks.push(e.data); };
        mediaRecorder.start(100);

        recSeconds = 0;
        document.getElementById('recording-bar').style.display = 'flex';
        document.getElementById('recTimer').textContent = '0:00';
        document.getElementById('micBtn').style.color = '#e53e3e';
        recInterval = setInterval(() => {
            recSeconds++;
            const m = Math.floor(recSeconds/60), s = recSeconds%60;
            document.getElementById('recTimer').textContent = m + ':' + String(s).padStart(2,'0');
        }, 1000);
    } catch(e) {
        alert('No se pudo acceder al micrófono: ' + e.message);
    }
}

async function stopRecording() {
    if (!mediaRecorder) return;
    clearInterval(recInterval);
    document.getElementById('recording-bar').style.display = 'none';
    document.getElementById('micBtn').style.color = '';

    return new Promise(resolve => {
        mediaRecorder.onstop = async () => {
            const mime = mediaRecorder.mimeType || 'audio/webm';
            const blob = new Blob(audioChunks, { type: mime });
            const ext  = mime.includes('ogg') ? 'ogg' : 'webm';
            const file = new File([blob], `audio_${Date.now()}.${ext}`, { type: mime });
            mediaRecorder.stream.getTracks().forEach(t => t.stop());
            mediaRecorder = null;

            if (!state.activeUserId) { resolve(); return; }
            const fd = new FormData();
            fd.append('user_id', state.activeUserId);
            fd.append('file', file);
            fd.append('is_voice', '1');
            try {
                const res  = await fetch(BASE + API_MEDIA, { method: 'POST', body: fd });
                const json = await res.json();
                if (json.success) await pollMessages();
                else alert('Error al enviar audio: ' + (json.error || ''));
            } catch(e) { alert('Error de red al enviar audio'); }
            resolve();
        };
        mediaRecorder.stop();
    });
}

function cancelRecording() {
    if (!mediaRecorder) return;
    clearInterval(recInterval);
    mediaRecorder.stream.getTracks().forEach(t => t.stop());
    mediaRecorder = null;
    audioChunks   = [];
    document.getElementById('recording-bar').style.display = 'none';
    document.getElementById('micBtn').style.color = '';
}

// ── Nuevo mensaje ────────────────────────────────────────────────────────────
const nuevoMensaje = (() => {
    let _phone = '';
    let _destLabel = '';
    let _patientName = '';
    let _tpl = null;
    let _buscarTimer = null;

    function _show(stepId) {
        ['nv-step-dest','nv-step-tpl','nv-step-vars'].forEach(id => {
            document.getElementById(id).style.display = id === stepId ? '' : 'none';
        });
    }

    function abrir() {
        _phone = ''; _destLabel = ''; _patientName = ''; _tpl = null;
        document.getElementById('nv-pac-search').value = '';
        document.getElementById('nv-phone-input').value = '';
        document.getElementById('nv-phone-hint').textContent = '';
        document.getElementById('nv-pac-results').innerHTML = '';
        document.getElementById('nv-btn-siguiente').disabled = true;
        _show('nv-step-dest');
        document.getElementById('nuevo-modal').classList.add('show');
        document.getElementById('nv-pac-search').focus();
    }

    function cerrar() {
        document.getElementById('nuevo-modal').classList.remove('show');
    }

    function _setDest(phone, label) {
        _phone = phone; _destLabel = label;
        document.getElementById('nv-btn-siguiente').disabled = !phone;
    }

    function onPhoneInput(val) {
        const digits = val.replace(/\D/,'');
        const hint   = document.getElementById('nv-phone-hint');
        if (!digits) { _setDest('', ''); hint.textContent = ''; return; }
        if (digits.length >= 10) {
            hint.textContent = '✅ Número válido';
            hint.style.color = '#16a34a';
            _setDest(digits, '+' + (digits.length === 10 ? '57' : '') + digits);
        } else {
            hint.textContent = digits.length + ' dígitos — mínimo 10';
            hint.style.color = '#94a3b8';
            _setDest('', '');
        }
        // Clear patient selection if typing a phone
        _patientName = '';
        document.getElementById('nv-pac-search').value = '';
        document.getElementById('nv-pac-results').innerHTML = '';
    }

    function buscarPaciente(q) {
        document.getElementById('nv-phone-input').value = '';
        document.getElementById('nv-phone-hint').textContent = '';
        _setDest('', '');
        clearTimeout(_buscarTimer);
        const el = document.getElementById('nv-pac-results');
        if (q.length < 2) { el.innerHTML = ''; return; }
        el.innerHTML = '<div style="padding:8px;font-size:.8rem;color:#94a3b8"><i class="fas fa-spinner fa-spin"></i> Buscando…</div>';
        _buscarTimer = setTimeout(async () => {
            try {
                const res  = await fetch(BASE + API_BUSCAR_PAC + '?q=' + encodeURIComponent(q));
                const json = await res.json();
                if (!json.ok || !json.data?.length) {
                    el.innerHTML = '<div style="padding:8px;font-size:.8rem;color:#94a3b8">Sin resultados</div>';
                    return;
                }
                el.innerHTML = '';
                json.data.forEach(p => {
                    const div = document.createElement('div');
                    div.className = 'nv-pac-item';
                    const tel = p.telefono || '—';
                    div.innerHTML =
                        `<span class="nv-pac-name">${escHtml(p.nombre_completo)}</span>` +
                        `<span class="nv-pac-sub">${escHtml(p.numero_documento || '')} · 📱 ${escHtml(tel)}</span>`;
                    div.onclick = () => {
                        const phone = (p.telefono || '').replace(/\D/g,'');
                        if (!phone) { alert('Este paciente no tiene teléfono registrado.'); return; }
                        _patientName = p.nombre_completo;
                        _setDest(phone, p.nombre_completo + ' · ' + tel);
                        document.getElementById('nv-pac-search').value = p.nombre_completo;
                        el.innerHTML = '';
                    };
                    el.appendChild(div);
                });
            } catch(e) {
                el.innerHTML = '<div style="padding:8px;font-size:.8rem;color:#ef4444">Error al buscar</div>';
            }
        }, 350);
    }

    async function irAPlantilla() {
        if (!_phone) return;
        document.getElementById('nv-dest-label').textContent = _destLabel || _phone;
        _show('nv-step-tpl');
        const listEl = document.getElementById('nv-tpl-list');
        listEl.innerHTML = '<div style="padding:16px;text-align:center;color:#94a3b8;font-size:.84rem"><i class="fas fa-spinner fa-spin"></i> Cargando…</div>';
        try {
            const res  = await fetch(BASE + API_PLANTILLAS);
            const json = await res.json();
            if (!json.ok || !json.data?.length) {
                listEl.innerHTML = '<div style="padding:16px;text-align:center;color:#94a3b8;font-size:.84rem">No hay plantillas habilitadas.<br><small>Configúralas en Configuración → Sesión.</small></div>';
                return;
            }
            listEl.innerHTML = '';
            json.data.forEach(tpl => {
                const div = document.createElement('div');
                div.className = 'tpl-item';
                div.innerHTML =
                    `<span class="tpl-item-name">${escHtml(tpl.name)}</span>` +
                    `<span class="tpl-item-meta">${escHtml(tpl.template_name)} · ${escHtml(tpl.language_code)}</span>` +
                    (tpl.body_text ? `<span class="tpl-item-body">${escHtml(tpl.body_text.substring(0,80))}${tpl.body_text.length>80?'…':''}</span>` : '');
                div.onclick = () => irAVariables(tpl);
                listEl.appendChild(div);
            });
        } catch(e) {
            listEl.innerHTML = '<div style="padding:16px;text-align:center;color:#ef4444;font-size:.84rem">Error al cargar plantillas</div>';
        }
    }

    async function irAVariables(tpl) {
        _tpl = tpl;
        document.getElementById('nv-vars-title').textContent = tpl.name;
        const previewBox = document.getElementById('nv-preview-box');
        if (tpl.body_text) { previewBox.textContent = tpl.body_text; previewBox.style.display = ''; }
        else previewBox.style.display = 'none';

        const varsBody = document.getElementById('nv-vars-body');
        varsBody.innerHTML = '<div style="padding:6px;color:#94a3b8;font-size:.82rem"><i class="fas fa-spinner fa-spin"></i> Cargando…</div>';
        _show('nv-step-vars');

        try {
            const res  = await fetch(BASE + 'api/get_template_details.php?id=' + tpl.id);
            const json = await res.json();
            const vars = json.template?.variables ?? [];
            if (!vars.length) {
                varsBody.innerHTML = '<div style="padding:6px;font-size:.82rem;color:#64748b">Esta plantilla no requiere variables.</div>';
            } else {
                varsBody.innerHTML = '';
                vars.forEach(v => {
                    const row = document.createElement('div');
                    row.className = 'tpl-var-row';
                    const ph = v.example || v.placeholder || v.label;
                    const varName = (v.placeholder || '').replace(/\{\{|\}\}/g, '').trim();
                    const autoVal = (varName === 'name' && _patientName) ? _patientName : '';
                    row.innerHTML = `<label>{{${v.index}}} — ${escHtml(v.label)}</label>` +
                        `<input type="text" data-var-index="${v.index}" data-var-placeholder="${escHtml(v.placeholder || '')}" placeholder="${escHtml(ph)}" value="${escHtml(autoVal)}">`;
                    varsBody.appendChild(row);
                });
            }
        } catch(e) {
            varsBody.innerHTML = '<div style="padding:6px;color:#ef4444;font-size:.82rem">Error al cargar variables</div>';
        }
    }

    function volverADest()     { _tpl = null; _show('nv-step-dest'); }
    function volverAPlantilla() { _tpl = null; _show('nv-step-tpl'); }

    async function enviar() {
        if (!_phone || !_tpl) return;
        const inputs = [...document.querySelectorAll('#nv-vars-body input[data-var-index]')];
        const missing = inputs.findIndex(el => !el.value.trim());
        if (missing !== -1) { inputs[missing].focus(); return; }
        const params = _buildParams(inputs);

        const templateName = _tpl.template_name;
        const templateLang = _tpl.language_code;
        cerrar();

        try {
            const res  = await fetch(BASE + API_START_CONV, {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ phone: _phone, template_name: templateName, lang: templateLang, params })
            });
            const json = await res.json();
            if (!json.ok) { alert('Error: ' + (json.error || 'No se pudo enviar')); return; }
            // Abrir la conversación recién creada
            await loadContacts();
            if (json.user_id) openChat(json.user_id);
        } catch(e) { alert('Error de conexión al enviar el mensaje'); }
    }

    return { abrir, cerrar, buscarPaciente, onPhoneInput, irAPlantilla, volverADest, volverAPlantilla, enviar };
})();

// ── Plantilla (2 pasos) ───────────────────────────────────────────────────────
async function openTemplatePicker() {
    if (!state.activeUserId) return;
    _tplSelected = null;
    document.getElementById('tpl-step-list').style.display = '';
    document.getElementById('tpl-step-vars').style.display = 'none';
    document.getElementById('tpl-modal').classList.add('show');

    const listBody = document.getElementById('tpl-list-body');
    listBody.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8;font-size:.84rem"><i class="fas fa-spinner fa-spin"></i> Cargando…</div>';

    try {
        const res  = await fetch(BASE + API_PLANTILLAS);
        const json = await res.json();
        if (!json.ok || !json.data?.length) {
            listBody.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8;font-size:.84rem">No hay plantillas habilitadas.<br><small>Configúralas en Configuración → Sesión.</small></div>';
            return;
        }
        listBody.innerHTML = '';
        json.data.forEach(tpl => {
            const div = document.createElement('div');
            div.className = 'tpl-item';
            div.innerHTML =
                `<span class="tpl-item-name">${escHtml(tpl.name)}</span>` +
                `<span class="tpl-item-meta">${escHtml(tpl.template_name)} · ${escHtml(tpl.language_code)}</span>` +
                (tpl.body_text ? `<span class="tpl-item-body">${escHtml(tpl.body_text.substring(0, 80))}${tpl.body_text.length > 80 ? '…' : ''}</span>` : '');
            div.onclick = () => selectTemplate(tpl);
            listBody.appendChild(div);
        });
    } catch(e) {
        listBody.innerHTML = '<div style="padding:20px;text-align:center;color:#ef4444;font-size:.84rem">Error al cargar plantillas.</div>';
    }
}

function closeTemplatePicker() {
    document.getElementById('tpl-modal').classList.remove('show');
    _tplSelected = null;
}

function tplGoBack() {
    document.getElementById('tpl-step-list').style.display = '';
    document.getElementById('tpl-step-vars').style.display = 'none';
    _tplSelected = null;
}

async function selectTemplate(tpl) {
    _tplSelected = tpl;
    document.getElementById('tpl-step-list').style.display = 'none';
    document.getElementById('tpl-step-vars').style.display = '';
    document.getElementById('tpl-vars-title').textContent = tpl.name;

    const previewBox = document.getElementById('tpl-preview-box');
    if (tpl.body_text) {
        previewBox.textContent = tpl.body_text;
        previewBox.style.display = '';
    } else {
        previewBox.style.display = 'none';
    }

    const varsBody = document.getElementById('tpl-vars-body');
    varsBody.innerHTML = '<div style="padding:8px 16px;color:#94a3b8;font-size:.82rem"><i class="fas fa-spinner fa-spin"></i> Cargando…</div>';

    try {
        const res  = await fetch(BASE + 'api/get_template_details.php?id=' + tpl.id);
        const json = await res.json();
        const vars = json.template?.variables ?? [];
        if (!vars.length) {
            varsBody.innerHTML = '<div style="padding:8px 16px;font-size:.82rem;color:#64748b">Esta plantilla no requiere variables.</div>';
        } else {
            varsBody.innerHTML = '';
            vars.forEach(v => {
                const row = document.createElement('div');
                row.className = 'tpl-var-row';
                const label = v.label || ('Variable ' + v.index);
                const ph    = v.example || v.placeholder || label;
                row.innerHTML = `<label>{{${v.index}}} — ${escHtml(label)}</label>` +
                    `<input type="text" data-var-index="${v.index}" data-var-placeholder="${escHtml(v.placeholder || '')}" placeholder="${escHtml(ph)}">`;
                varsBody.appendChild(row);
            });
        }
    } catch(e) {
        varsBody.innerHTML = '<div style="padding:8px 16px;color:#ef4444;font-size:.82rem">Error al cargar variables.</div>';
    }
}

function _buildParams(inputs) {
    const hasNamed = inputs.some(el => {
        const n = (el.dataset.varPlaceholder || '').replace(/\{\{|\}\}/g, '').trim();
        return n && !/^\d+$/.test(n);
    });
    if (hasNamed) {
        const obj = {};
        inputs.forEach(el => {
            const n = (el.dataset.varPlaceholder || '').replace(/\{\{|\}\}/g, '').trim() || el.dataset.varIndex;
            obj[n] = el.value.trim();
        });
        return obj;
    }
    return inputs.map(el => el.value.trim());
}

async function sendTemplate() {
    if (!_tplSelected || !state.activeUserId) return;
    const inputs = [...document.querySelectorAll('#tpl-vars-body input[data-var-index]')];
    const missing = inputs.findIndex(el => !el.value.trim());
    if (missing !== -1) { inputs[missing].focus(); return; }
    const params = _buildParams(inputs);

    const templateName = _tplSelected.template_name;
    const templateLang = _tplSelected.language_code;
    closeTemplatePicker();

    try {
        const res  = await fetch(BASE + API_SEND, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                user_id: state.activeUserId,
                type:     'template',
                template: templateName,
                lang:     templateLang,
                params
            })
        });
        const json = await res.json();
        if (!json.success) alert('Error al enviar plantilla: ' + (json.error || ''));
        else await pollMessages();
    } catch(e) { alert('Error de red al enviar plantilla'); }
}

async function markRead(userId) {
    try {
        await fetch(BASE + API_READ, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ user_id: userId })
        });
    } catch(e) {}
}

// ── Helpers ───────────────────────────────────────────────────────────────────
function handleKey(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
}
function autoResize(el) {
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 120) + 'px';
}
function escHtml(str) {
    return String(str ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function nl2br(str) { return str.replace(/\n/g, '<br>'); }
function dateLabel(ts) {
    const d = new Date(ts.replace(' ', 'T'));
    const now = new Date();
    const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1);
    if (d.toDateString() === now.toDateString()) return 'Hoy';
    if (d.toDateString() === yesterday.toDateString()) return 'Ayer';
    return d.toLocaleDateString('es-CO', { weekday:'short', day:'2-digit', month:'short' });
}
function buildDateSep(label) {
    const el = document.createElement('div');
    el.className = 'date-sep';
    el.textContent = label;
    return el;
}
function msgDay(ts) { return new Date(ts.replace(' ', 'T')).toDateString(); }

function formatTime(ts) {
    const d = new Date(ts.replace(' ', 'T'));
    const now = new Date();
    if (d.toDateString() === now.toDateString()) {
        return d.toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit' });
    }
    return d.toLocaleDateString('es-CO', { day: '2-digit', month: '2-digit' });
}
function formatTimeFull(ts) {
    const d = new Date(ts.replace(' ', 'T'));
    return d.toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit' });
}
</script>
</body>
</html>
