// admin-newsletter.jsx — Email Hub
// CDN + in-browser-Babel / window-global style (no ES imports).
// React is the CDN global; Icon/Btn/Tabs come from admin-shell.jsx;
// Supabase is window.supabase; the canonical email template comes from
// window.AdminEmailTemplates (admin-templates.js).
//
// The Email Hub has three tabs, kept deliberately separate:
//   • Broadcasts — one-off campaigns sent to the whole list.
//   • Sequences  — multi-step automated drip flows, built on a Zapier-style
//                  drag-and-drop canvas (trigger → email/delay steps).
//   • Analytics  — summary counters.
// Every email — broadcast or sequence step — is rendered through the one
// canonical WiseFunnel template so all mail leaving WiseFunnel via Resend
// shares a single on-brand layout.
const { useState: useStateN, useEffect: useEffectN } = React;
const Icon = window.Icon;
const Tmpl = window.AdminEmailTemplates;

function generateUuid() {
  if (typeof crypto !== 'undefined' && crypto.randomUUID) {
    return crypto.randomUUID();
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

// ── Data service ────────────────────────────────────────────────
const NewsletterAPI = (function () {
  const SUPABASE_URL      = 'https://iwvlmpgeodctctmaacja.supabase.co';
  const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml3dmxtcGdlb2RjdGN0bWFhY2phIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjYwMzY3MTUsImV4cCI6MjA4MTYxMjcxNX0.hMs9OGPTdhPAs0lSKzDWE1Cc-K27NoW11--niolkoWY';
  let _db = null;
  const db = () => (_db || (_db = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY)));
  const workspaceId = () => {
    const wsId = localStorage.getItem('workspace_id') || localStorage.getItem('active_workspace_id') || '';
    if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(wsId)) {
      return wsId;
    }
    return null;
  };
  const SEQ_KEY = 'wf_email_sequences';

  async function getCampaigns() {
    try {
      const { data, error } = await db()
        .from('newsletter_campaigns')
        .select('*')
        .order('created_at', { ascending: false });
      if (error) throw error;
      return (data || []).map(c => ({
        id: c.id, name: c.name, type: c.type, html: c.html, subject: c.subject,
        status: c.status, recipientCount: c.recipient_count,
      }));
    } catch (e) {
      console.error('[Newsletter] getCampaigns failed:', e.message);
      return [];
    }
  }

  async function saveDraftCampaign(name, subject, html, type) {
    try {
      const now = new Date().toISOString();
      const { error } = await db().from('newsletter_campaigns').insert({
        workspace_id: workspaceId(), name, subject, html, type,
        status: 'draft', created_at: now, updated_at: now,
      });
      if (error) throw error;
      return { success: true };
    } catch (e) {
      console.error('[Newsletter] saveDraftCampaign failed:', e.message);
      return { success: false, error: e.message };
    }
  }

  async function deleteCampaign(id) {
    try {
      const { error } = await db()
        .from('newsletter_campaigns').delete().eq('id', id).eq('status', 'draft');
      if (error) throw error;
      return { success: true };
    } catch (e) {
      return { success: false, error: e.message };
    }
  }

  async function sendBroadcastEmail(campaignId, subject, html) {
    try {
      const { data, error } = await db().functions.invoke('newsletter-send', {
        body: { workspace_id: workspaceId(), campaign_id: campaignId, type: 'broadcast', subject, html },
      });
      if (error) throw error;
      await db().from('newsletter_campaigns')
        .update({ status: 'sent', sent_at: new Date().toISOString() }).eq('id', campaignId);
      return { success: true, sent: data?.sent_count || 0 };
    } catch (e) {
      console.error('[Newsletter] sendBroadcastEmail failed:', e.message);
      return { success: false, error: e.message };
    }
  }

  async function fetchTrelloFeatures(limit) {
    try {
      const { data, error } = await db().functions.invoke('trello-fetch-cards', {
        body: { board_name: 'marketing', list_name: 'New Features', limit: limit || 10 },
      });
      if (error) throw error;
      return (data?.cards || []).map(card => ({
        id: card.id, title: card.name, description: card.desc || '',
        trelloUrl: card.url, imageUrl: card.imageUrl,
      }));
    } catch (e) {
      console.error('[Newsletter] fetchTrelloFeatures failed:', e.message);
      return [];
    }
  }

  // ── Email sequences ────────────────────────────────────────────
  // Stored in localStorage so the builder works regardless of whether the
  // newsletter DB migration has been deployed. Each saved sequence keeps its
  // trigger + ordered steps; activating one wires it into Resend server-side.
  async function getSequences() {
    try {
      const { data, error } = await db()
        .from('sequence_definitions')
        .select('*')
        .order('created_at', { ascending: false });

      let list = [];
      if (data && !error) {
        list = data.map(s => ({
          id: s.id,
          name: s.name,
          trigger: s.trigger,
          steps: s.steps || [],
          deployed_at: s.deployed_at,
          active: !!s.active,
        }));
      } else {
        list = JSON.parse(localStorage.getItem(SEQ_KEY) || '[]');
      }

      let updated = false;
      const upgraded = list.map(s => {
        if (s && s.id && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s.id)) {
          s.id = generateUuid();
          updated = true;
        }
        return s;
      });
      if (updated) {
        localStorage.setItem(SEQ_KEY, JSON.stringify(upgraded));
      }
      return upgraded;
    } catch (e) {
      try {
        return JSON.parse(localStorage.getItem(SEQ_KEY) || '[]');
      } catch (_) {
        return [];
      }
    }
  }
  async function saveSequence(seq) {
    if (!seq.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seq.id)) {
      seq.id = generateUuid();
    }
    const all = await getSequences();
    const i = all.findIndex(s => s.id === seq.id);
    if (i >= 0) all[i] = seq; else all.unshift(seq);
    localStorage.setItem(SEQ_KEY, JSON.stringify(all));

    try {
      await db().from('sequence_definitions').upsert({
        workspace_id: workspaceId(),
        id: seq.id,
        name: seq.name,
        trigger: seq.trigger,
        steps: seq.steps || [],
        deployed_at: seq.deployed_at || null,
      });
    } catch (e) {
      console.warn('[Newsletter] saveSequence to Supabase failed:', e.message);
    }
    return { success: true };
  }
  async function deleteSequence(id) {
    localStorage.setItem(SEQ_KEY, JSON.stringify((await getSequences()).filter(s => s.id !== id)));
    try {
      await db().from('sequence_definitions').delete().eq('id', id);
    } catch (e) {
      console.warn('[Newsletter] deleteSequence from Supabase failed:', e.message);
    }
    return { success: true };
  }
  // ── Email templates ────────────────────────────────────────────
  // Stored locally (DB cached when migration is deployed). Used by the
  // Templates tab and by the Deploy button (one row per sequence email step).
  const TPL_KEY = 'wf_email_templates';
  async function getTemplates() {
    try {
      const { data, error } = await db()
        .from('email_templates')
        .select('*')
        .order('updated_at', { ascending: false });

      let list = [];
      if (data && !error) {
        list = data.map(t => ({
          id: t.id,
          name: t.name,
          subject: t.subject,
          model: t.model,
          html: t.html,
          category: t.category,
          source_ref: t.source_ref,
          resend_template_id: t.resend_template_id,
          updated_at: t.updated_at,
        }));
      } else {
        list = JSON.parse(localStorage.getItem(TPL_KEY) || '[]');
      }

      let updated = false;
      const upgraded = list.map(t => {
        if (t && t.id && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t.id)) {
          t.id = generateUuid();
          updated = true;
        }
        return t;
      });
      if (updated) {
        localStorage.setItem(TPL_KEY, JSON.stringify(upgraded));
      }
      return upgraded;
    } catch (e) {
      try {
        return JSON.parse(localStorage.getItem(TPL_KEY) || '[]');
      } catch (_) {
        return [];
      }
    }
  }
  async function saveTemplate(tpl) {
    if (!tpl.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tpl.id)) {
      tpl.id = generateUuid();
    }
    const all = await getTemplates();
    const i = all.findIndex(t => t.id === tpl.id);
    const row = { ...tpl, updated_at: new Date().toISOString() };
    if (i >= 0) all[i] = row; else all.unshift(row);
    localStorage.setItem(TPL_KEY, JSON.stringify(all));
    try {
      await db().from('email_templates').upsert({
        id: tpl.id, name: tpl.name, subject: tpl.subject, model: tpl.model, html: tpl.html,
        category: tpl.category || 'broadcast', source_ref: tpl.source_ref || null,
        resend_template_id: tpl.resend_template_id || null,
      });
    } catch (e) {
      console.warn('[Newsletter] saveTemplate to Supabase failed:', e.message);
    }
    return { success: true };
  }
  async function deleteTemplate(id) {
    localStorage.setItem(TPL_KEY, JSON.stringify((await getTemplates()).filter(t => t.id !== id)));
    try {
      await db().from('email_templates').delete().eq('id', id);
    } catch (e) {
      console.warn('[Newsletter] deleteTemplate from Supabase failed:', e.message);
    }
    return { success: true };
  }
  async function duplicateTemplate(id) {
    const all = await getTemplates();
    const src = all.find(t => t.id === id);
    if (!src) return { success: false, error: 'template not found' };
    const copy = { ...src, id: generateUuid(), name: 'Copy of ' + src.name, updated_at: new Date().toISOString() };
    await saveTemplate(copy);
    return { success: true, template: copy };
  }

  // ── Resend (via resend-proxy edge function) ────────────────────
  async function listResendAudiences() {
    try {
      const { data, error } = await db().functions.invoke('resend-proxy', { body: { op: 'listAudiences' } });
      if (error) throw error;
      if (!data?.configured) return { configured: false, audiences: [] };
      return { configured: true, audiences: data.data || data || [] };
    } catch (e) {
      return { configured: false, error: e.message, audiences: [] };
    }
  }
  async function renderViaEdge(model, firstName, vars) {
    try {
      const { data, error } = await db().functions.invoke('render-email-template', {
        body: { model, firstName: firstName || '{{{firstName}}}', vars: vars || {} },
      });
      if (error) throw error;
      return data.html;
    } catch (e) {
      console.warn('[Newsletter] render-email-template edge function not deployed; falling back to client renderer:', e.message);
      return Tmpl.buildWisefunnelEmail(model);
    }
  }

  // ── Deploy a sequence ──────────────────────────────────────────
  // For each email step:
  //   1. Render the HTML via render-email-template (Deno edge fn).
  //   2. POST it to Resend as a stored Template (createTemplate + publishTemplate)
  //      so dashboard Automations can reference the resulting template ID.
  //   3. Cache the row in our own email_templates table for the Templates tab.
  // Then upsert the sequence_definitions row with the per-step Resend IDs.
  // Activation is managed in the Resend dashboard, not here.
  async function pushTemplateToResend(name, subject, html) {
    try {
      // Clean HTML from double-braces variables to triple-braces
      const cleanedHtml = (html || '')
        .replace(/\{\{\{([^{}]+)\}\}\}/g, '{{$1}}')
        .replace(/\{\/\{\{([^{}]+)\}\}/g, '{{/$1}}') // fix any close-block edge cases
        .replace(/\{\{([^{}]+)\}\}/g, '{{{$1}}}');

      const { data, error } = await db().functions.invoke('resend-proxy', {
        body: { op: 'createTemplate', name, subject, html: cleanedHtml },
      });
      if (error) throw error;
      if (!data || data.configured === false) return { configured: false };
      const tid = data.id || data.data?.id;
      if (!tid) {
        const errMsg = data.message || data.error?.message || data.error || 'createTemplate returned no id';
        return { configured: true, error: `Resend error: ${errMsg}`, raw: data };
      }
      // Publish so it can be referenced from dashboard Automations.
      try {
        await db().functions.invoke('resend-proxy', { body: { op: 'publishTemplate', templateId: tid } });
      } catch (e) { /* publish is best-effort */ }
      return { configured: true, resendTemplateId: tid };
    } catch (e) {
      return { configured: false, error: e.message };
    }
  }

  async function deploySequence(seq) {
    if (!seq.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seq.id)) {
      seq.id = generateUuid();
    }
    const stepsWithTemplates = [];
    const resendIds = [];
    const warnings = [];
    for (let i = 0; i < (seq.steps || []).length; i++) {
      const step = seq.steps[i];
      if (step.type !== 'email') { stepsWithTemplates.push(step); resendIds.push(null); continue; }
      const html = await renderViaEdge(step.model);
      const tplId = generateUuid();
      let name = `${seq.name} - Step ${i + 1}: ${step.model.subject || 'Email'}`;
      if (name.length > 50) {
        name = name.slice(0, 47) + '...';
      }
      const push = await pushTemplateToResend(name, step.model.subject, html);
      if (!push.configured)        warnings.push(`Step ${i + 1}: ${push.error || 'Resend not configured'}`);
      else if (push.error)         warnings.push(`Step ${i + 1}: ${push.error}`);
      const resendTemplateId = push.resendTemplateId || null;
      await saveTemplate({
        id: tplId, name, subject: step.model.subject, model: step.model, html,
        category: 'sequence', source_ref: `sequence:${seq.id}:step:${i}`,
        resend_template_id: resendTemplateId,
      });
      stepsWithTemplates.push({ ...step, templateId: tplId, resendTemplateId });
      resendIds.push(resendTemplateId);
    }
    const deployed = { ...seq, steps: stepsWithTemplates, resend_template_ids: resendIds, deployed_at: new Date().toISOString() };
    await saveSequence(deployed);
    try {
      await db().from('sequence_definitions').upsert({
        workspace_id: workspaceId(),
        id: deployed.id, name: deployed.name, trigger: deployed.trigger,
        steps: deployed.steps, resend_template_ids: resendIds, deployed_at: deployed.deployed_at,
      });
    } catch (e) {
      console.warn('[Newsletter] sequence_definitions upsert failed (table may not exist yet):', e.message);
    }
    return { success: true, sequence: deployed, warnings };
  }

  return {
    getCampaigns, saveDraftCampaign, deleteCampaign, sendBroadcastEmail,
    fetchTrelloFeatures, getSequences, saveSequence, deleteSequence,
    getTemplates, saveTemplate, deleteTemplate, duplicateTemplate,
    listResendAudiences, renderViaEdge, deploySequence,
  };
})();

// ── Shared style tokens ─────────────────────────────────────────
const NL_CARD = { background: '#fff', border: '1px solid #E5E7EB', borderRadius: 14, padding: 20 };
const NL_INPUT = {
  width: '100%', padding: '9px 12px', border: '1px solid #E5E7EB', borderRadius: 8,
  fontFamily: 'inherit', fontSize: 13, color: '#1A2B3B', outline: 'none', background: '#fff',
};
const NL_LABEL = { display: 'block', fontSize: 12, fontWeight: 700, color: '#374151', marginBottom: 6 };
const STATUS_CFG = {
  draft:     { c: '#6B7280', bg: '#F3F4F6', icon: 'Clock',       label: 'Draft' },
  scheduled: { c: '#3B82F6', bg: '#EFF6FF', icon: 'Calendar',    label: 'Scheduled' },
  sending:   { c: '#F59E0B', bg: '#FFFBEB', icon: 'Zap',         label: 'Sending' },
  sent:      { c: '#10B981', bg: '#ECFDF5', icon: 'CheckCircle', label: 'Sent' },
};

// ── Sequence model helpers ──────────────────────────────────────
const SEQ_TRIGGERS = [
  // Lifecycle events (drive Resend automations via sequence-runner)
  { value: 'user.created',           label: 'New user signup' },
  { value: 'user.verified',          label: 'Email verified' },
  { value: 'trial.started',          label: 'Trial started' },
  { value: 'trial.expiring',         label: 'Trial expiring' },
  { value: 'subscription.created',   label: 'New subscription' },
  { value: 'subscription.updated',   label: 'Plan changed' },
  { value: 'subscription.cancelled', label: 'Subscription cancelled' },
  { value: 'funnel.created',         label: 'First funnel created' },
  { value: 'funnel.published',       label: 'Funnel published' },
  // Legacy (kept for backward compatibility with older saved sequences)
  { value: 'lead_joins',             label: 'Lead joins WiseFunnel (legacy)' },
  { value: 'funnel_submit',          label: 'Funnel form submitted (legacy)' },
  { value: 'signup',                 label: 'New account sign-up (legacy)' },
  { value: 'tag_added',              label: 'Tag added to a lead (legacy)' },
];
const triggerLabel = v => (SEQ_TRIGGERS.find(t => t.value === v) || SEQ_TRIGGERS[0]).label;
const DELAY_UNITS = ['minutes', 'hours', 'days'];

// Condition step — gates sequence continuation (filter semantics: if-true continue, else stop).
const CONDITION_FIELDS = [
  { value: 'contact.properties.funnel_count', label: 'contact.properties.funnel_count' },
  { value: 'contact.properties.plan',         label: 'contact.properties.plan' },
  { value: 'contact.properties.onboarding_status', label: 'contact.properties.onboarding_status' },
  { value: 'contact.properties.customer_status',   label: 'contact.properties.customer_status' },
  { value: 'event.plan',                      label: 'event.plan' },
  { value: 'event.daysRemaining',             label: 'event.daysRemaining' },
  { value: 'event.cycle',                     label: 'event.cycle' },
  { value: 'event.source',                    label: 'event.source' },
];
const CONDITION_OPS = [
  { value: 'equals',         label: 'equals' },
  { value: 'not_equals',     label: 'not equals' },
  { value: 'greater_than',   label: 'greater than' },
  { value: 'less_than',      label: 'less than' },
  { value: 'greater_or_eq',  label: '>=' },
  { value: 'less_or_eq',     label: '<=' },
  { value: 'contains',       label: 'contains' },
  { value: 'exists',         label: 'exists' },
];
function conditionLabel(step) {
  if (!step) return 'If';
  const f = (step.field || '').split('.').pop();
  const opObj = CONDITION_OPS.find(o => o.value === step.operator);
  const op = opObj ? opObj.label : (step.operator || '');
  if (step.operator === 'exists') return `If ${f} ${op}`;
  return `If ${f} ${op} ${step.value}`;
}

function seqUid() { return 's' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
function makeStep(type) {
  if (type === 'delay')     return { id: seqUid(), type: 'delay', amount: 2, unit: 'days' };
  if (type === 'condition') return { id: seqUid(), type: 'condition', field: 'contact.properties.funnel_count', operator: 'less_than', value: '1' };
  // An email step carries a full section model — same blocks as a broadcast,
  // but most sections start hidden (sequence emails are usually short).
  return { id: seqUid(), type: 'email', model: Tmpl.sequenceModel() };
}
// Older saved email steps stored flat fields; lift them into a section model.
// Also assigns an id if missing (prefilled sequences ship without ids).
function normalizeStep(step) {
  const withId = step.id ? step : { ...step, id: seqUid() };
  if (withId.type !== 'email' || withId.model) return withId;
  const m = Tmpl.sequenceModel();
  if (withId.subject)  m.subject = withId.subject;
  if (withId.headline) m.sections.intro.headline = withId.headline;
  if (withId.body)     m.sections.intro.body = withId.body;
  if (withId.ctaText || withId.ctaLink || withId.chipLabel) {
    m.sections.updates.items[0] = {
      chipLabel: withId.chipLabel || 'For You', chipColor: 'orange',
      title: 'Ready to keep going?', body: 'Pick up where you left off inside WiseFunnel.',
      imageUrl: '', ctaText: withId.ctaText || 'Open WiseFunnel', ctaLink: withId.ctaLink || 'https://wisefunnel.io/dashboard',
    };
  }
  return { id: withId.id, type: 'email', model: m };
}
function sequenceSummary(steps) {
  const emails = steps.filter(s => s.type === 'email').length;
  let mins = 0;
  steps.forEach(s => {
    if (s.type !== 'delay') return;
    const a = Number(s.amount) || 0;
    mins += s.unit === 'days' ? a * 1440 : s.unit === 'hours' ? a * 60 : a;
  });
  const span = mins >= 1440 ? Math.round(mins / 1440) + 'd'
    : mins >= 60 ? Math.round(mins / 60) + 'h'
    : mins + 'm';
  return { emails, span };
}

// ── Campaign list (broadcasts) ──────────────────────────────────
function NLCampaignList({ campaigns, loading, onPreview, onSend, onDelete, onBulkDelete }) {
  const [sel, setSel] = useStateN(new Set());
  const draftIds = campaigns.filter(c => c.status === 'draft').map(c => c.id);
  const allSelected = draftIds.length > 0 && draftIds.every(id => sel.has(id));
  const toggleAll = () => setSel(allSelected ? new Set() : new Set(draftIds));
  const toggleOne = (id) => setSel(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });

  if (loading) return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '48px 0', gap: 10 }}>
      <Icon name="Loader" size={26} color="#F97316" />
      <span style={{ fontSize: 13, color: '#6B7280' }}>Loading broadcasts…</span>
    </div>
  );
  if (!campaigns.length) return (
    <div style={{ textAlign: 'center', padding: '48px 20px', border: '1px dashed #D1D5DB', borderRadius: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 10 }}><Icon name="Inbox" size={36} color="#D1D5DB" /></div>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#6B7280' }}>No broadcasts yet</div>
      <div style={{ fontSize: 12, color: '#9CA3AF', marginTop: 3 }}>Create your first one-off email campaign</div>
    </div>
  );

  const selArr = [...sel];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {sel.size > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', background: '#1A2B3B', borderRadius: 10 }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: '#fff', flex: 1 }}>{sel.size} selected</span>
          <button onClick={() => { if (confirm(`Delete ${sel.size} draft(s)?`)) { onBulkDelete(selArr); setSel(new Set()); } }}
            style={{ padding: '4px 12px', borderRadius: 7, background: '#F43F5E', color: '#fff', border: 'none', cursor: 'pointer', fontSize: 12, fontWeight: 700, fontFamily: 'inherit' }}>
            Delete
          </button>
          <button onClick={() => setSel(new Set())}
            style={{ padding: '4px 10px', borderRadius: 7, background: 'transparent', color: '#9CA3AF', border: '1px solid #374151', cursor: 'pointer', fontSize: 12, fontWeight: 600, fontFamily: 'inherit' }}>
            Clear
          </button>
        </div>
      )}
      <div style={{ border: '1px solid #E5E7EB', borderRadius: 12, overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '32px 1fr 120px 90px 130px', padding: '9px 16px', background: '#F9FAFB', borderBottom: '1px solid #E5E7EB', alignItems: 'center' }}>
          <input type="checkbox" checked={allSelected} onChange={toggleAll}
            style={{ accentColor: '#F97316', width: 14, height: 14, cursor: 'pointer' }} />
          {['Campaign', 'Status', 'Recipients', 'Actions'].map(h => (
            <span key={h} style={{ fontSize: 10, fontWeight: 800, color: '#9CA3AF', textTransform: 'uppercase', letterSpacing: '0.07em' }}>{h}</span>
          ))}
        </div>
        {campaigns.map((c, i) => {
          const st = STATUS_CFG[c.status] || STATUS_CFG.draft;
          const isDraft = c.status === 'draft';
          const checked = sel.has(c.id);
          return (
            <div key={c.id}
              style={{ display: 'grid', gridTemplateColumns: '32px 1fr 120px 90px 130px', alignItems: 'center', padding: '11px 16px', borderBottom: i < campaigns.length - 1 ? '1px solid #F3F4F6' : 'none', background: checked ? '#FFF7ED' : 'transparent', transition: 'background 0.1s' }}>
              <input type="checkbox" checked={checked} onChange={() => isDraft && toggleOne(c.id)} disabled={!isDraft}
                style={{ accentColor: '#F97316', width: 14, height: 14, cursor: isDraft ? 'pointer' : 'default', opacity: isDraft ? 1 : 0.3 }} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 600, color: '#1A2B3B', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.name}</div>
                <div style={{ fontSize: 11, color: '#9CA3AF', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.subject}</div>
              </div>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, fontWeight: 700, color: st.c, background: st.bg, padding: '3px 8px', borderRadius: 999, width: 'fit-content' }}>
                <Icon name={st.icon} size={11} color={st.c} />{st.label}
              </span>
              <span style={{ fontSize: 12, color: '#6B7280' }}>{c.recipientCount || '—'}</span>
              <div style={{ display: 'flex', gap: 4 }}>
                <button onClick={() => onPreview(c.id)} title="Preview" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Eye" size={16} color="#6B7280" /></button>
                {isDraft && (
                  <>
                    <button onClick={() => onSend(c.id)} title="Send" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Send" size={16} color="#10B981" /></button>
                    <button onClick={() => onDelete(c.id)} title="Delete" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Trash2" size={16} color="#F43F5E" /></button>
                  </>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Email preview modal ─────────────────────────────────────────
function NLEmailPreview({ html, subject, onClose }) {
  return (
    <>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(26,43,59,0.45)', zIndex: 60 }} />
      <div style={{ position: 'fixed', inset: 0, zIndex: 61, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, pointerEvents: 'none' }}>
        <div style={{ pointerEvents: 'auto', background: '#fff', borderRadius: 14, boxShadow: '0 24px 60px rgba(0,0,0,0.18)', width: 'min(720px,100%)', maxHeight: '90vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 18px', borderBottom: '1px solid #E5E7EB' }}>
            <div>
              <div style={{ fontSize: 14, fontWeight: 800, color: '#1A2B3B' }}>{subject || 'Email Preview'}</div>
              <div style={{ fontSize: 11, color: '#9CA3AF', marginTop: 1 }}>WiseFunnel template · Resend-ready</div>
            </div>
            <button onClick={onClose} style={{ border: 'none', background: 'none', cursor: 'pointer', padding: 4, display: 'flex' }}><Icon name="X" size={18} color="#9CA3AF" /></button>
          </div>
          <div style={{ flex: 1, overflow: 'auto', background: '#F3F4F6', padding: 20 }}>
            <iframe srcDoc={html} title="Email Preview" style={{ width: '100%', height: 640, border: 0, borderRadius: 8, background: '#fff' }} />
          </div>
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, padding: '12px 18px', borderTop: '1px solid #E5E7EB' }}>
            <Btn variant="secondary" onClick={onClose}>Close</Btn>
            <a href={`data:text/html;charset=utf-8,${encodeURIComponent(html || '')}`} download="email-preview.html"
              style={{ display: 'inline-flex', alignItems: 'center', padding: '7px 14px', borderRadius: 8, fontSize: 12, fontWeight: 700, textDecoration: 'none', background: '#F97316', color: '#fff' }}>
              Download HTML
            </a>
          </div>
        </div>
      </div>
    </>
  );
}

// ── Block-based Email Builder ───────────────────────────────────
// The email is edited as a stack of named BLOCKS — one per template
// section (see Tmpl.SECTION_SCHEMA). Each block carries an eye toggle
// to include/exclude it from the send, and clearly named fields for
// every element inside the section (text, image-placeholder URLs,
// list items). The HTML preview is rebuilt live from the model, so
// what you see always matches what Resend will send.
const EB_CHIP_COLORS   = [{ value: 'orange', label: 'Orange' }, { value: 'green', label: 'Green' }, { value: 'blue', label: 'Blue' }];
const EB_AVATAR_COLORS = [
  { value: '#f97316', label: 'Orange' }, { value: '#2563eb', label: 'Blue' },
  { value: '#16a34a', label: 'Green' },  { value: '#7c3aed', label: 'Purple' }, { value: '#1a2b3b', label: 'Navy' },
];

// One labelled field. `full` makes it span both grid columns.
function EBField({ label, full, children }) {
  return (
    <div style={{ gridColumn: full ? '1 / -1' : 'auto' }}>
      <label style={NL_LABEL}>{label}</label>
      {children}
    </div>
  );
}
function EBText({ value, onChange, placeholder }) {
  return <input type="text" value={value || ''} placeholder={placeholder || ''} onChange={onChange} style={NL_INPUT} />;
}
function EBArea({ value, onChange }) {
  return <textarea value={value || ''} onChange={onChange} style={{ ...NL_INPUT, minHeight: 62, resize: 'vertical' }} />;
}
function EBSelect({ value, onChange, options }) {
  return (
    <select value={value} onChange={onChange} style={NL_INPUT}>
      {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
    </select>
  );
}

// A named editor block for one template section, with an eye toggle.
function EBBlock({ name, desc, enabled, open, compact, onToggleEnabled, onToggleOpen, children }) {
  return (
    <div style={{ border: `1px solid ${enabled ? '#E5E7EB' : '#EEF0F2'}`, borderRadius: 12, background: enabled ? '#fff' : '#FAFAFA', overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', opacity: enabled ? 1 : 0.65 }}>
        <button onClick={onToggleEnabled} title={enabled ? 'Hide this section from the email' : 'Show this section in the email'}
          style={{ border: 'none', background: enabled ? '#FFF7ED' : '#F3F4F6', cursor: 'pointer', width: 30, height: 30, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
          <Icon name={enabled ? 'Eye' : 'EyeOff'} size={15} color={enabled ? '#F97316' : '#9CA3AF'} />
        </button>
        <div onClick={onToggleOpen} style={{ flex: 1, minWidth: 0, cursor: 'pointer' }}>
          <div style={{ fontSize: 13, fontWeight: 800, color: '#1A2B3B', display: 'flex', alignItems: 'center', gap: 8 }}>
            {name}
            {!enabled && <span style={{ fontSize: 10, fontWeight: 700, color: '#9CA3AF', background: '#F3F4F6', padding: '1px 6px', borderRadius: 5 }}>Hidden</span>}
          </div>
          <div style={{ fontSize: 11, color: '#9CA3AF', marginTop: 1 }}>{desc}</div>
        </div>
        <button onClick={onToggleOpen} style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 4, display: 'flex' }}>
          <Icon name={open ? 'ChevronDown' : 'ChevronRight'} size={16} color="#9CA3AF" />
        </button>
      </div>
      {open && <div style={{ borderTop: '1px solid #F3F4F6', padding: 14, display: 'grid', gridTemplateColumns: compact ? '1fr' : '1fr 1fr', gap: 12 }}>{children}</div>}
    </div>
  );
}

// A small sub-card used for list items (feature updates, members, rows).
function EBItemCard({ title, onRemove, children }) {
  return (
    <div style={{ border: '1px solid #E5E7EB', borderRadius: 10, padding: 12, background: '#FCFCFD' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
        <span style={{ fontSize: 11, fontWeight: 800, color: '#6B7280', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{title}</span>
        <button onClick={onRemove} title="Remove" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 4, display: 'flex', color: '#F43F5E' }}>
          <Icon name="Trash2" size={14} color="#F43F5E" />
        </button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>{children}</div>
    </div>
  );
}

// ── EmailBlockEditor ────────────────────────────────────────────
// The section-by-section editor, shared by the broadcast builder AND
// every email step inside a sequence. Renders email meta + one EBBlock
// per template section; each block's eye toggle hides that section
// from the sent email. Driven by a model prop; reports edits via
// onChange. `compact` stacks fields in one column for narrow contexts.
function EmailBlockEditor({ model, onChange, compact }) {
  const [open, setOpen] = useStateN({ intro: true });

  const S = model.sections;
  const setMeta       = patch => onChange({ ...model, ...patch });
  const setSection    = (key, patch) => onChange({ ...model, sections: { ...model.sections, [key]: { ...model.sections[key], ...patch } } });
  const toggleEnabled = key => setSection(key, { enabled: !S[key].enabled });
  const toggleOpen    = key => setOpen(o => ({ ...o, [key]: !o[key] }));
  const setItem  = (key, idx, patch) => setSection(key, { items: S[key].items.map((it, i) => i === idx ? { ...it, ...patch } : it) });
  const addItem  = (key, blank)      => setSection(key, { items: [...S[key].items, blank] });
  const delItem  = (key, idx)        => setSection(key, { items: S[key].items.filter((_, i) => i !== idx) });
  const setMember = (idx, patch) => setSection('celebration', { members: S.celebration.members.map((m, i) => i === idx ? { ...m, ...patch } : m) });
  const addMember = ()           => setSection('celebration', { members: [...S.celebration.members, { name: 'New Member', role: 'Just joined', avatarColor: '#f97316' }] });
  const delMember = idx          => setSection('celebration', { members: S.celebration.members.filter((_, i) => i !== idx) });

  // Field editors per section key.
  const fieldsFor = (key) => {
    if (key === 'intro') return [
      <EBField key="il" label="Issue Label"><EBText value={S.intro.issueLabel} onChange={e => setSection('intro', { issueLabel: e.target.value })} /></EBField>,
      <EBField key="hl" label="Headline"><EBText value={S.intro.headline} onChange={e => setSection('intro', { headline: e.target.value })} /></EBField>,
      <EBField key="bd" label="Intro Paragraph" full><EBArea value={S.intro.body} onChange={e => setSection('intro', { body: e.target.value })} /></EBField>,
    ];
    if (key === 'hero') return [
      <EBField key="iu" label="Hero Image — placeholder URL" full><EBText value={S.hero.imageUrl} onChange={e => setSection('hero', { imageUrl: e.target.value })} placeholder="https://…/image.webp" /></EBField>,
      <EBField key="ia" label="Image Alt Text" full><EBText value={S.hero.imageAlt} onChange={e => setSection('hero', { imageAlt: e.target.value })} /></EBField>,
    ];
    if (key === 'updates') return [
      <div key="list" style={{ gridColumn: '1 / -1', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {S.updates.items.map((u, idx) => (
          <EBItemCard key={idx} title={`Feature Update ${idx + 1}`} onRemove={() => delItem('updates', idx)}>
            <EBField label="Chip Label"><EBText value={u.chipLabel} onChange={e => setItem('updates', idx, { chipLabel: e.target.value })} /></EBField>
            <EBField label="Chip Color"><EBSelect value={u.chipColor} options={EB_CHIP_COLORS} onChange={e => setItem('updates', idx, { chipColor: e.target.value })} /></EBField>
            <EBField label="Title" full><EBText value={u.title} onChange={e => setItem('updates', idx, { title: e.target.value })} /></EBField>
            <EBField label="Body" full><EBArea value={u.body} onChange={e => setItem('updates', idx, { body: e.target.value })} /></EBField>
            <EBField label="Screenshot Image — placeholder URL" full><EBText value={u.imageUrl} onChange={e => setItem('updates', idx, { imageUrl: e.target.value })} placeholder="https://…/screenshot.webp" /></EBField>
            <EBField label="CTA Button Text"><EBText value={u.ctaText} onChange={e => setItem('updates', idx, { ctaText: e.target.value })} /></EBField>
            <EBField label="CTA Link"><EBText value={u.ctaLink} onChange={e => setItem('updates', idx, { ctaLink: e.target.value })} /></EBField>
          </EBItemCard>
        ))}
        <Btn variant="secondary" onClick={() => addItem('updates', { chipLabel: 'New Feature', chipColor: 'green', title: 'New feature title', body: 'Describe what shipped.', imageUrl: '', ctaText: 'Learn more', ctaLink: 'https://wisefunnel.io' })}>
          <Icon name="Plus" size={13} color="#1A2B3B" /> Add Feature Update
        </Btn>
      </div>,
    ];
    if (key === 'improvement') return [
      <EBField key="c" label="Chip Label"><EBText value={S.improvement.chipLabel} onChange={e => setSection('improvement', { chipLabel: e.target.value })} /></EBField>,
      <EBField key="t" label="Title"><EBText value={S.improvement.title} onChange={e => setSection('improvement', { title: e.target.value })} /></EBField>,
      <EBField key="b" label="Body" full><EBArea value={S.improvement.body} onChange={e => setSection('improvement', { body: e.target.value })} /></EBField>,
    ];
    if (key === 'celebration') return [
      <EBField key="t" label="Title" full><EBText value={S.celebration.title} onChange={e => setSection('celebration', { title: e.target.value })} /></EBField>,
      <EBField key="m" label="Welcome Message" full><EBArea value={S.celebration.message} onChange={e => setSection('celebration', { message: e.target.value })} /></EBField>,
      <div key="members" style={{ gridColumn: '1 / -1', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {S.celebration.members.map((m, idx) => (
          <EBItemCard key={idx} title={`Member ${idx + 1}`} onRemove={() => delMember(idx)}>
            <EBField label="Name"><EBText value={m.name} onChange={e => setMember(idx, { name: e.target.value })} /></EBField>
            <EBField label="Role / Caption"><EBText value={m.role} onChange={e => setMember(idx, { role: e.target.value })} /></EBField>
            <EBField label="Avatar Color" full><EBSelect value={m.avatarColor} options={EB_AVATAR_COLORS} onChange={e => setMember(idx, { avatarColor: e.target.value })} /></EBField>
          </EBItemCard>
        ))}
        <Btn variant="secondary" onClick={addMember}><Icon name="Plus" size={13} color="#1A2B3B" /> Add Member</Btn>
      </div>,
    ];
    if (key === 'alsoShipped') return [
      <EBField key="t" label="Section Title" full><EBText value={S.alsoShipped.title} onChange={e => setSection('alsoShipped', { title: e.target.value })} /></EBField>,
      <div key="rows" style={{ gridColumn: '1 / -1', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {S.alsoShipped.items.map((it, idx) => (
          <EBItemCard key={idx} title={`Row ${idx + 1}`} onRemove={() => delItem('alsoShipped', idx)}>
            <EBField label="Tag"><EBText value={it.tag} onChange={e => setItem('alsoShipped', idx, { tag: e.target.value })} /></EBField>
            <EBField label="Description"><EBText value={it.text} onChange={e => setItem('alsoShipped', idx, { text: e.target.value })} /></EBField>
          </EBItemCard>
        ))}
        <Btn variant="secondary" onClick={() => addItem('alsoShipped', { tag: 'Area', text: 'What changed.' })}>
          <Icon name="Plus" size={13} color="#1A2B3B" /> Add Row
        </Btn>
      </div>,
    ];
    if (key === 'liveEvent') return [
      <EBField key="c" label="Chip Label"><EBText value={S.liveEvent.chipLabel} onChange={e => setSection('liveEvent', { chipLabel: e.target.value })} /></EBField>,
      <EBField key="t" label="Title"><EBText value={S.liveEvent.title} onChange={e => setSection('liveEvent', { title: e.target.value })} /></EBField>,
      <EBField key="b" label="Body" full><EBArea value={S.liveEvent.body} onChange={e => setSection('liveEvent', { body: e.target.value })} /></EBField>,
      <EBField key="d" label="Date"><EBText value={S.liveEvent.date} onChange={e => setSection('liveEvent', { date: e.target.value })} /></EBField>,
      <EBField key="ti" label="Time"><EBText value={S.liveEvent.time} onChange={e => setSection('liveEvent', { time: e.target.value })} /></EBField>,
      <EBField key="ct" label="CTA Button Text"><EBText value={S.liveEvent.ctaText} onChange={e => setSection('liveEvent', { ctaText: e.target.value })} /></EBField>,
      <EBField key="cl" label="CTA Link"><EBText value={S.liveEvent.ctaLink} onChange={e => setSection('liveEvent', { ctaLink: e.target.value })} /></EBField>,
    ];
    if (key === 'signoff') return [
      <EBField key="l" label="Closing Line"><EBText value={S.signoff.line} onChange={e => setSection('signoff', { line: e.target.value })} /></EBField>,
      <EBField key="tm" label="Team Name"><EBText value={S.signoff.team} onChange={e => setSection('signoff', { team: e.target.value })} /></EBField>,
    ];
    return null;
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {/* Email meta */}
      <div style={{ display: 'grid', gridTemplateColumns: compact ? '1fr' : '1fr 1fr', gap: 12 }}>
        <div><label style={NL_LABEL}>Email Subject</label>
          <input type="text" value={model.subject} onChange={e => setMeta({ subject: e.target.value })} style={NL_INPUT} /></div>
        <div><label style={NL_LABEL}>Preheader (inbox preview text)</label>
          <input type="text" value={model.preheader} onChange={e => setMeta({ preheader: e.target.value })} style={NL_INPUT} /></div>
      </div>

      {/* Section blocks */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {Tmpl.SECTION_SCHEMA.map(sch => (
          <EBBlock key={sch.key} name={sch.name} desc={sch.desc} compact={compact}
            enabled={S[sch.key].enabled} open={!!open[sch.key]}
            onToggleEnabled={() => toggleEnabled(sch.key)} onToggleOpen={() => toggleOpen(sch.key)}>
            {fieldsFor(sch.key)}
          </EBBlock>
        ))}
      </div>
    </div>
  );
}

// ── Broadcast builder — full WiseFunnel template, every section on. ──
function NLBuilder({ onSave, saving, audiences }) {
  const [model, setModel]     = useStateN(() => Tmpl.defaultModel());
  const [preview, setPreview] = useStateN(false);
  const [audienceId, setAudienceId] = useStateN('');
  const html = Tmpl.buildWisefunnelEmail(model);
  const enabledCount = Object.keys(model.sections).filter(k => model.sections[k].enabled).length;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div>
        <div style={{ fontSize: 16, fontWeight: 800, color: '#1A2B3B' }}>Email Builder</div>
        <div style={{ fontSize: 12, color: '#9CA3AF', marginTop: 2 }}>
          Edit the WiseFunnel template block by block — toggle the eye to hide any section. {enabledCount} of {Tmpl.SECTION_SCHEMA.length} sections enabled.
        </div>
      </div>

      {/* Resend audience picker */}
      <div>
        <label style={NL_LABEL}>Resend Audience</label>
        {audiences && audiences.configured ? (
          <select value={audienceId} onChange={e => setAudienceId(e.target.value)} style={NL_INPUT}>
            <option value="">— Select a Resend audience to send to —</option>
            {(audiences.audiences || []).map(a => (
              <option key={a.id} value={a.id}>{a.name} {a.id ? `(${String(a.id).slice(0, 8)}…)` : ''}</option>
            ))}
          </select>
        ) : (
          <div style={{ ...NL_INPUT, color: '#9CA3AF', cursor: 'not-allowed' }}>
            — resend-proxy edge function not reachable (see banner above) —
          </div>
        )}
      </div>

      <EmailBlockEditor model={model} onChange={setModel} />

      <div style={{ display: 'flex', gap: 10, paddingTop: 4 }}>
        <Btn variant="secondary" onClick={() => setPreview(true)}><Icon name="Eye" size={14} color="#1A2B3B" /> Preview Email</Btn>
        <Btn variant="primary" onClick={() => onSave(html, model.subject)} disabled={saving} style={{ marginLeft: 'auto', background: '#1A2B3B' }}>
          <Icon name={saving ? 'Loader' : 'Save'} size={14} color="#fff" />
          {saving ? 'Saving…' : 'Save Draft'}
        </Btn>
      </div>

      {preview && <NLEmailPreview html={html} subject={model.subject} onClose={() => setPreview(false)} />}
    </div>
  );
}

// ── Sequence builder — palette block (draggable source) ─────────
function SeqPaletteBlock({ stepType, icon, title, desc, accent }) {
  return (
    <div draggable
      onDragStart={e => { e.dataTransfer.setData('text/plain', JSON.stringify({ op: 'new', stepType })); e.dataTransfer.effectAllowed = 'copy'; }}
      style={{ borderTop: '1px solid #E5E7EB', borderRight: '1px solid #E5E7EB', borderBottom: '1px solid #E5E7EB', borderLeft: `3px solid ${accent}`, borderRadius: 10, padding: '10px 12px', background: '#fff', cursor: 'grab', userSelect: 'none' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
        <Icon name={icon} size={14} color={accent} />
        <span style={{ fontSize: 12, fontWeight: 800, color: '#1A2B3B' }}>{title}</span>
      </div>
      <div style={{ fontSize: 10.5, color: '#9CA3AF', marginTop: 3, lineHeight: 1.4 }}>{desc}</div>
    </div>
  );
}

// ── Sequence builder — connector + drop zone between nodes ──────
function SeqDropZone({ index, dragOver, menuOpen, onDragOver, onDragLeave, onDrop, onToggleMenu, onPick }) {
  const active = dragOver === index;
  return (
    <div style={{ position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <div
        onDragOver={e => { e.preventDefault(); onDragOver(index); }}
        onDragLeave={() => onDragLeave(index)}
        onDrop={e => onDrop(e, index)}
        style={{
          width: active ? '100%' : 2,
          height: active ? 40 : 26,
          background: active ? '#FFF7ED' : '#E5E7EB',
          border: active ? '2px dashed #F97316' : 'none',
          borderRadius: active ? 10 : 0,
          transition: 'all 0.1s', display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
        {active && <span style={{ fontSize: 11, fontWeight: 800, color: '#F97316' }}>Drop here</span>}
      </div>
      {!active && (
        <button onClick={e => { e.stopPropagation(); onToggleMenu(index); }} title="Add step"
          style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 22, height: 22, borderRadius: '50%', border: '1px solid #E5E7EB', background: '#fff', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#9CA3AF', boxShadow: '0 1px 3px rgba(0,0,0,0.08)' }}>
          <Icon name="Plus" size={13} color="#9CA3AF" />
        </button>
      )}
      {menuOpen && (
        <div onClick={e => e.stopPropagation()} style={{ position: 'absolute', top: '50%', left: 'calc(50% + 20px)', transform: 'translateY(-50%)', background: '#fff', border: '1px solid #E5E7EB', borderRadius: 10, boxShadow: '0 8px 24px rgba(0,0,0,0.12)', zIndex: 20, padding: 4, width: 150 }}>
          {[
            { t: 'email',     icon: 'Mail',      label: 'Email',        c: '#F97316' },
            { t: 'delay',     icon: 'Hourglass', label: 'Wait / Delay', c: '#3B82F6' },
            { t: 'condition', icon: 'GitBranch', label: 'Condition',    c: '#8B5CF6' },
          ].map(o => (
            <button key={o.t} onClick={() => onPick(index, o.t)}
              style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', border: 'none', background: 'transparent', cursor: 'pointer', padding: '7px 8px', borderRadius: 7, fontFamily: 'inherit', fontSize: 12, fontWeight: 700, color: '#1A2B3B' }}
              onMouseEnter={e => e.currentTarget.style.background = '#F9FAFB'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
              <Icon name={o.icon} size={14} color={o.c} />{o.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Sequence builder — a single step node ───────────────────────
function SeqNode({ step, index, selected, onSelect, onUpdate, onDelete, onDragStart, onPreview }) {
  const isEmail     = step.type === 'email';
  const isCondition = step.type === 'condition';
  const isDelay     = step.type === 'delay';
  const accent  = isEmail ? '#F97316' : isCondition ? '#8B5CF6' : '#3B82F6';
  const iconName = isEmail ? 'Mail' : isCondition ? 'GitBranch' : 'Hourglass';
  const iconBg   = isEmail ? '#FFF7ED' : isCondition ? '#F5F3FF' : '#EFF6FF';
  const open    = selected === step.id;
  const enabledN = isEmail && step.model
    ? Object.keys(step.model.sections).filter(k => step.model.sections[k].enabled).length : 0;
  const title   = isEmail ? ((step.model && step.model.subject) || 'Untitled email')
                : isCondition ? conditionLabel(step)
                : `Wait ${step.amount || 0} ${step.unit}`;
  const sub     = isEmail ? `${enabledN} of ${Tmpl.SECTION_SCHEMA.length} sections shown`
                : isCondition ? 'Continue only if this rule matches'
                : 'Pause before the next step';
  return (
    <div style={{ width: '100%' }}>
      <div
        draggable
        onDragStart={e => { e.dataTransfer.setData('text/plain', JSON.stringify({ op: 'move', id: step.id })); e.dataTransfer.effectAllowed = 'move'; onDragStart(); }}
        onClick={() => onSelect(open ? null : step.id)}
        style={{
          borderTop: `1px solid ${open ? accent : '#E5E7EB'}`,
          borderRight: `1px solid ${open ? accent : '#E5E7EB'}`,
          borderBottom: `1px solid ${open ? accent : '#E5E7EB'}`,
          borderLeft: `3px solid ${accent}`,
          borderRadius: 12, background: '#fff', cursor: 'grab',
          boxShadow: open ? '0 6px 18px rgba(0,0,0,0.10)' : '0 1px 3px rgba(0,0,0,0.05)',
        }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 12px' }}>
          <Icon name="GripVertical" size={15} color="#CBD5E1" fill="#CBD5E1" />
          <div style={{ width: 30, height: 30, borderRadius: 8, background: iconBg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <Icon name={iconName} size={15} color={accent} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 12.5, fontWeight: 800, color: '#1A2B3B', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{title}</div>
            <div style={{ fontSize: 11, color: '#9CA3AF', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{sub}</div>
          </div>
          <span style={{ fontSize: 10, fontWeight: 800, color: '#9CA3AF', background: '#F3F4F6', borderRadius: 6, padding: '2px 6px' }}>{index + 1}</span>
          <button onClick={e => { e.stopPropagation(); onDelete(step.id); }} title="Remove step"
            style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 4, display: 'flex', color: '#F43F5E' }}>
            <Icon name="Trash2" size={14} color="#F43F5E" />
          </button>
        </div>

        {open && (
          <div style={{ borderTop: '1px solid #F3F4F6', padding: 12, display: 'flex', flexDirection: 'column', gap: 10 }} onClick={e => e.stopPropagation()}>
            {isEmail && (
              <>
                <div style={{ fontSize: 11, color: '#9CA3AF', fontWeight: 600 }}>
                  Edit this email section by section — toggle the eye on any block to hide it.
                </div>
                <EmailBlockEditor model={step.model} onChange={m => onUpdate(step.id, { model: m })} compact />
                <div><Btn variant="secondary" onClick={() => onPreview(step)}><Icon name="Eye" size={13} color="#1A2B3B" /> Preview email</Btn></div>
              </>
            )}
            {isDelay && (
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                <div><label style={NL_LABEL}>Wait amount</label>
                  <input type="number" min="1" style={NL_INPUT} value={step.amount} onChange={e => onUpdate(step.id, { amount: Math.max(1, parseInt(e.target.value, 10) || 1) })} /></div>
                <div><label style={NL_LABEL}>Unit</label>
                  <select style={NL_INPUT} value={step.unit} onChange={e => onUpdate(step.id, { unit: e.target.value })}>
                    {DELAY_UNITS.map(u => <option key={u} value={u}>{u}</option>)}
                  </select></div>
              </div>
            )}
            {isCondition && (
              <>
                <div style={{ fontSize: 11, color: '#9CA3AF', fontWeight: 600 }}>
                  Filter rule — if the condition is true the sequence continues, otherwise it stops here.
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                  <div><label style={NL_LABEL}>Field</label>
                    <select style={NL_INPUT} value={step.field} onChange={e => onUpdate(step.id, { field: e.target.value })}>
                      {CONDITION_FIELDS.map(f => <option key={f.value} value={f.value}>{f.label}</option>)}
                    </select></div>
                  <div><label style={NL_LABEL}>Operator</label>
                    <select style={NL_INPUT} value={step.operator} onChange={e => onUpdate(step.id, { operator: e.target.value })}>
                      {CONDITION_OPS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                    </select></div>
                </div>
                {step.operator !== 'exists' && (
                  <div><label style={NL_LABEL}>Value</label>
                    <input style={NL_INPUT} value={step.value || ''} onChange={e => onUpdate(step.id, { value: e.target.value })} placeholder="e.g. 1, 'growth', 'true'" /></div>
                )}
              </>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ── Sequence builder — the Zapier-style canvas ──────────────────
function SequenceBuilder({ initial, onSave, onCancel, onDeploy, deploying }) {
  const [name, setName]         = useStateN(initial ? initial.name : 'Untitled sequence');
  const [trigger, setTrigger]   = useStateN(initial ? initial.trigger : 'user.created');
  const [steps, setSteps]       = useStateN(initial ? initial.steps.map(normalizeStep) : [makeStep('email')]);
  const [selected, setSelected] = useStateN(null);
  const [dragOver, setDragOver] = useStateN(null);
  const [menuAt, setMenuAt]     = useStateN(null);
  const [preview, setPreview]   = useStateN(null);

  // Pre-fill with the canonical sequence for the active trigger.
  const prefill = () => {
    const lib = window.AdminPrefilledSequences || {};
    const tpl = lib[trigger] || lib._default;
    if (!tpl) { alert('No prefilled sequence for this trigger.'); return; }
    if (steps.length > 1 && !confirm(`Replace the current ${steps.length} step(s) with the ${tpl.name} prefill?`)) return;
    setName(tpl.name);
    setSteps(tpl.steps.map(normalizeStep));
    setSelected(null);
  };

  const insertStep = (index, step) => {
    const arr = steps.slice(); arr.splice(index, 0, step); setSteps(arr); setSelected(step.id);
  };
  const moveStep = (id, index) => {
    const from = steps.findIndex(s => s.id === id);
    if (from < 0) return;
    const arr = steps.slice();
    const item = arr.splice(from, 1)[0];
    let to = index; if (from < index) to = index - 1;
    arr.splice(to, 0, item); setSteps(arr);
  };
  const updateStep = (id, patch) => setSteps(steps.map(s => (s.id === id ? { ...s, ...patch } : s)));
  const removeStep = id => { setSteps(steps.filter(s => s.id !== id)); if (selected === id) setSelected(null); };

  const handleDrop = (e, index) => {
    e.preventDefault();
    setDragOver(null);
    let d; try { d = JSON.parse(e.dataTransfer.getData('text/plain')); } catch (_) { return; }
    if (d.op === 'new') insertStep(index, makeStep(d.stepType));
    else if (d.op === 'move') moveStep(d.id, index);
  };

  const previewStep = step => {
    setPreview({ subject: step.model.subject, html: Tmpl.buildWisefunnelEmail(step.model) });
  };

  const buildSeq = () => ({
    id: (initial && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(initial.id)) ? initial.id : generateUuid(),
    name: name.trim() || 'Untitled sequence',
    trigger, steps,
    active: initial ? !!initial.active : false,
    deployed_at: initial ? initial.deployed_at : null,
    updatedAt: new Date().toISOString(),
  });
  const save = () => {
    if (!steps.length) { alert('Add at least one step before saving.'); return; }
    onSave(buildSeq());
  };
  const deploy = () => {
    if (!steps.length) { alert('Add at least one step before deploying.'); return; }
    onDeploy(buildSeq());
  };

  const sum = sequenceSummary(steps);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {/* Header bar */}
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 200 }}>
          <label style={NL_LABEL}>Sequence Name</label>
          <input value={name} onChange={e => setName(e.target.value)} style={{ ...NL_INPUT, fontSize: 14, fontWeight: 700 }} />
        </div>
        <div style={{ width: 220 }}>
          <label style={NL_LABEL}>Trigger</label>
          <select value={trigger} onChange={e => setTrigger(e.target.value)} style={NL_INPUT}>
            {SEQ_TRIGGERS.map(t => <option key={t.value} value={t.value}>{t.label}</option>)}
          </select>
        </div>
        <Btn variant="secondary" onClick={prefill} title="Pre-fill with the canonical sequence for this trigger">
          <Icon name="Sparkles" size={14} color="#1A2B3B" /> Pre-fill with AI
        </Btn>
        <Btn variant="secondary" onClick={onCancel}>Cancel</Btn>
        <Btn variant="primary" onClick={save}><Icon name="Save" size={14} color="#fff" /> Save</Btn>
        <Btn variant="primary" onClick={deploy} disabled={deploying} style={{ background: '#1A2B3B' }} title="Render every email step + register the sequence in Resend">
          <Icon name={deploying ? 'Loader' : 'Rocket'} size={14} color="#fff" /> {deploying ? 'Deploying…' : 'Deploy to Resend'}
        </Btn>
      </div>

      <div style={{ fontSize: 11, fontWeight: 700, color: '#9CA3AF' }}>
        {sum.emails} email{sum.emails === 1 ? '' : 's'} · spans ~{sum.span} · drag blocks onto the canvas or use the + buttons
      </div>

      {/* Palette + canvas */}
      <div style={{ display: 'flex', gap: 16, alignItems: 'flex-start' }}>
        {/* Palette */}
        <div style={{ width: 168, flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ fontSize: 10, fontWeight: 800, color: '#9CA3AF', textTransform: 'uppercase', letterSpacing: '0.07em' }}>Blocks</div>
          <SeqPaletteBlock stepType="email"     icon="Mail"      title="Email"        desc="Send a WiseFunnel-templated email" accent="#F97316" />
          <SeqPaletteBlock stepType="delay"     icon="Hourglass" title="Wait / Delay" desc="Pause before the next step"       accent="#3B82F6" />
          <SeqPaletteBlock stepType="condition" icon="GitBranch" title="Condition"    desc="Filter: continue only if rule matches" accent="#8B5CF6" />
          <div style={{ fontSize: 10.5, color: '#9CA3AF', lineHeight: 1.5, marginTop: 4 }}>
            Drag a block onto the flow, or drag a step by its handle to reorder.
          </div>
        </div>

        {/* Canvas */}
        <div onClick={() => setMenuAt(null)}
          style={{ flex: 1, background: '#F9FAFB', border: '1px solid #E5E7EB', borderRadius: 14, padding: '24px 16px', minHeight: 320 }}>
          <div style={{ maxWidth: 460, margin: '0 auto', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
            {/* Trigger node */}
            <div style={{ width: '100%', border: '1px solid #1A2B3B', background: '#1A2B3B', borderRadius: 12, padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{ width: 30, height: 30, borderRadius: 8, background: 'rgba(249,115,22,0.22)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name="Zap" size={15} color="#F97316" fill="#F97316" />
              </div>
              <div>
                <div style={{ fontSize: 10, fontWeight: 800, color: '#F97316', textTransform: 'uppercase', letterSpacing: '0.07em' }}>Trigger</div>
                <div style={{ fontSize: 12.5, fontWeight: 700, color: '#fff' }}>{triggerLabel(trigger)}</div>
              </div>
            </div>

            {/* Steps interleaved with drop zones */}
            {steps.map((step, i) => (
              <React.Fragment key={step.id}>
                <SeqDropZone
                  index={i} dragOver={dragOver} menuOpen={menuAt === i}
                  onDragOver={setDragOver} onDragLeave={idx => setDragOver(d => (d === idx ? null : d))}
                  onDrop={handleDrop}
                  onToggleMenu={idx => setMenuAt(m => (m === idx ? null : idx))}
                  onPick={(idx, t) => { insertStep(idx, makeStep(t)); setMenuAt(null); }}
                />
                <SeqNode
                  step={step} index={i} selected={selected}
                  onSelect={setSelected} onUpdate={updateStep} onDelete={removeStep}
                  onDragStart={() => setSelected(null)} onPreview={previewStep}
                />
              </React.Fragment>
            ))}

            {/* Final drop zone */}
            <SeqDropZone
              index={steps.length} dragOver={dragOver} menuOpen={menuAt === steps.length}
              onDragOver={setDragOver} onDragLeave={idx => setDragOver(d => (d === idx ? null : d))}
              onDrop={handleDrop}
              onToggleMenu={idx => setMenuAt(m => (m === idx ? null : idx))}
              onPick={(idx, t) => { insertStep(idx, makeStep(t)); setMenuAt(null); }}
            />

            {/* End cap */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 11, fontWeight: 800, color: '#9CA3AF', background: '#fff', border: '1px dashed #D1D5DB', borderRadius: 999, padding: '6px 14px' }}>
              <Icon name="CheckCircle" size={13} color="#9CA3AF" /> Sequence ends
            </div>
          </div>
        </div>
      </div>

      {preview && <NLEmailPreview html={preview.html} subject={preview.subject} onClose={() => setPreview(null)} />}
    </div>
  );
}

// ── Saved-sequences list ────────────────────────────────────────
function SequenceList({ sequences, onEdit, onDelete, onBulkDelete }) {
  const [sel, setSel] = useStateN(new Set());
  const allIds = sequences.map(s => s.id);
  const allSelected = allIds.length > 0 && allIds.every(id => sel.has(id));
  const toggleAll = () => setSel(allSelected ? new Set() : new Set(allIds));
  const toggleOne = (id) => setSel(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });

  if (!sequences.length) return (
    <div style={{ textAlign: 'center', padding: '48px 20px', border: '1px dashed #D1D5DB', borderRadius: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 10 }}><Icon name="GitBranch" size={34} color="#D1D5DB" /></div>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#6B7280' }}>No sequences yet</div>
      <div style={{ fontSize: 12, color: '#9CA3AF', marginTop: 3 }}>Build an automated drip flow with the visual builder</div>
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {/* Bulk action bar */}
      {sel.size > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', background: '#1A2B3B', borderRadius: 10 }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: '#fff', flex: 1 }}>{sel.size} selected</span>
          <button onClick={() => { if (confirm(`Delete ${sel.size} sequence(s)?`)) { onBulkDelete([...sel]); setSel(new Set()); } }}
            style={{ padding: '4px 12px', borderRadius: 7, background: '#F43F5E', color: '#fff', border: 'none', cursor: 'pointer', fontSize: 12, fontWeight: 700, fontFamily: 'inherit' }}>
            Delete
          </button>
          <button onClick={() => setSel(new Set())}
            style={{ padding: '4px 10px', borderRadius: 7, background: 'transparent', color: '#9CA3AF', border: '1px solid #374151', cursor: 'pointer', fontSize: 12, fontWeight: 600, fontFamily: 'inherit' }}>
            Clear
          </button>
        </div>
      )}
      {/* Select-all row */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 4px' }}>
        <input type="checkbox" checked={allSelected} onChange={toggleAll}
          style={{ accentColor: '#F97316', width: 14, height: 14, cursor: 'pointer' }} />
        <span style={{ fontSize: 11, color: '#9CA3AF', fontWeight: 600 }}>Select all</span>
      </div>
      {sequences.map(s => {
        const sum = sequenceSummary(s.steps || []);
        const deployed = !!s.deployed_at;
        const pushed = (s.resend_template_ids || []).filter(Boolean).length;
        const checked = sel.has(s.id);
        return (
          <div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 12, border: `1px solid ${checked ? '#FED7AA' : '#E5E7EB'}`, borderRadius: 12, padding: '12px 14px', background: checked ? '#FFF7ED' : '#fff', transition: 'all 0.1s' }}>
            <input type="checkbox" checked={checked} onChange={() => toggleOne(s.id)}
              style={{ accentColor: '#F97316', width: 14, height: 14, cursor: 'pointer', flexShrink: 0 }} />
            <div style={{ width: 34, height: 34, borderRadius: 9, background: deployed ? '#ECFDF5' : '#FFF7ED', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name="GitBranch" size={16} color={deployed ? '#10B981' : '#F97316'} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: '#1A2B3B', display: 'flex', alignItems: 'center', gap: 7 }}>
                {s.name}
                {deployed
                  ? <span style={{ fontSize: 9, fontWeight: 800, color: '#059669', background: '#D1FAE5', padding: '2px 7px', borderRadius: 999, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Deployed{pushed ? ` · ${pushed} on Resend` : ''}</span>
                  : <span style={{ fontSize: 9, fontWeight: 800, color: '#6B7280', background: '#F3F4F6', padding: '2px 7px', borderRadius: 999, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Draft</span>}
              </div>
              <div style={{ fontSize: 11, color: '#9CA3AF' }}>
                {triggerLabel(s.trigger)} · {sum.emails} email{sum.emails === 1 ? '' : 's'} · spans ~{sum.span}
                {deployed && (<><br /><span style={{ color: '#6B7280' }}>Wire the automation in Resend's dashboard using the published template IDs.</span></>)}
              </div>
            </div>
            <Btn variant="secondary" onClick={() => onEdit(s)}><Icon name="Workflow" size={13} color="#1A2B3B" /> Edit</Btn>
            <button onClick={() => onDelete(s.id)} title="Delete" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, display: 'flex' }}>
              <Icon name="Trash2" size={16} color="#F43F5E" />
            </button>
          </div>
        );
      })}
    </div>
  );
}

// ── Templates tab — list of saved email templates with multi-select. ────
function TemplatesList({ templates, onPreview, onDelete, onBulkDelete, onBulkDuplicate }) {
  const [sel, setSel] = useStateN(new Set());
  const allIds = templates.map(t => t.id);
  const allSelected = allIds.length > 0 && allIds.every(id => sel.has(id));
  const toggleAll = () => setSel(allSelected ? new Set() : new Set(allIds));
  const toggleOne = (id) => setSel(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });

  if (!templates.length) return (
    <div style={{ textAlign: 'center', padding: '48px 20px', border: '1px dashed #D1D5DB', borderRadius: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 10 }}><Icon name="FileText" size={34} color="#D1D5DB" /></div>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#6B7280' }}>No templates yet</div>
      <div style={{ fontSize: 12, color: '#9CA3AF', marginTop: 3 }}>Deploy a sequence or build a broadcast — every email saved here is a template.</div>
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {/* Bulk action bar */}
      {sel.size > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', background: '#1A2B3B', borderRadius: 10 }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: '#fff', flex: 1 }}>{sel.size} selected</span>
          <button onClick={() => { onBulkDuplicate([...sel]); setSel(new Set()); }}
            style={{ padding: '4px 12px', borderRadius: 7, background: '#F97316', color: '#fff', border: 'none', cursor: 'pointer', fontSize: 12, fontWeight: 700, fontFamily: 'inherit' }}>
            Duplicate
          </button>
          <button onClick={() => { if (confirm(`Delete ${sel.size} template(s)?`)) { onBulkDelete([...sel]); setSel(new Set()); } }}
            style={{ padding: '4px 12px', borderRadius: 7, background: '#F43F5E', color: '#fff', border: 'none', cursor: 'pointer', fontSize: 12, fontWeight: 700, fontFamily: 'inherit' }}>
            Delete
          </button>
          <button onClick={() => setSel(new Set())}
            style={{ padding: '4px 10px', borderRadius: 7, background: 'transparent', color: '#9CA3AF', border: '1px solid #374151', cursor: 'pointer', fontSize: 12, fontWeight: 600, fontFamily: 'inherit' }}>
            Clear
          </button>
        </div>
      )}
      <div style={{ border: '1px solid #E5E7EB', borderRadius: 12, overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '32px 1fr 110px 130px 110px', padding: '9px 16px', background: '#F9FAFB', borderBottom: '1px solid #E5E7EB', alignItems: 'center' }}>
          <input type="checkbox" checked={allSelected} onChange={toggleAll}
            style={{ accentColor: '#F97316', width: 14, height: 14, cursor: 'pointer' }} />
          {['Template', 'Category', 'Updated', 'Actions'].map(h => (
            <span key={h} style={{ fontSize: 10, fontWeight: 800, color: '#9CA3AF', textTransform: 'uppercase', letterSpacing: '0.07em' }}>{h}</span>
          ))}
        </div>
        {templates.map((t, i) => {
          const checked = sel.has(t.id);
          return (
            <div key={t.id}
              style={{ display: 'grid', gridTemplateColumns: '32px 1fr 110px 130px 110px', alignItems: 'center', padding: '11px 16px', borderBottom: i < templates.length - 1 ? '1px solid #F3F4F6' : 'none', background: checked ? '#FFF7ED' : 'transparent', transition: 'background 0.1s' }}>
              <input type="checkbox" checked={checked} onChange={() => toggleOne(t.id)}
                style={{ accentColor: '#F97316', width: 14, height: 14, cursor: 'pointer' }} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 600, color: '#1A2B3B', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.name}</div>
                <div style={{ fontSize: 11, color: '#9CA3AF', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.subject || '—'}</div>
              </div>
              <span style={{ fontSize: 11, fontWeight: 700, color: t.category === 'sequence' ? '#7C3AED' : '#F97316', textTransform: 'capitalize' }}>{t.category || 'broadcast'}</span>
              <span style={{ fontSize: 11, color: '#9CA3AF' }}>{t.updated_at ? new Date(t.updated_at).toLocaleDateString() : '—'}</span>
              <div style={{ display: 'flex', gap: 4 }}>
                <button onClick={() => onPreview(t)} title="Preview" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Eye" size={16} color="#6B7280" /></button>
                <button onClick={() => { onBulkDuplicate([t.id]); }} title="Duplicate" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Copy" size={16} color="#F97316" /></button>
                <button onClick={() => onDelete(t.id)} title="Delete" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 6, borderRadius: 7, display: 'flex' }}><Icon name="Trash2" size={16} color="#F43F5E" /></button>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Email Hub page ──────────────────────────────────────────────
function NewsletterPage() {
  const [tab, setTab]               = useStateN('broadcasts');
  const [campaigns, setCampaigns]   = useStateN([]);
  const [sequences, setSequences]   = useStateN([]);
  const [loading, setLoading]       = useStateN(false);
  const [saving, setSaving]         = useStateN(false);
  const [preview, setPreview]       = useStateN(null);
  const [creating, setCreating]     = useStateN(false);   // broadcast create view
  const [editingSeq, setEditingSeq] = useStateN(null);    // null | {} (new) | sequence
  const [deploying, setDeploying]   = useStateN(false);
  const [templates, setTemplates]   = useStateN([]);
  const [audiences, setAudiences]   = useStateN({ configured: false, audiences: [], loaded: false });

  useEffectN(() => {
    NewsletterAPI.listResendAudiences().then(r => setAudiences({ ...r, loaded: true }));
  }, []);

  // Broadcasts = every campaign that is not a saved sequence.
  const broadcasts = campaigns.filter(c => c.type !== 'sequence');

  const loadCampaigns = async () => {
    setLoading(true);
    setCampaigns(await NewsletterAPI.getCampaigns());
    setLoading(false);
  };
  const loadSequences = async () => {
    setLoading(true);
    setSequences(await NewsletterAPI.getSequences());
    setLoading(false);
  };
  const loadTemplates = async () => {
    setLoading(true);
    setTemplates(await NewsletterAPI.getTemplates());
    setLoading(false);
  };

  useEffectN(() => {
    if (tab === 'broadcasts') loadCampaigns();
    if (tab === 'sequences') loadSequences();
    if (tab === 'templates') loadTemplates();
  }, [tab]);

  const handleSaveBroadcast = async (html, subject) => {
    setSaving(true);
    const res = await NewsletterAPI.saveDraftCampaign(subject, subject, html, 'broadcast');
    setSaving(false);
    if (res.success) { alert('Draft saved.'); setCreating(false); loadCampaigns(); }
    else alert('Failed to save draft: ' + (res.error || 'unknown error'));
  };

  const handleSend = async (id) => {
    if (!confirm('Send this email to all subscribers?')) return;
    const c = campaigns.find(x => x.id === id);
    const res = await NewsletterAPI.sendBroadcastEmail(id, c?.subject, c?.html);
    if (res.success) { alert(`Email sent to ${res.sent} subscribers.`); loadCampaigns(); }
    else alert('Failed to send: ' + (res.error || 'unknown error'));
  };

  const handleDelete = async (id) => {
    if (!confirm('Delete this campaign?')) return;
    const res = await NewsletterAPI.deleteCampaign(id);
    if (res.success) loadCampaigns();
    else alert('Failed to delete: ' + (res.error || 'unknown error'));
  };

  const handlePreview = (id) => {
    const c = campaigns.find(x => x.id === id);
    if (c) setPreview({ html: c.html, subject: c.subject });
  };

  const handleSaveSequence = async (seq) => {
    await NewsletterAPI.saveSequence(seq);
    await loadSequences();
    setEditingSeq(null);
  };
  const handleDeleteSequence = async (id) => {
    if (!confirm('Delete this sequence?')) return;
    await NewsletterAPI.deleteSequence(id);
    await loadSequences();
  };
  const handleBulkDeleteCampaigns = async (ids) => {
    await Promise.all(ids.map(id => NewsletterAPI.deleteCampaign(id)));
    await loadCampaigns();
  };
  const handleBulkDeleteSequences = async (ids) => {
    await Promise.all(ids.map(id => NewsletterAPI.deleteSequence(id)));
    await loadSequences();
  };
  const handleBulkDeleteTemplates = async (ids) => {
    await Promise.all(ids.map(id => NewsletterAPI.deleteTemplate(id)));
    await loadTemplates();
  };
  const handleBulkDuplicateTemplates = async (ids) => {
    await Promise.all(ids.map(id => NewsletterAPI.duplicateTemplate(id)));
    await loadTemplates();
  };
  const handleDeploySequence = async (seq) => {
    setDeploying(true);
    try {
      const res = await NewsletterAPI.deploySequence(seq);
      await loadSequences();
      await loadTemplates();
      if (res.success) {
        const emails = (res.sequence.steps || []).filter(s => s.type === 'email').length;
        const pushed = (res.sequence.resend_template_ids || []).filter(Boolean).length;
        const warnMsg = (res.warnings && res.warnings.length) ? `\n\nWarnings:\n - ${res.warnings.join('\n - ')}` : '';
        alert(
          `Deployed "${seq.name}".\n` +
          `${pushed} of ${emails} email template(s) pushed to Resend.\n\n` +
          `Next: open Resend's dashboard → Automations and wire the trigger using the published template IDs.${warnMsg}`
        );
        setEditingSeq(null);
      }
    } catch (e) {
      alert('Deploy failed: ' + e.message);
    } finally {
      setDeploying(false);
    }
  };
  const handlePreviewTemplate = (t) => setPreview({ html: t.html || (t.model && Tmpl.buildWisefunnelEmail(t.model)) || '', subject: t.subject || t.name });
  const handleDeleteTemplate  = async (id) => {
    if (!confirm('Delete this template?')) return;
    await NewsletterAPI.deleteTemplate(id);
    await loadTemplates();
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontSize: 16, fontWeight: 800, color: '#1A2B3B' }}>Email Hub</div>
          <div style={{ fontSize: 12, color: '#9CA3AF', marginTop: 2 }}>Broadcasts and automated sequences — all sent via Resend</div>
        </div>
        {tab === 'broadcasts' && !creating && (
          <Btn variant="primary" onClick={() => setCreating(true)}>
            <Icon name="Plus" size={14} color="#fff" /> New Broadcast
          </Btn>
        )}
        {tab === 'sequences' && !editingSeq && (
          <Btn variant="primary" onClick={() => setEditingSeq({})}>
            <Icon name="Plus" size={14} color="#fff" /> New Sequence
          </Btn>
        )}
      </div>

      <Tabs
        options={[
          { label: 'Broadcasts', value: 'broadcasts' },
          { label: 'Sequences',  value: 'sequences'  },
          { label: 'Templates',  value: 'templates'  },
          { label: 'Analytics',  value: 'analytics'  },
        ]}
        value={tab}
        onChange={v => { setTab(v); setCreating(false); setEditingSeq(null); }}
      />

      {/* Resend configuration status banner — only shows if the proxy can't
          reach Resend. The project already has RESEND_API_KEY set as a
          Supabase secret (used by send-transactional-email, send-team-invite,
          resend-lead-notifications and other existing functions), so the
          resend-proxy edge function inherits the same key. If this banner
          shows up, deploy `resend-proxy` and confirm Supabase secrets. */}
      {audiences.loaded && !audiences.configured && (
        <div style={{ background: '#FFFBEB', border: '1px solid #FCD34D', color: '#92400E', padding: '8px 12px', borderRadius: 10, fontSize: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
          <Icon name="ShieldAlert" size={14} color="#92400E" />
          <span><strong>resend-proxy can't reach Resend.</strong> Deploy the <code>resend-proxy</code> edge function — it reads the same <code>RESEND_API_KEY</code> Supabase secret your other email functions already use.</span>
        </div>
      )}

      {/* BROADCASTS */}
      {tab === 'broadcasts' && !creating && (
        <div style={NL_CARD}>
          <div style={{ fontSize: 13, fontWeight: 700, color: '#1A2B3B', marginBottom: 14 }}>One-off Broadcasts</div>
          <NLCampaignList campaigns={broadcasts} loading={loading} onPreview={handlePreview} onSend={handleSend} onDelete={handleDelete} onBulkDelete={handleBulkDeleteCampaigns} />
        </div>
      )}
      {tab === 'broadcasts' && creating && (
        <div style={NL_CARD}>
          <div style={{ marginBottom: 14 }}><Btn variant="ghost" onClick={() => setCreating(false)}><Icon name="ChevronRight" size={13} color="#6B7280" style={{ transform: 'rotate(180deg)' }} /> Back to broadcasts</Btn></div>
          <NLBuilder onSave={handleSaveBroadcast} saving={saving} audiences={audiences} />
        </div>
      )}

      {/* SEQUENCES */}
      {tab === 'sequences' && !editingSeq && (
        <div style={NL_CARD}>
          <div style={{ fontSize: 13, fontWeight: 700, color: '#1A2B3B', marginBottom: 14 }}>Automated Email Sequences</div>
          <SequenceList sequences={sequences} onEdit={setEditingSeq} onDelete={handleDeleteSequence} onBulkDelete={handleBulkDeleteSequences} />
        </div>
      )}
      {tab === 'sequences' && editingSeq && (
        <div style={NL_CARD}>
          <SequenceBuilder
            initial={editingSeq && editingSeq.id ? editingSeq : null}
            onSave={handleSaveSequence}
            onCancel={() => setEditingSeq(null)}
            onDeploy={handleDeploySequence}
            deploying={deploying}
          />
        </div>
      )}

      {/* TEMPLATES */}
      {tab === 'templates' && (
        <div style={NL_CARD}>
          <div style={{ fontSize: 13, fontWeight: 700, color: '#1A2B3B', marginBottom: 14 }}>Email Templates</div>
          <TemplatesList templates={templates} onPreview={handlePreviewTemplate} onDelete={handleDeleteTemplate} onBulkDelete={handleBulkDeleteTemplates} onBulkDuplicate={handleBulkDuplicateTemplates} />
        </div>
      )}

      {/* ANALYTICS */}
      {tab === 'analytics' && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 12 }} className="g4">
          {[
            { label: 'Broadcasts Sent',   value: broadcasts.filter(c => c.status === 'sent').length, icon: 'Send' },
            { label: 'Draft Broadcasts',  value: broadcasts.filter(c => c.status === 'draft').length, icon: 'Clock' },
            { label: 'Deployed Sequences',value: sequences.filter(s => s.deployed_at).length, icon: 'GitBranch' },
            { label: 'Templates',         value: templates.length, icon: 'FileText' },
            { label: 'Resend Audiences', value: audiences.configured ? (audiences.audiences || []).length : '—', icon: 'Users' },
            { label: 'Sequence Emails',  value: sequences.reduce((s, q) => s + (q.steps || []).filter(x => x.type === 'email').length, 0), icon: 'Mail' },
            { label: 'Total Recipients', value: broadcasts.reduce((s, c) => s + (c.recipientCount || 0), 0) || '—', icon: 'Send' },
            { label: 'Resend Status',    value: audiences.configured ? 'Live' : 'Not configured', icon: 'Power' },
          ].map(s => (
            <div key={s.label} style={NL_CARD} className="metric-card">
              <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 8 }}><Icon name={s.icon} size={20} color="#F97316" /></div>
              <div style={{ fontSize: 11, fontWeight: 700, color: '#9CA3AF', textTransform: 'uppercase', letterSpacing: '0.07em', textAlign: 'center' }}>{s.label}</div>
              <div style={{ fontSize: 24, fontWeight: 900, color: '#1A2B3B', textAlign: 'center', marginTop: 4 }}>{s.value}</div>
            </div>
          ))}
        </div>
      )}

      {preview && <NLEmailPreview html={preview.html} subject={preview.subject} onClose={() => setPreview(null)} />}
    </div>
  );
}

Object.assign(window, { NewsletterPage });
