// popup.js — Skoop v0.2

'use strict';

// ── ESTADO GLOBAL ─────────────────────────────────────────
let currentSlug = null;
let currentMembers = [];
let currentFeed = [];
let currentFilter = 'atRisk';
let _labelTypes = [];
let _templates = [];
let _settings = { blurMode: false, darkMode: false };
let _dmPreviewMembers = [];
let _scanPollingTimer = null;
let _communityTotal = null;

// ── INICIO ────────────────────────────────────────────────

document.addEventListener('DOMContentLoaded', async () => {
  // Mostrar la versión real del manifest (para confirmar la recarga)
  try {
    const v = chrome.runtime.getManifest().version;
    const badge = document.getElementById('version-badge');
    if (badge) badge.textContent = 'v' + v;
  } catch (_) {}

  // Modo gratis: ya NO se bloquea la extensión sin licencia. Quien no tenga
  // llave usa Skoop en modo gratis (data limitada + IA bloqueada en el dashboard).
  // La llave (gratis para F100K o de pago) desbloquea todo.
  try {
    await init();
  } catch (err) {
    console.error('[Skoop] Error al inicializar:', err);
    show('state-no-skool');
  }
  bindEvents();
  await loadSettings();
  await loadAlertsConfig();
});

// ── INIT ──────────────────────────────────────────────────

async function waitForSW(retries = 3, delayMs = 300) {
  for (let i = 0; i < retries; i++) {
    const communities = await msg('GET_COMMUNITIES');
    if (communities !== null) return communities;
    if (i < retries - 1) await new Promise((r) => setTimeout(r, delayMs));
  }
  return null;
}

async function init() {
  show('state-loading');

  const [communitiesRaw, activeTab] = await Promise.all([
    waitForSW(),
    getActiveSkoolTab(),
  ]);

  const communities = communitiesRaw || [];

  // Slug de la comunidad que el usuario está viendo AHORA mismo (desde la URL del tab activo)
  const activeSlug = activeTab ? slugFromUrl(activeTab.url) : null;

  // Solo "sin datos" si no hay comunidades guardadas Y tampoco estamos parados en una comunidad Skool
  if (communities.length === 0 && !activeSlug) {
    show(activeTab ? 'state-no-data' : 'state-no-skool');
    return;
  }

  // Garantizar que la comunidad activa esté en la lista aunque todavía no se haya indexado.
  // Así el usuario SIEMPRE puede operar sobre la comunidad que tiene abierta (no un fallback ajeno).
  const commList = communities.slice();
  if (activeSlug && !commList.find((c) => c.slug === activeSlug)) {
    commList.unshift({ slug: activeSlug, name: activeSlug });
    // Registrarla en storage para que persista y también aparezca en el dashboard
    msg('REGISTER_COMMUNITY', { slug: activeSlug });
  }

  const select = $('community-select');
  const clearSelect = $('setting-clear-community');
  select.innerHTML = '';
  clearSelect.innerHTML = '<option value="">Selecciona...</option>';

  for (const c of commList) {
    const opt = document.createElement('option');
    opt.value = c.slug;
    opt.textContent = c.name || c.slug;
    select.appendChild(opt.cloneNode(true));
    clearSelect.appendChild(opt);
  }

  // SIEMPRE preferir la comunidad del tab activo; solo si no hay tab Skool, caer a la primera guardada
  currentSlug = activeSlug || commList[0].slug;
  select.value = currentSlug;

  show('dashboard');

  // Cargar datos globales (etiquetas y plantillas)
  [_labelTypes, _templates] = await Promise.all([
    msg('GET_LABEL_TYPES'),
    msg('GET_TEMPLATES'),
  ]);
  _labelTypes = _labelTypes || [];
  _templates = _templates || [];

  await loadCommunityData(currentSlug);
}

async function loadCommunityData(slug) {
  if (!slug) return;
  currentSlug = slug;

  const [stats, members, feed, endpoints] = await Promise.all([
    msg('GET_STATS', { slug }),
    msg('GET_MEMBERS', { slug }),
    msg('GET_FEED', { slug, limit: 50 }),
    msg('GET_ENDPOINTS', { slug }),
  ]);

  currentMembers = members || [];
  currentFeed = feed || [];

  renderStats(stats);
  renderMembers(currentFilter);
  renderFeed(currentFeed);
  renderEndpoints(endpoints || []);
  populateMemberSelects();

  if (stats?.lastSync) {
    $('last-sync-label').textContent = 'Sync: ' + relativeTime(stats.lastSync);
  } else {
    $('last-sync-label').textContent = 'Navegando en Skool...';
  }

  // Guardar total para barra de progreso
  _communityTotal = stats?.totalMembers || null;

  // Reanudar polling si había un scan en curso
  const scanStatus = await msg('GET_SCAN_STATUS', { slug });

  // Mostrar SIEMPRE el banner de escaneo: el scan ya funciona desde cualquier página
  // (si no estás en /members, abre una pestaña en background con el slug correcto).
  $('scan-banner').classList.remove('hidden');

  if (scanStatus?.running) startScanPolling();
  updateScanUI(scanStatus);

  // Cargar los tabs de las nuevas features
  renderLabelsTab();
  renderPipelineTab();
  renderTemplatesTab();
  await renderDmTab();
}

// ── RENDER: STATS ─────────────────────────────────────────

function renderStats(stats) {
  if (!stats) return;
  const { totalMembers, riskBuckets } = stats;
  setText('stat-total', totalMembers || 0);
  setText('stat-active', riskBuckets?.active || 0);
  setText('stat-silent', riskBuckets?.silent || 0);
  setText('stat-risk', (riskBuckets?.atRisk || 0) + (riskBuckets?.churned || 0));

  // Mostrar cuántos miembros aún no tienen datos de actividad
  const unknown = riskBuckets?.unknown || 0;
  const unknownEl = $('stat-unknown-hint');
  if (unknownEl) {
    if (unknown > 0) {
      unknownEl.textContent = `⚠ ${unknown} sin datos de actividad — haz el scan`;
      unknownEl.classList.remove('hidden');
    } else {
      unknownEl.classList.add('hidden');
    }
  }
}

// ── RENDER: MIEMBROS ──────────────────────────────────────

// Nuevos (≤2 meses) que ya se apagaron — prioridad de rescate.
function _isRescue(m) {
  return m.monthsInCommunity != null && m.monthsInCommunity <= 2 &&
    (m.riskLevel === 'silent' || m.riskLevel === 'atRisk' || m.riskLevel === 'churned');
}

function renderMembers(filter) {
  currentFilter = filter;
  const container = $('members-list');

  let list = currentMembers;
  if (filter === 'cancelling') {
    list = currentMembers.filter((m) => m.subStatus === 'cancelling' || m.subStatus === 'canceled');
    // los que se van primero, arriba
    list = list.sort((a, b) => (a.daysUntilCancel ?? 9999) - (b.daysUntilCancel ?? 9999));
  } else if (filter === 'rescue') {
    list = currentMembers.filter(_isRescue);
    list = list.sort((a, b) => (a.monthsInCommunity ?? 99) - (b.monthsInCommunity ?? 99));
  } else {
    if (filter) list = currentMembers.filter((m) => m.riskLevel === filter);
    list = list.sort((a, b) => (b.daysSinceLastPost ?? 999) - (a.daysSinceLastPost ?? 999));
  }

  if (list.length === 0) {
    container.innerHTML = `<div class="list-empty">Sin miembros${filter ? ' con este estado' : ''}.<br/>Navega a la sección de Miembros en Skool.</div>`;
    return;
  }

  container.innerHTML = list.slice(0, 50).map(renderMemberItem).join('');
}

function renderMemberItem(m) {
  const initials = (m.name || '?').split(' ').map((w) => w[0]).slice(0, 2).join('');
  const avatar = m.avatarUrl
    ? `<img class="member-avatar" src="${esc(m.avatarUrl)}" alt="" onerror="this.style.display='none'">`
    : `<div class="avatar-placeholder">${esc(initials)}</div>`;

  const badgeMap = { active: 'badge-active', silent: 'badge-silent', atRisk: 'badge-atRisk', churned: 'badge-churned' };
  const badgeLabelMap = { active: '🟢 Activo', silent: '🟡 Silencio', atRisk: '🔴 En riesgo', churned: '⚫ Churn' };
  const riskClass = badgeMap[m.riskLevel] || 'badge-unknown';
  const riskLabel = badgeLabelMap[m.riskLevel] || '❔';

  const meta = [];
  if (m.role) meta.push(m.role);
  if (m.monthsInCommunity != null) meta.push(`${m.monthsInCommunity}m en la comu`);
  if (m.daysSinceLastPost !== null && m.daysSinceLastPost !== undefined) meta.push(`${m.daysSinceLastPost}d sin post`);
  if (m.postCount) meta.push(`${m.postCount} posts`);
  if (m.commentCount) meta.push(`💬 ${m.commentCount}`);
  if (m.daysSinceLastComment != null) meta.push(`coment. hace ${m.daysSinceLastComment}d`);
  if (m.level != null) meta.push(`Nv.${m.level}${Number(m.level) <= 1 ? ' 🔰' : ''}`);
  if (m.tier) meta.push(`🎟 ${m.tier}`);

  // Fase 1: contacto (WhatsApp + Instagram), nicho y plan desde la encuesta/billing
  const contactBits = [];
  if (m.whatsapp) {
    const waDigits = m.whatsapp.replace(/[^\d]/g, '');
    contactBits.push(`<a href="https://wa.me/${esc(waDigits)}" target="_blank" title="Escribir por WhatsApp" style="color:#25D366;text-decoration:none">📱 ${esc(m.whatsapp)}</a>`);
  }
  if (m.instagram) {
    contactBits.push(`<a href="https://instagram.com/${esc(m.instagram.replace(/^@/, ''))}" target="_blank" title="Ver Instagram" style="color:#E1306C;text-decoration:none">◎ ${esc(m.instagram)}</a>`);
  }
  const contactHtml = contactBits.length
    ? `<div class="member-meta" style="display:flex;gap:10px;flex-wrap:wrap;font-size:10px;margin-top:2px">${contactBits.join('')}</div>`
    : '';
  const nicheHtml = m.niche
    ? `<div class="member-meta" style="font-size:10px;color:var(--text-muted)">🎯 ${esc(m.niche)}</div>`
    : '';
  const billingBits = [];
  if (m.ltv != null) billingBits.push(`💰 $${m.ltv}`);
  if (m.planPrice != null && m.planPrice !== m.ltv) billingBits.push(typeof m.planPrice === 'number' ? `plan $${m.planPrice}` : `plan ${esc(m.planPrice)}`);
  const billingHtml = billingBits.length
    ? `<div class="member-meta" style="font-size:10px;color:var(--text-muted)">${billingBits.join(' · ')}</div>`
    : '';

  // Etiquetas del miembro
  const memberLabels = (m.labelIds || [])
    .map((id) => _labelTypes.find((l) => l.id === id))
    .filter(Boolean);

  const labelsHtml = memberLabels.length
    ? `<div style="margin-top:3px;display:flex;flex-wrap:wrap;gap:3px">${memberLabels.map((l) =>
        `<span class="label-chip" style="background:${l.color}22;color:${l.color};border-color:${l.color}44">${esc(l.text)}</span>`
      ).join('')}</div>`
    : '';

  // Pipeline badge
  const pipeStageMap = { lead: '🔵 Lead', prospect: '🟡 Prospecto', hot: '🔴 Caliente', client: '🟢 Cliente' };
  const pipeHtml = m.pipeline?.stage
    ? `<span class="pipeline-stage-badge stage-${m.pipeline.stage}">${pipeStageMap[m.pipeline.stage]}</span>`
    : '';

  // Fase 2: badge "Por irse"
  let cancelHtml = '';
  if (m.subStatus === 'cancelling') {
    const d = m.daysUntilCancel != null ? ` ${m.daysUntilCancel}d` : '';
    cancelHtml = `<span class="member-badge badge-cancel" title="Canceló pero su acceso sigue activo">🔴 Por irse${d}</span>`;
  } else if (m.subStatus === 'canceled') {
    cancelHtml = `<span class="member-badge badge-cancel">⚫ Canceló</span>`;
  }

  return `
    <div class="member-item">
      ${avatar}
      <div class="member-info">
        <div class="member-name">${esc(m.name || m.username || 'Sin nombre')}</div>
        ${m.username ? `<div class="member-meta">@${esc(m.username)}</div>` : ''}
        ${meta.length ? `<div class="member-meta">${meta.join(' · ')}</div>` : ''}
        ${m.email ? `<div class="member-meta" style="color:var(--purple-light);font-size:10px">✉ ${esc(m.email)}</div>` : ''}
        ${contactHtml}
        ${nicheHtml}
        ${billingHtml}
        ${labelsHtml}
      </div>
      <div style="display:flex;flex-direction:column;align-items:flex-end;gap:3px;flex-shrink:0">
        <span class="member-badge ${riskClass}">${riskLabel}</span>
        ${cancelHtml}
        ${pipeHtml}
      </div>
    </div>
  `;
}

// ── RENDER: FEED ──────────────────────────────────────────

function renderFeed(posts) {
  const container = $('feed-list');
  if (!posts || posts.length === 0) {
    container.innerHTML = `<div class="list-empty">Sin posts indexados.<br/>Navega al Feed de tu comunidad en Skool.</div>`;
    return;
  }
  container.innerHTML = posts.slice(0, 30).map((p) => {
    const ts = p.createdAt ? relativeTime(new Date(p.createdAt).getTime()) : '';
    return `
      <div class="post-item">
        <div class="post-author">${esc(p.authorName || 'Miembro')}</div>
        <div class="post-meta">${ts}${p.likesCount ? ` · ❤️ ${p.likesCount}` : ''}${p.commentsCount ? ` · 💬 ${p.commentsCount}` : ''}</div>
        ${p.content ? `<div class="post-content">${esc(p.content)}</div>` : ''}
      </div>
    `;
  }).join('');
}

// ── RENDER: ENDPOINTS ─────────────────────────────────────

function renderEndpoints(endpoints) {
  const container = $('endpoints-list');
  if (!endpoints || endpoints.length === 0) {
    container.innerHTML = `<div class="list-empty">Sin endpoints detectados.<br/>Navega por Skool para descubrirlos.</div>`;
    return;
  }
  container.innerHTML = endpoints.slice(0, 40).map((e) => `
    <div class="endpoint-item">
      <div class="endpoint-url"><span style="color:#8892a4">${esc(e.method)}</span> ${esc(e.url)}</div>
      <div class="endpoint-meta">visto ${e.count}x · último: ${relativeTime(e.lastSeen)}</div>
    </div>
  `).join('');
}

// ── ETIQUETAS ─────────────────────────────────────────────

const PRESET_COLORS = [
  '#7c3aed', '#3b82f6', '#10b981', '#f59e0b',
  '#ef4444', '#ec4899', '#f97316', '#14b8a6',
];

function renderLabelsTab() {
  renderLabelTypes();
  renderLabelFilterSelect();
  renderLabeledMembers('');
}

function renderLabelTypes() {
  const container = $('label-types-list');
  if (!_labelTypes.length) {
    container.innerHTML = `<div class="list-empty" style="padding:12px 14px">Sin etiquetas creadas aún. Haz clic en "+ Nueva".</div>`;
    return;
  }
  container.innerHTML = _labelTypes.map((l) => `
    <div class="label-type-item">
      <span class="label-chip" style="background:${l.color}22;color:${l.color};border-color:${l.color}44">${esc(l.text)}</span>
      <span style="font-size:11px;color:var(--text-muted)">
        ${currentMembers.filter((m) => (m.labelIds || []).includes(l.id)).length} miembros
      </span>
      <div class="label-type-item-actions">
        <button class="btn-mini" onclick="deleteLabelType('${esc(l.id)}')">🗑</button>
      </div>
    </div>
  `).join('');
}

function renderLabelFilterSelect() {
  const sel = $('label-filter-select');
  sel.innerHTML = '<option value="">Ver todos</option>' +
    _labelTypes.map((l) => `<option value="${esc(l.id)}">${esc(l.text)}</option>`).join('');
}

function renderLabeledMembers(filterLabelId) {
  const container = $('labeled-members-list');
  const list = filterLabelId
    ? currentMembers.filter((m) => (m.labelIds || []).includes(filterLabelId))
    : currentMembers.filter((m) => (m.labelIds || []).length > 0);

  if (!list.length) {
    container.innerHTML = `<div class="list-empty">Sin miembros con etiqueta${filterLabelId ? ' seleccionada' : ''}.</div>`;
    return;
  }

  container.innerHTML = list.slice(0, 30).map((m) => {
    const labels = (m.labelIds || [])
      .map((id) => _labelTypes.find((l) => l.id === id)).filter(Boolean);
    return `
      <div class="member-item" style="align-items:center">
        <div class="member-info">
          <div class="member-name">${esc(m.name || m.username || 'Sin nombre')}</div>
          <div style="display:flex;flex-wrap:wrap;gap:3px;margin-top:3px">
            ${labels.map((l) => `
              <span class="label-chip removable"
                style="background:${l.color}22;color:${l.color};border-color:${l.color}44"
                onclick="removeLabelFromMember('${esc(String(m.id))}','${esc(l.id)}')"
                title="Quitar etiqueta">
                ${esc(l.text)} ✕
              </span>
            `).join('')}
          </div>
        </div>
      </div>
    `;
  }).join('');
}

function populateMemberSelects() {
  const sorted = [...currentMembers].sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  const options = sorted.map((m) =>
    `<option value="${esc(String(m.id))}">${esc(m.name || m.username || m.id)}</option>`
  ).join('');

  $('label-member-select').innerHTML = '<option value="">Selecciona miembro...</option>' + options;
  $('pipeline-member-select').innerHTML = '<option value="">Selecciona miembro...</option>' + options;
}

function showLabelAssignArea(memberId) {
  const area = $('label-assign-area');
  if (!memberId) { area.classList.add('hidden'); return; }
  const m = currentMembers.find((m) => String(m.id) === memberId);
  if (!m) { area.classList.add('hidden'); return; }
  area.classList.remove('hidden');

  const memberLabelIds = m.labelIds || [];
  area.innerHTML = `
    <p style="font-size:11px;color:var(--text-muted);margin-bottom:6px">
      Etiquetas de <strong>${esc(m.name || m.username)}</strong>:
    </p>
    <div style="display:flex;flex-wrap:wrap;gap:4px">
      ${_labelTypes.map((l) => {
        const assigned = memberLabelIds.includes(l.id);
        return `
          <span class="label-chip" style="
            background:${assigned ? l.color + '33' : 'transparent'};
            color:${assigned ? l.color : 'var(--text-muted)'};
            border-color:${assigned ? l.color : 'var(--border)'};
            cursor:pointer"
            onclick="toggleLabelOnMember('${esc(memberId)}','${esc(l.id)}')">
            ${assigned ? '✓ ' : ''}${esc(l.text)}
          </span>
        `;
      }).join('')}
    </div>
  `;
}

window.toggleLabelOnMember = async (memberId, labelId) => {
  await msg('TOGGLE_MEMBER_LABEL', { slug: currentSlug, memberId, labelId });
  // Actualizar estado local
  const m = currentMembers.find((m) => String(m.id) === memberId);
  if (m) {
    const ids = m.labelIds || [];
    const idx = ids.indexOf(labelId);
    if (idx >= 0) ids.splice(idx, 1);
    else ids.push(labelId);
    m.labelIds = ids;
  }
  showLabelAssignArea(memberId);
  renderLabelTypes();
  renderLabeledMembers($('label-filter-select').value);
  renderMembers(currentFilter);
};

window.deleteLabelType = async (id) => {
  if (!confirm('¿Eliminar esta etiqueta? Se quitará de todos los miembros.')) return;
  await msg('DELETE_LABEL_TYPE', { id });
  _labelTypes = _labelTypes.filter((l) => l.id !== id);
  // Limpiar labelIds de miembros en memoria local
  currentMembers.forEach((m) => {
    m.labelIds = (m.labelIds || []).filter((lid) => lid !== id);
  });
  renderLabelsTab();
  renderMembers(currentFilter);
  toast('Etiqueta eliminada');
};

window.removeLabelFromMember = async (memberId, labelId) => {
  await msg('TOGGLE_MEMBER_LABEL', { slug: currentSlug, memberId, labelId });
  const m = currentMembers.find((m) => String(m.id) === memberId);
  if (m) m.labelIds = (m.labelIds || []).filter((id) => id !== labelId);
  renderLabeledMembers($('label-filter-select').value);
  renderMembers(currentFilter);
};

function showNewLabelForm() {
  const text = prompt('Nombre de la etiqueta (ej: VIP, Recuperar, Hot Lead):');
  if (!text || !text.trim()) return;

  // Elegir color automáticamente
  const usedColors = _labelTypes.map((l) => l.color);
  const color = PRESET_COLORS.find((c) => !usedColors.includes(c)) || PRESET_COLORS[_labelTypes.length % PRESET_COLORS.length];

  const label = { id: `lbl_${Date.now()}`, text: text.trim(), color };
  msg('SAVE_LABEL_TYPE', { label }).then(() => {
    _labelTypes.push(label);
    renderLabelsTab();
    renderLabelFilterSelect();
    toast(`Etiqueta "${label.text}" creada`);
  });
}

// ── PIPELINE ──────────────────────────────────────────────

async function renderPipelineTab(stageFilter = '') {
  const pipelineMembers = await msg('GET_PIPELINE_MEMBERS', { slug: currentSlug }) || [];

  // Summary
  const stages = { lead: 0, prospect: 0, hot: 0, client: 0 };
  pipelineMembers.forEach((m) => { if (stages[m.pipeline?.stage] !== undefined) stages[m.pipeline.stage]++; });

  $('pipeline-summary').innerHTML = `
    <div class="pipeline-stat"><div class="pipeline-stat-num" style="color:#60a5fa">${stages.lead}</div><div class="pipeline-stat-label">🔵 Lead</div></div>
    <div class="pipeline-stat"><div class="pipeline-stat-num" style="color:var(--yellow)">${stages.prospect}</div><div class="pipeline-stat-label">🟡 Prospecto</div></div>
    <div class="pipeline-stat"><div class="pipeline-stat-num" style="color:var(--red)">${stages.hot}</div><div class="pipeline-stat-label">🔴 Caliente</div></div>
    <div class="pipeline-stat"><div class="pipeline-stat-num" style="color:var(--green)">${stages.client}</div><div class="pipeline-stat-label">🟢 Cliente</div></div>
  `;

  const list = stageFilter ? pipelineMembers.filter((m) => m.pipeline?.stage === stageFilter) : pipelineMembers;
  const container = $('pipeline-list');

  if (!list.length) {
    container.innerHTML = `<div class="list-empty">Sin miembros en el pipeline${stageFilter ? ' con este estado' : ''}.<br/>Agrega miembros usando el formulario de abajo.</div>`;
    return;
  }

  const stageMap = { lead: 'stage-lead', prospect: 'stage-prospect', hot: 'stage-hot', client: 'stage-client' };
  const stageLabelMap = { lead: '🔵 Lead', prospect: '🟡 Prospecto', hot: '🔴 Caliente', client: '🟢 Cliente' };

  container.innerHTML = list.map((m) => `
    <div class="pipeline-member-item">
      <div class="member-info">
        <div class="member-name">${esc(m.name || m.username || m.id)}</div>
        ${m.pipeline?.notes ? `<div class="pipeline-notes">${esc(m.pipeline.notes)}</div>` : ''}
      </div>
      <div style="display:flex;flex-direction:column;align-items:flex-end;gap:4px">
        <span class="pipeline-stage-badge ${stageMap[m.pipeline.stage] || ''}">${stageLabelMap[m.pipeline.stage] || m.pipeline.stage}</span>
        <button class="btn-mini" style="font-size:9px" onclick="removePipelineMember('${esc(String(m.id))}')">Quitar</button>
      </div>
    </div>
  `).join('');
}

window.removePipelineMember = async (memberId) => {
  await msg('SAVE_PIPELINE', { slug: currentSlug, memberId, stage: 'none', notes: '' });
  renderPipelineTab(document.querySelector('.pipeline-stage-filter .filter-btn.active')?.dataset.stage || '');
  renderMembers(currentFilter);
  toast('Miembro quitado del pipeline');
};

// ── PLANTILLAS ────────────────────────────────────────────

function renderTemplatesTab() {
  const container = $('templates-list');
  if (!_templates.length) {
    container.innerHTML = `<div class="list-empty" style="padding:12px 14px">Sin plantillas creadas.<br/>Haz clic en "+ Nueva" para crear tu primera plantilla.</div>`;
    return;
  }

  container.innerHTML = _templates.map((t) => `
    <div class="template-item">
      <div class="template-item-header">
        <span class="template-name">${esc(t.name)}</span>
        <div class="template-actions">
          <button class="btn-mini" onclick="editTemplate('${esc(t.id)}')">✏️</button>
          <button class="btn-mini" onclick="deleteTemplate('${esc(t.id)}')">🗑</button>
        </div>
      </div>
      <div class="template-body-preview">${esc(t.body)}</div>
    </div>
  `).join('');

  // Actualizar selects de plantillas en DM y bienvenida
  updateTemplateSelects();
}

function updateTemplateSelects() {
  const opts = '<option value="">Sin plantilla (mensaje libre)</option>' +
    _templates.map((t) => `<option value="${esc(t.id)}">${esc(t.name)}</option>`).join('');
  $('dm-template-select').innerHTML = opts;

  const welcomeOpts = '<option value="">Selecciona plantilla...</option>' +
    _templates.map((t) => `<option value="${esc(t.id)}">${esc(t.name)}</option>`).join('');
  $('welcome-template-select').innerHTML = welcomeOpts;
}

function showTemplateForm(template = null) {
  const form = $('template-form');
  $('template-form-title').textContent = template ? 'Editar plantilla' : 'Nueva plantilla';
  $('template-id').value = template?.id || '';
  $('template-name').value = template?.name || '';
  $('template-body').value = template?.body || '';
  form.classList.remove('hidden');
  $('template-name').focus();
}

function hideTemplateForm() {
  $('template-form').classList.add('hidden');
  $('template-id').value = '';
  $('template-name').value = '';
  $('template-body').value = '';
}

window.editTemplate = (id) => {
  const t = _templates.find((t) => t.id === id);
  if (t) showTemplateForm(t);
};

window.deleteTemplate = async (id) => {
  if (!confirm('¿Eliminar esta plantilla?')) return;
  await msg('DELETE_TEMPLATE', { id });
  _templates = _templates.filter((t) => t.id !== id);
  renderTemplatesTab();
  toast('Plantilla eliminada');
};

async function saveCurrentTemplate() {
  const name = $('template-name').value.trim();
  const body = $('template-body').value.trim();
  if (!name || !body) { toast('Completa nombre y cuerpo de la plantilla'); return; }

  const id = $('template-id').value || `tpl_${Date.now()}`;
  const tpl = { id, name, body };

  await msg('SAVE_TEMPLATE', { template: tpl });

  const idx = _templates.findIndex((t) => t.id === id);
  if (idx >= 0) _templates[idx] = tpl;
  else _templates.push(tpl);

  hideTemplateForm();
  renderTemplatesTab();
  toast(`Plantilla "${name}" guardada`);
}

// ── DM MASIVO ─────────────────────────────────────────────

function applyTemplate(body, member) {
  return body
    .replace(/#NOMBRE#/g, member.name || member.username || 'amigo/a')
    .replace(/#USERNAME#/g, member.username || member.name || '')
    .replace(/#DIAS#/g, member.daysSinceLastPost !== null && member.daysSinceLastPost !== undefined ? String(member.daysSinceLastPost) : 'varios')
    .replace(/#ROL#/g, member.role || 'miembro');
}

async function renderDmTab() {
  updateTemplateSelects();
  await loadPendingWelcomes();
}

async function generateDmPreview() {
  const templateId = $('dm-template-select').value;
  const recipientFilter = $('dm-recipients-select').value;
  let body = $('dm-body').value.trim();

  if (templateId && !body) {
    const tpl = _templates.find((t) => t.id === templateId);
    if (tpl) {
      body = tpl.body;
      $('dm-body').value = body;
    }
  }

  if (!body) { toast('Escribe un mensaje o selecciona una plantilla'); return; }

  let recipients = currentMembers;
  if (recipientFilter && recipientFilter !== 'all') {
    recipients = currentMembers.filter((m) => m.riskLevel === recipientFilter);
  }

  _dmPreviewMembers = recipients.slice(0, 50);

  const countEl = $('dm-preview-count');
  countEl.textContent = `${_dmPreviewMembers.length} destinatario${_dmPreviewMembers.length !== 1 ? 's' : ''}`;
  countEl.classList.remove('hidden');

  const container = $('dm-preview-list');
  if (!_dmPreviewMembers.length) {
    container.innerHTML = `<div class="list-empty">Sin miembros para este filtro.</div>`;
    return;
  }

  container.innerHTML = _dmPreviewMembers.map((m, i) => {
    const personalizedBody = applyTemplate(body, m);
    return `
      <div class="dm-preview-item">
        <div class="dm-preview-name">${esc(m.name || m.username || 'Miembro')}</div>
        <div class="dm-preview-body">${esc(personalizedBody)}</div>
        <button class="dm-copy-btn" onclick="copyDmForMember(${i})">Copiar este mensaje</button>
      </div>
    `;
  }).join('');
}

window.copyDmForMember = (idx) => {
  const m = _dmPreviewMembers[idx];
  if (!m) return;
  const body = applyTemplate($('dm-body').value, m);
  navigator.clipboard.writeText(body).then(() => toast(`✓ Copiado para ${m.name || m.username}`));
};

function copyAllDms() {
  const body = $('dm-body').value;
  if (!body || !_dmPreviewMembers.length) { toast('Genera la vista previa primero'); return; }

  const all = _dmPreviewMembers.map((m) =>
    `=== ${m.name || m.username || 'Miembro'} ===\n${applyTemplate(body, m)}`
  ).join('\n\n');

  navigator.clipboard.writeText(all).then(() => toast(`✓ ${_dmPreviewMembers.length} mensajes copiados`));
}

function openDmsInSkool() {
  if (!_dmPreviewMembers.length) { toast('Genera la vista previa primero'); return; }
  const limit = Math.min(_dmPreviewMembers.length, 5);
  for (let i = 0; i < limit; i++) {
    const m = _dmPreviewMembers[i];
    const url = m.username
      ? `https://www.skool.com/@${m.username}`
      : `https://www.skool.com/${currentSlug}/member/${m.id}`;
    chrome.tabs.create({ url, active: i === 0 });
  }
  if (_dmPreviewMembers.length > 5) {
    toast(`Abriendo 5 de ${_dmPreviewMembers.length} perfiles`);
  }
}

// ── BIENVENIDA AUTOMÁTICA ─────────────────────────────────

async function loadPendingWelcomes() {
  const [config, pending] = await Promise.all([
    msg('GET_WELCOME'),
    msg('GET_PENDING_WELCOMES', { slug: currentSlug }),
  ]);
  const welcomeConfig = config || {};

  $('welcome-enabled').checked = !!welcomeConfig.enabled;
  if (welcomeConfig.templateId) {
    $('welcome-template-select').value = welcomeConfig.templateId;
  }

  renderPendingWelcomes(pending || []);
}

function renderPendingWelcomes(pending) {
  const container = $('pending-welcomes-list');
  if (!pending.length) {
    container.innerHTML = `<div class="list-empty">Sin bienvenidas pendientes.</div>`;
    return;
  }

  container.innerHTML = pending.slice(0, 20).map((item) => {
    const m = item.member;
    const personalizedBody = applyTemplate(item.template.body, m);
    return `
      <div class="welcome-item">
        <div class="welcome-item-name">${esc(m.name || m.username || 'Nuevo miembro')}</div>
        <button class="welcome-item-btn"
          onclick="copyWelcomeDm('${esc(String(m.id))}','${esc(personalizedBody).replace(/'/g, "\\'")}')">
          Copiar DM
        </button>
      </div>
    `;
  }).join('');
}

window.copyWelcomeDm = async (memberId, body) => {
  await navigator.clipboard.writeText(body);
  await msg('MARK_WELCOME_SENT', { memberId });
  toast('✓ DM copiado — marcado como enviado');
  await loadPendingWelcomes();
};

// ── CONFIGURACIÓN ─────────────────────────────────────────

async function loadSettings() {
  const s = await msg('GET_SETTINGS') || {};
  _settings = s;
  $('setting-blur').checked = !!s.blurMode;
  $('setting-dark').checked = !!s.darkMode;
  updateQuickToggles(s);
}

async function saveSettingToggle(key, value) {
  _settings[key] = value;
  await msg('SAVE_SETTINGS', { settings: _settings });
  updateQuickToggles(_settings);
  toast(`${key === 'blurMode' ? 'Blur' : 'Dark mode'} ${value ? 'activado' : 'desactivado'}`);
}

function updateQuickToggles(s) {
  $('btn-blur').classList.toggle('active', !!s.blurMode);
  $('btn-dark').classList.toggle('active', !!s.darkMode);
  $('setting-blur').checked = !!s.blurMode;
  $('setting-dark').checked = !!s.darkMode;
}

async function loadAlertsConfig() {
  const config = await msg('GET_ALERTS_CONFIG') || {};
  $('alert-churn').checked = config.churnAlert !== false;
  $('alert-new-member').checked = config.newMemberAlert !== false;
}

// ── SCAN ──────────────────────────────────────────────────

function updateScanUI(status) {
  if (!status) return;
  const wrap = $('scan-progress-wrap');
  const fill = $('scan-progress-fill');
  const text = $('scan-progress-text');
  const label = $('scan-banner-label');
  const btn = $('btn-scan');
  if (!btn) return;

  if (status.running) {
    wrap.classList.remove('hidden');
    btn.innerHTML = '⏹ Detener';
    btn.style.background = 'var(--red)';
    label.textContent = 'Escaneando comunidad...';
  } else {
    btn.innerHTML = '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> Escanear';
    btn.style.background = '';
    label.textContent = status.completedAt
      ? `Último scan: ${relativeTime(status.completedAt)}`
      : 'Escanear todos los miembros';
  }

  if (status.scanned > 0) {
    wrap.classList.remove('hidden');
    const total = _communityTotal || status.scanned;
    text.textContent = `${status.scanned} miembros escaneados${_communityTotal ? ' de ~' + _communityTotal : ''}`;
    fill.style.width = Math.min(100, (status.scanned / total) * 100) + '%';
  }
}

function startScanPolling() {
  clearInterval(_scanPollingTimer);
  _scanPollingTimer = setInterval(async () => {
    if (!currentSlug) return;
    const status = await msg('GET_SCAN_STATUS', { slug: currentSlug });
    updateScanUI(status);
    if (!status?.running) {
      clearInterval(_scanPollingTimer);
      // Refrescar datos al terminar el scan
      await loadCommunityData(currentSlug);
    }
  }, 1500);
}

// ── EVENTOS ───────────────────────────────────────────────

function bindEvents() {
  // Comunidad
  $('community-select').addEventListener('change', (e) => loadCommunityData(e.target.value));

  // Refresh
  $('btn-refresh').addEventListener('click', () => { loadCommunityData(currentSlug); toast('Datos actualizados'); });

  // Scan de miembros
  $('btn-scan').addEventListener('click', async () => {
    const status = await msg('GET_SCAN_STATUS', { slug: currentSlug });
    if (status?.running) {
      clearInterval(_scanPollingTimer);
      await msg('SAVE_SCAN_STATUS', { slug: currentSlug, running: false });
      updateScanUI({ ...status, running: false });
      toast('Scan detenido');
      return;
    }
    const result = await msg('START_SCAN_FROM_POPUP', { slug: currentSlug });
    if (!result?.ok) {
      toast(result?.error === 'not on members page'
        ? 'Ve a la sección Miembros de tu comunidad en Skool primero'
        : result?.error || 'No se pudo iniciar el scan');
      return;
    }
    toast(result.backgroundTab
      ? 'Abriendo tab en background — escaneará y se cerrará sola'
      : 'Scan iniciado — scrolleando por todos los miembros...');
    await msg('SAVE_SCAN_STATUS', { slug: currentSlug, running: true, scanned: 0 });
    updateScanUI({ running: true, scanned: 0 });
    startScanPolling();
  });

  // Scan de actividad (feed → comentarios por miembro) — Fase 3
  const btnScanFeed = $('btn-scan-feed');
  if (btnScanFeed) btnScanFeed.addEventListener('click', async () => {
    const result = await msg('START_FEED_SCAN_FROM_POPUP', { slug: currentSlug });
    if (!result?.ok) { toast(result?.error || 'No se pudo iniciar el scan de actividad'); return; }
    toast(result.backgroundTab
      ? 'Escaneando actividad en background — se cerrará sola al terminar'
      : 'Escaneando feed y comentarios...');
  });

  // Dashboard completo
  $('btn-open-dashboard').addEventListener('click', () => {
    chrome.tabs.create({ url: chrome.runtime.getURL('dashboard/index.html') });
  });

  // Settings toggle — estado EXPLÍCITO para que dashboard y settings nunca queden
  // ambos visibles a la vez (un toggle ciego puede desincronizarse).
  $('btn-settings').addEventListener('click', () => {
    const settingsOpen = !$('settings-panel').classList.contains('hidden');
    $('settings-panel').classList.toggle('hidden', settingsOpen);
    $('dashboard').classList.toggle('hidden', !settingsOpen);
  });
  $('btn-close-settings').addEventListener('click', () => {
    $('dashboard').classList.remove('hidden');
    $('settings-panel').classList.add('hidden');
  });

  // Blur / Dark mode quick toggles (header)
  $('btn-blur').addEventListener('click', () => saveSettingToggle('blurMode', !_settings.blurMode));
  $('btn-dark').addEventListener('click', () => saveSettingToggle('darkMode', !_settings.darkMode));

  // Settings: blur / dark mode
  $('setting-blur').addEventListener('change', (e) => saveSettingToggle('blurMode', e.target.checked));
  $('setting-dark').addEventListener('change', (e) => saveSettingToggle('darkMode', e.target.checked));

  // Alertas
  $('alert-churn').addEventListener('change', async () => {
    await msg('SAVE_ALERTS_CONFIG', { config: {
      churnAlert: $('alert-churn').checked,
      newMemberAlert: $('alert-new-member').checked,
    }});
  });
  $('alert-new-member').addEventListener('change', async () => {
    await msg('SAVE_ALERTS_CONFIG', { config: {
      churnAlert: $('alert-churn').checked,
      newMemberAlert: $('alert-new-member').checked,
    }});
  });

  // Exportar MD
  $('btn-export-md').addEventListener('click', async () => {
    if (!currentSlug) return;
    const stats = await msg('GET_STATS', { slug: currentSlug });
    const md = window.SkoopMdExport.generateCommunityMd({ stats, members: currentMembers, feed: currentFeed, slug: currentSlug });
    downloadFile(md, `skoop-${currentSlug}-${datestamp()}.md`, 'text/markdown');
    toast('✓ .md descargado — ábrelo con Claude');
  });

  // Exportar JSON
  $('btn-export-json').addEventListener('click', async () => {
    if (!currentSlug) return;
    const raw = await msg('EXPORT_RAW', { slug: currentSlug });
    downloadFile(JSON.stringify(raw, null, 2), `skoop-${currentSlug}-${datestamp()}.json`, 'application/json');
    toast('✓ JSON descargado');
  });

  // Limpiar datos
  $('btn-clear').addEventListener('click', async () => {
    const slug = $('setting-clear-community').value;
    if (!slug) return;
    if (!confirm(`¿Borrar todos los datos de "${slug}"?`)) return;
    await msg('CLEAR_COMMUNITY', { slug });
    toast('Datos borrados');
    await init();
  });

  // Botón licencia: si hay llave activa → desactivar; si no → ir a activar.
  $('btn-deactivate').addEventListener('click', async () => {
    const s = await chrome.storage.local.get('skoop_license').catch(() => ({}));
    if (s.skoop_license?.valid) {
      if (!confirm('¿Desactivar la licencia en este dispositivo? Volverás al modo gratis.')) return;
      await chrome.storage.local.remove('skoop_license');
    }
    window.location.href = 'license.html';
  });

  // Info de licencia (muestra el plan o "modo gratis").
  chrome.storage.local.get('skoop_license').then((stored) => {
    const lic = stored.skoop_license;
    const el = $('license-info');
    const btn = $('btn-deactivate');
    if (lic?.valid) {
      if (el) el.textContent = `Plan ${(lic.plan || '').toUpperCase()} · ${lic.email || ''}${lic.activatedAt ? ` · activada ${new Date(lic.activatedAt).toLocaleDateString('es-MX')}` : ''}`;
      if (btn) btn.textContent = 'Desactivar licencia';
    } else {
      if (el) el.innerHTML = '🔓 <strong>Modo gratis</strong> — data limitada e IA bloqueada. Activa tu llave para desbloquear todo.';
      if (btn) { btn.textContent = '🔑 Activar llave'; btn.classList.remove('btn-danger'); btn.classList.add('btn-primary'); }
    }
  });

  // ── TABS ──────────────────────────────────────────────────
  document.querySelectorAll('.tab').forEach((tab) => {
    tab.addEventListener('click', () => {
      document.querySelectorAll('.tab').forEach((t) => t.classList.remove('active'));
      // Ocultar TODAS las pestañas: quitar 'active' Y poner 'hidden'.
      // (.hidden es display:none !important, así que hay que quitarlo en la activa
      //  o la pestaña nunca se ve aunque tenga 'active'.)
      document.querySelectorAll('.tab-content').forEach((c) => {
        c.classList.remove('active');
        c.classList.add('hidden');
      });
      tab.classList.add('active');
      const target = $(`tab-${tab.dataset.tab}`);
      if (target) {
        target.classList.add('active');
        target.classList.remove('hidden');
      }
    });
  });

  // ── FILTROS MIEMBROS ──────────────────────────────────────
  document.querySelectorAll('#tab-members .filter-btn').forEach((btn) => {
    btn.addEventListener('click', () => {
      document.querySelectorAll('#tab-members .filter-btn').forEach((b) => b.classList.remove('active'));
      btn.classList.add('active');
      renderMembers(btn.dataset.filter);
    });
  });

  // ── ETIQUETAS ─────────────────────────────────────────────
  $('btn-new-label').addEventListener('click', showNewLabelForm);

  $('label-member-select').addEventListener('change', (e) => showLabelAssignArea(e.target.value));

  $('label-filter-select').addEventListener('change', (e) => renderLabeledMembers(e.target.value));

  // ── PIPELINE ──────────────────────────────────────────────
  document.querySelectorAll('.pipeline-stage-filter .filter-btn').forEach((btn) => {
    btn.addEventListener('click', () => {
      document.querySelectorAll('.pipeline-stage-filter .filter-btn').forEach((b) => b.classList.remove('active'));
      btn.classList.add('active');
      renderPipelineTab(btn.dataset.stage);
    });
  });

  $('btn-add-pipeline').addEventListener('click', async () => {
    const memberId = $('pipeline-member-select').value;
    const stage = $('pipeline-stage-select').value;
    const notes = $('pipeline-notes').value;
    if (!memberId) { toast('Selecciona un miembro'); return; }
    await msg('SAVE_PIPELINE', { slug: currentSlug, memberId, stage, notes });
    // Actualizar en memoria
    const m = currentMembers.find((m) => String(m.id) === memberId);
    if (m) m.pipeline = { stage, notes, updatedAt: Date.now() };
    $('pipeline-notes').value = '';
    $('pipeline-member-select').value = '';
    renderPipelineTab('');
    renderMembers(currentFilter);
    toast('Miembro agregado al pipeline');
  });

  // ── PLANTILLAS ────────────────────────────────────────────
  $('btn-new-template').addEventListener('click', () => showTemplateForm());
  $('btn-cancel-template').addEventListener('click', hideTemplateForm);
  $('btn-save-template').addEventListener('click', saveCurrentTemplate);

  $('dm-template-select').addEventListener('change', (e) => {
    const tpl = _templates.find((t) => t.id === e.target.value);
    if (tpl) $('dm-body').value = tpl.body;
  });

  // ── DM MASIVO ─────────────────────────────────────────────
  $('btn-dm-preview').addEventListener('click', generateDmPreview);
  $('btn-dm-copy-all').addEventListener('click', copyAllDms);
  $('btn-dm-open-skool').addEventListener('click', openDmsInSkool);

  // Enviar emails via Resend
  $('btn-send-emails').addEventListener('click', async () => {
    // Envío masivo = función Pro. En modo gratis manda a activar la llave.
    const _lic = await chrome.storage.local.get('skoop_license').catch(() => ({}));
    if (!_lic.skoop_license?.valid) {
      toast('🔒 Activa tu llave para enviar emails');
      window.location.href = 'license.html';
      return;
    }
    const body = $('dm-body').value;
    if (!body) { toast('Escribe el mensaje primero'); return; }
    if (!_dmPreviewMembers.length) { toast('Genera la vista previa primero'); return; }

    const withEmail = _dmPreviewMembers.filter((m) => m.email);
    if (withEmail.length === 0) {
      toast('Ningún miembro tiene email capturado — haz el scan primero');
      return;
    }

    if (!confirm(`Enviar ${withEmail.length} emails via Resend ¿confirmas?`)) return;

    const statusEl = $('email-send-status');
    statusEl.textContent = `Enviando ${withEmail.length} emails...`;
    statusEl.classList.remove('hidden');

    const recipients = withEmail.map((m) => ({
      email: m.email,
      name: m.name || m.username,
      username: m.username || '',
      daysSince: m.daysSinceLastPost,
    }));

    const subject = prompt('Asunto del email:', 'Te extrañamos en la comunidad') || 'Te extrañamos en la comunidad';

    try {
      const resp = await fetch('https://skoop-api.vercel.app/api/send-email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ recipients, subject, body }),
      });
      const result = await resp.json();
      statusEl.textContent = `✓ ${result.sent} enviados · ${result.failed} fallidos`;
      toast(`✓ ${result.sent} emails enviados`);
    } catch (err) {
      statusEl.textContent = `Error: ${err.message}`;
      toast('Error al enviar emails');
    }
  });

  // ── BIENVENIDA ────────────────────────────────────────────
  $('welcome-enabled').addEventListener('change', async (e) => {
    const templateId = $('welcome-template-select').value;
    await msg('SAVE_WELCOME', { config: { enabled: e.target.checked, templateId } });
    toast(`Bienvenida automática ${e.target.checked ? 'activada' : 'desactivada'}`);
  });

  $('welcome-template-select').addEventListener('change', async (e) => {
    const enabled = $('welcome-enabled').checked;
    await msg('SAVE_WELCOME', { config: { templateId: e.target.value, enabled } });
  });
}

// ── UTILIDADES ────────────────────────────────────────────

function msg(type, extra = {}) {
  return new Promise((resolve) => {
    const timer = setTimeout(() => resolve(null), 5000);
    chrome.runtime.sendMessage({ type, ...extra })
      .then((res) => { clearTimeout(timer); resolve(res ?? null); })
      .catch(() => { clearTimeout(timer); resolve(null); });
  });
}

function $(id) { return document.getElementById(id); }

function setText(id, val) {
  const el = $(id);
  if (el) el.textContent = val;
}

function show(id) {
  ['state-no-skool', 'state-loading', 'state-no-data', 'dashboard'].forEach((s) => {
    const el = $(s);
    if (el) el.classList.toggle('hidden', s !== id);
  });
}

function esc(str) {
  return String(str || '')
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function relativeTime(ts) {
  if (!ts) return '';
  const diff = Date.now() - ts;
  const min = Math.floor(diff / 60000);
  const h = Math.floor(diff / 3600000);
  const d = Math.floor(diff / 86400000);
  if (min < 1) return 'ahora';
  if (min < 60) return `${min}m`;
  if (h < 24) return `${h}h`;
  if (d === 1) return 'ayer';
  if (d < 7) return `${d}d`;
  return `${Math.floor(d / 7)}sem`;
}

function datestamp() { return new Date().toISOString().split('T')[0]; }

function downloadFile(content, filename, mimeType) {
  const blob = new Blob([content], { type: mimeType });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

function toast(message, duration = 2500) {
  const el = $('toast');
  el.textContent = message;
  el.classList.remove('hidden');
  clearTimeout(el._timer);
  el._timer = setTimeout(() => el.classList.add('hidden'), duration);
}

async function getActiveSkoolTab() {
  const tabs = await chrome.tabs.query({ url: 'https://*.skool.com/*', active: true, currentWindow: true });
  return tabs.length > 0 ? tabs[0] : null;
}

// Extrae el slug de comunidad (primer segmento del path) de una URL de Skool.
// Devuelve null para paths del sistema o perfiles (@usuario), no comunidades.
function slugFromUrl(url) {
  try {
    const u = new URL(url);
    const parts = u.pathname.split('/').filter(Boolean);
    const RESERVED = ['api', '_next', 'static', 'favicon', 'login', 'signup'];
    const raw = parts[0] || '';
    if (!raw || RESERVED.includes(raw) || raw.startsWith('@')) return null;
    return raw;
  } catch {
    return null;
  }
}
