import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Link, useLocation } from 'react-router-dom'

// ── Constants ────────────────────────────────────────────────────────────────

const REPLY_FROM_OPTIONS = [
  { value: 'Verse by Verse with Nate <hello@versebyversewithnate.us>', label: 'hello@versebyversewithnate.us' },
  { value: 'Verse by Verse with Nate <nate@versebyversewithnate.us>', label: 'nate@versebyversewithnate.us' },
]

// ── Types ────────────────────────────────────────────────────────────────────

interface Attachment {
  id: string
  filename: string
  contentType: string
  size: number
}

interface EmailDeliveryState {
  status: string
  lastEventAt: string | null
  lastEventType: string | null
  resendEmailId: string | null
  error: string | null
}

interface ContactSubmission {
  id: string
  threadId: string
  submittedAt: string
  name: string
  email: string
  message: string
  messageType: 'question' | 'testimony' | 'topic' | 'general'
  subscribe: boolean
  archived?: boolean
  starred?: boolean
  snoozedUntil?: string | null
  source?: 'contact-form' | 'download' | 'inbound-email'
  inboundTo?: string
  htmlBody?: string | null
  messageId?: string
  attachments?: Attachment[]
  emailStatus?: {
    welcome: EmailDeliveryState
    adminNotification: EmailDeliveryState
    adminReply: EmailDeliveryState
  }
}

interface Thread {
  threadId: string
  messages: ContactSubmission[]  // all inbound messages in thread
  sentMessages: ReplyHistoryItem[] // outbound replies in thread
  latest: ContactSubmission
  subject: string
  email: string
  name: string
}

interface ReplyDraft {
  submissionId: string | null
  threadId: string | null
  recipientName: string
  recipientEmail: string
  subject: string
  message: string
  fromAddress: string
  scheduledAt: string   // '' = send now
}

interface ReplyTemplate {
  id: string
  label: string
  subject: string
  message: string
}

interface ReplyHistoryItem {
  id: string
  submissionId: string
  toEmail: string
  toName: string
  fromEmail: string
  subject: string
  preview: string
  sentAt: string
  scheduledAt?: string | null
}

interface ReplyConfig {
  fromEmail: string
  fromIdentity: string
  resendApiConfigured: boolean
  canSendReplies: boolean
  note: string
}

interface EmailSettings {
  signature: string
}

// ── Helpers ──────────────────────────────────────────────────────────────────

function extractSubject(message: string): string {
  const m = /^Subject:\s*(.+)/m.exec(message ?? '')
  return m ? m[1].trim() : ''
}

function extractBodyPreview(message: string): string {
  return message.replace(/^Subject:\s*.+\n+/m, '').trim().slice(0, 200)
}

function normalizeSubject(s: string): string {
  return s.replace(/^(re|fwd?):\s*/i, '').trim().toLowerCase()
}

function formatDate(value: string | null | undefined): string {
  if (!value) return '—'
  const d = new Date(value)
  return Number.isNaN(d.getTime()) ? '—' : d.toLocaleString()
}

function formatShortDate(value: string | null | undefined): string {
  if (!value) return '—'
  const d = new Date(value)
  if (Number.isNaN(d.getTime())) return '—'
  const now = new Date()
  if (d.toDateString() === now.toDateString()) return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
  if (d.getFullYear() === now.getFullYear()) return d.toLocaleDateString([], { month: 'short', day: 'numeric' })
  return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' })
}

function decodeHtmlBody(raw: string): string {
  if (!raw || raw.trimStart().startsWith('<')) return raw
  try { return atob(raw.replace(/\s+/g, '')) } catch { return raw }
}

function formatBytes(n: number): string {
  if (n < 1024) return `${n} B`
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`
  return `${(n / (1024 * 1024)).toFixed(1)} MB`
}

function readIds(): Set<string> {
  try { return new Set(JSON.parse(localStorage.getItem('em-read-ids') ?? '[]') as string[]) }
  catch { return new Set() }
}

function persistReadId(id: string) {
  try {
    const next = [...readIds(), id].slice(-1000)
    localStorage.setItem('em-read-ids', JSON.stringify(next))
  } catch {}
}

function snoozeQuickDate(option: 'tomorrow' | 'next-week' | 'next-month'): string {
  const d = new Date()
  if (option === 'tomorrow') { d.setDate(d.getDate() + 1); d.setHours(9, 0, 0, 0) }
  else if (option === 'next-week') { d.setDate(d.getDate() + 7); d.setHours(9, 0, 0, 0) }
  else { d.setMonth(d.getMonth() + 1); d.setDate(1); d.setHours(9, 0, 0, 0) }
  return d.toISOString()
}

function scheduleQuickDate(option: 'tomorrow-9' | 'next-monday-9'): string {
  const d = new Date()
  if (option === 'tomorrow-9') {
    d.setDate(d.getDate() + 1); d.setHours(9, 0, 0, 0)
  } else {
    const day = d.getDay()
    const daysUntilMonday = day === 0 ? 1 : 8 - day
    d.setDate(d.getDate() + daysUntilMonday); d.setHours(9, 0, 0, 0)
  }
  // Format for datetime-local input value
  return d.toISOString().slice(0, 16)
}

// ── Thread builder ────────────────────────────────────────────────────────────

function buildThreads(submissions: ContactSubmission[], history: ReplyHistoryItem[]): Map<string, Thread> {
  const map = new Map<string, Thread>()

  for (const s of submissions) {
    const tid = s.threadId ?? s.id
    const existing = map.get(tid)
    if (existing) {
      existing.messages.push(s)
      if (new Date(s.submittedAt) > new Date(existing.latest.submittedAt)) existing.latest = s
    } else {
      const subject = s.source === 'inbound-email' ? extractSubject(s.message) : ''
      map.set(tid, {
        threadId: tid,
        messages: [s],
        sentMessages: [],
        latest: s,
        subject,
        email: s.email,
        name: s.name,
      })
    }
  }

  // Attach sent replies to threads
  for (const item of history) {
    if (!item.submissionId) continue
    for (const [, thread] of map) {
      if (thread.messages.some(m => m.id === item.submissionId)) {
        thread.sentMessages.push(item)
        break
      }
    }
  }

  return map
}

// ── EmailClient ──────────────────────────────────────────────────────────────

type Mailbox = 'inbox' | 'starred' | 'snoozed' | 'archived'

function EmailClient({ onLogout }: { onLogout: () => void }) {
  const location = useLocation()
  const [submissions, setSubmissions] = useState<ContactSubmission[]>([])
  const [loadStatus, setLoadStatus] = useState<'loading' | 'ready' | 'error'>('loading')
  const [mailbox, setMailbox] = useState<Mailbox>('inbox')
  const [selectedThreadId, setSelectedThreadId] = useState<string | null>(null)
  const [search, setSearch] = useState('')
  const [localReadIds, setLocalReadIds] = useState<Set<string>>(readIds)
  const [replyDraft, setReplyDraft] = useState<ReplyDraft | null>(null)
  const [replySending, setReplySending] = useState(false)
  const [replyMsg, setReplyMsg] = useState('')
  const [templates, setTemplates] = useState<ReplyTemplate[]>([])
  const [history, setHistory] = useState<ReplyHistoryItem[]>([])
  const [config, setConfig] = useState<ReplyConfig | null>(null)
  const [templateStatus, setTemplateStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle')
  const [showTemplatesMgr, setShowTemplatesMgr] = useState(false)
  const [showHistory, setShowHistory] = useState(false)
  const [showSettings, setShowSettings] = useState(false)
  const [showBroadcasts, setShowBroadcasts] = useState(false)
  const [showAuditLog, setShowAuditLog] = useState(false)
  // Broadcasts
  const [broadcastSubject, setBroadcastSubject] = useState('')
  const [broadcastPreview, setBroadcastPreview] = useState('')
  const [broadcastBody, setBroadcastBody] = useState('')
  const [broadcastBusy, setBroadcastBusy] = useState(false)
  const [broadcastMsg, setBroadcastMsg] = useState('')
  // Audit log
  const [auditEntries, setAuditEntries] = useState<{ id: string; action: string; details: string; at: string }[]>([])
  // Webhooks
  const [showWebhooks, setShowWebhooks] = useState(false)
  const [webhooks, setWebhooks] = useState<{ id: string; url: string; label: string; events: string[]; createdAt: string }[]>([])
  const [wNewUrl, setWNewUrl] = useState('')
  const [wNewLabel, setWNewLabel] = useState('')
  const [wAddBusy, setWAddBusy] = useState(false)
  const [wAddMsg, setWAddMsg] = useState('')
  const [wTestMsg, setWTestMsg] = useState<Record<string, string>>({})
  const [emailSettings, setEmailSettings] = useState<EmailSettings>({ signature: 'Grace and peace,\nVerse by Verse with Nate' })
  const [settingsSig, setSettingsSig] = useState('')
  const [settingsSaving, setSettingsSaving] = useState(false)
  const [settingsSaved, setSettingsSaved] = useState(false)
  const [actionMsg, setActionMsg] = useState('')
  const [showContactPicker, setShowContactPicker] = useState(false)
  const [contactPickerSearch, setContactPickerSearch] = useState('')
  // Bulk selection
  const [selectMode, setSelectMode] = useState(false)
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
  // Snooze popover
  const [snoozeTargetId, setSnoozeTargetId] = useState<string | null>(null)
  const [snoozeCustom, setSnoozeCustom] = useState('')
  // Schedule send
  const [showSchedule, setShowSchedule] = useState(false)
  const [scheduleCustom, setScheduleCustom] = useState('')
  // AI draft
  const [draftBusy, setDraftBusy] = useState(false)
  const [draftMsg, setDraftMsg] = useState('')

  const composeRef = useRef<HTMLTextAreaElement>(null)
  const listRef = useRef<HTMLElement>(null)
  const now = new Date()

  // ── Load ──

  const loadAll = useCallback(async () => {
    try {
      const [subRes, tplRes, histRes, cfgRes, settingsRes] = await Promise.all([
        fetch('/api/admin-contact-submissions'),
        fetch('/api/admin-contact-reply-templates'),
        fetch('/api/admin-contact-reply-history'),
        fetch('/api/admin-reply-config'),
        fetch('/api/admin-email-settings'),
      ])
      if (!subRes.ok) throw new Error('submissions failed')
      const subData = await subRes.json() as { submissions?: ContactSubmission[] }
      setSubmissions(subData.submissions ?? [])
      setLoadStatus('ready')
      if (tplRes.ok) {
        const d = await tplRes.json() as { templates?: ReplyTemplate[] }
        setTemplates(d.templates ?? [])
      }
      if (histRes.ok) {
        const d = await histRes.json() as { items?: ReplyHistoryItem[] }
        setHistory(d.items ?? [])
      }
      if (cfgRes.ok) setConfig(await cfgRes.json() as ReplyConfig)
      if (settingsRes.ok) {
        const s = await settingsRes.json() as EmailSettings
        setEmailSettings(s)
      }
    } catch {
      setLoadStatus('error')
    }
  }, [])

  useEffect(() => { loadAll() }, [loadAll])

  // Pre-filled compose from Calendar "Announce" button
  useEffect(() => {
    const s = location.state as { compose?: boolean; subject?: string; body?: string } | null
    if (!s?.compose) return
    setReplyDraft({
      submissionId: null,
      threadId: null,
      recipientName: '',
      recipientEmail: '',
      subject: s.subject ?? '',
      message: s.body ?? '',
      fromAddress: REPLY_FROM_OPTIONS[0].value,
      scheduledAt: '',
    })
    window.history.replaceState({}, '', window.location.pathname)
  }, [location.state])

  useEffect(() => {
    const id = setInterval(async () => {
      try {
        const r = await fetch('/api/admin-contact-submissions')
        if (!r.ok) return
        const data = await r.json() as { submissions?: ContactSubmission[] }
        const fresh = data.submissions ?? []
        setSubmissions(prev => {
          const freshById = new Map(fresh.map(s => [s.id, s]))
          const existingIds = new Set(prev.map(s => s.id))
          const merged = prev.map(s => freshById.get(s.id) ?? s)
          const newOnes = fresh.filter(s => !existingIds.has(s.id))
          return newOnes.length > 0 ? [...newOnes, ...merged] : merged
        })
      } catch {}
    }, 30_000)
    return () => clearInterval(id)
  }, [])

  // ── Thread computation ──

  const allThreads = useMemo(() => buildThreads(submissions, history), [submissions, history])

  // Snooze check: messages whose snoozedUntil has passed get re-shown in inbox
  const effectiveSubmissions = useMemo(() => {
    const nowMs = now.getTime()
    return submissions.map(s => {
      if (s.snoozedUntil && new Date(s.snoozedUntil).getTime() <= nowMs) {
        return { ...s, snoozedUntil: null }
      }
      return s
    })
  }, [submissions, now])

  const filteredThreadIds = useMemo(() => {
    // Re-build threads from effective submissions for filtering
    const threads = buildThreads(effectiveSubmissions, history)
    const q = search.trim().toLowerCase()

    return [...threads.values()].filter(thread => {
      const latest = thread.latest
      const msgs = thread.messages

      if (mailbox === 'inbox') {
        if (msgs.some(m => m.archived === true)) return false
        if (msgs.some(m => m.snoozedUntil && new Date(m.snoozedUntil) > now)) return false
      } else if (mailbox === 'starred') {
        if (!msgs.some(m => m.starred === true)) return false
        if (msgs.every(m => m.archived === true)) return false
      } else if (mailbox === 'snoozed') {
        if (!msgs.some(m => m.snoozedUntil && new Date(m.snoozedUntil) > now)) return false
      } else if (mailbox === 'archived') {
        if (msgs.every(m => m.archived !== true)) return false
      }

      if (!q) return true
      const subj = thread.subject.toLowerCase()
      return (
        latest.name.toLowerCase().includes(q) ||
        latest.email.toLowerCase().includes(q) ||
        subj.includes(q) ||
        msgs.some(m => m.message.toLowerCase().includes(q))
      )
    }).sort((a, b) => new Date(b.latest.submittedAt).getTime() - new Date(a.latest.submittedAt).getTime())
  }, [effectiveSubmissions, history, mailbox, search, now])

  const selectedThread = filteredThreadIds.find(t => t.threadId === selectedThreadId) ?? null

  // Get the full thread for the selected (including all messages)
  const selectedFullThread = selectedThread
    ? allThreads.get(selectedThread.threadId) ?? selectedThread
    : null

  const inboxCount = useMemo(() => {
    const threads = buildThreads(effectiveSubmissions, history)
    return [...threads.values()].filter(t =>
      t.messages.every(m => m.archived !== true) &&
      t.messages.every(m => !m.snoozedUntil || new Date(m.snoozedUntil) <= now)
    ).length
  }, [effectiveSubmissions, history, now])

  const unreadCount = useMemo(() => {
    const threads = buildThreads(effectiveSubmissions, history)
    return [...threads.values()].filter(t =>
      t.messages.every(m => m.archived !== true) &&
      t.messages.every(m => !m.snoozedUntil || new Date(m.snoozedUntil) <= now) &&
      t.messages.some(m => !localReadIds.has(m.id))
    ).length
  }, [effectiveSubmissions, history, localReadIds, now])

  // Auto-select first on mailbox switch
  useEffect(() => {
    if (filteredThreadIds.length === 0) { setSelectedThreadId(null); return }
    if (!selectedThreadId || !filteredThreadIds.some(t => t.threadId === selectedThreadId)) {
      setSelectedThreadId(filteredThreadIds[0].threadId)
    }
  }, [filteredThreadIds, selectedThreadId])

  // Mark as read when thread selected
  useEffect(() => {
    if (!selectedFullThread) return
    const unread = selectedFullThread.messages.filter(m => !localReadIds.has(m.id))
    if (unread.length === 0) return
    const newSet = new Set(localReadIds)
    for (const m of unread) { persistReadId(m.id); newSet.add(m.id) }
    setLocalReadIds(newSet)
  }, [selectedFullThread])

  useEffect(() => {
    if (!selectedThreadId || !listRef.current) return
    const el = listRef.current.querySelector(`[data-tid="${selectedThreadId}"]`) as HTMLElement | null
    el?.scrollIntoView({ block: 'nearest' })
  }, [selectedThreadId])

  const hadDraftRef = useRef(false)
  useEffect(() => {
    const hasDraft = Boolean(replyDraft)
    if (hasDraft && !hadDraftRef.current) setTimeout(() => composeRef.current?.focus(), 50)
    hadDraftRef.current = hasDraft
  }, [replyDraft])

  // ── Keyboard nav ──

  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      const tag = (e.target as Element).tagName.toLowerCase()
      if (tag === 'input' || tag === 'textarea' || tag === 'select') return
      const idx = filteredThreadIds.findIndex(t => t.threadId === selectedThreadId)
      if (e.key === 'ArrowDown' || e.key === 'j') {
        e.preventDefault()
        if (idx < filteredThreadIds.length - 1) setSelectedThreadId(filteredThreadIds[idx + 1].threadId)
      } else if (e.key === 'ArrowUp' || e.key === 'k') {
        e.preventDefault()
        if (idx > 0) setSelectedThreadId(filteredThreadIds[idx - 1].threadId)
      } else if (e.key === 'r' && selectedThread && !replyDraft) {
        openReply(selectedThread.latest)
      } else if (e.key === 'Escape' && replyDraft) {
        setReplyDraft(null)
      } else if (e.key === 'e' && selectedThread) {
        const isArchived = selectedThread.messages.some(m => m.archived)
        handleBulkAction([...selectedThread.messages.map(m => m.id)], isArchived ? 'unarchive' : 'archive')
      } else if (e.key === 's' && selectedThread) {
        const isStarred = selectedThread.messages.some(m => m.starred)
        handleBulkAction([...selectedThread.messages.map(m => m.id)], isStarred ? 'unstar' : 'star')
      }
    }
    document.addEventListener('keydown', onKey)
    return () => document.removeEventListener('keydown', onKey)
  }, [filteredThreadIds, selectedThreadId, selectedThread, replyDraft])

  // ── Actions ──

  async function handlePatch(id: string, patch: Record<string, unknown>) {
    const res = await fetch(`/api/admin-contact-submissions/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(patch),
    })
    if (!res.ok) { flash('Failed to update.'); return false }
    setSubmissions(prev => prev.map(s => s.id === id ? { ...s, ...patch } as ContactSubmission : s))
    return true
  }

  async function handleBulkAction(ids: string[], action: 'archive' | 'unarchive' | 'delete' | 'star' | 'unstar') {
    if (action === 'delete' && !confirm(`Delete ${ids.length} message${ids.length !== 1 ? 's' : ''} permanently?`)) return
    const res = await fetch('/api/admin-contact-submissions/bulk', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ ids, action }),
    })
    if (!res.ok) { flash('Failed to apply action.'); return }
    setSubmissions(prev => {
      const idSet = new Set(ids)
      if (action === 'delete') return prev.filter(s => !idSet.has(s.id))
      const patch = action === 'archive' ? { archived: true }
        : action === 'unarchive' ? { archived: false }
        : action === 'star' ? { starred: true }
        : { starred: false }
      return prev.map(s => idSet.has(s.id) ? { ...s, ...patch } : s)
    })
    setSelectedIds(new Set())
    if (action === 'delete') flash(`Deleted ${ids.length} message${ids.length !== 1 ? 's' : ''}.`)
    else if (action === 'archive') flash(`Archived ${ids.length} message${ids.length !== 1 ? 's' : ''}.`)
    else if (action === 'unarchive') flash(`Moved ${ids.length} to inbox.`)
    else if (action === 'star') flash(`Starred ${ids.length} message${ids.length !== 1 ? 's' : ''}.`)
    else flash(`Unstarred ${ids.length} message${ids.length !== 1 ? 's' : ''}.`)
  }

  async function handleSnooze(id: string, snoozedUntil: string | null) {
    await handlePatch(id, { snoozedUntil })
    setSnoozeTargetId(null)
    flash(snoozedUntil ? `Snoozed until ${formatDate(snoozedUntil)}.` : 'Snooze cancelled.')
  }

  async function handleDelete(id: string) {
    if (!confirm('Delete this message permanently?')) return
    const res = await fetch(`/api/admin-contact-submissions/${encodeURIComponent(id)}`, { method: 'DELETE' })
    if (!res.ok) { flash('Failed to delete.'); return }
    setSubmissions(prev => prev.filter(s => s.id !== id))
    flash('Message deleted.')
  }

  function flash(msg: string) {
    setActionMsg(msg)
    setTimeout(() => setActionMsg(''), 3000)
  }

  async function handleBroadcast(e: React.FormEvent) {
    e.preventDefault(); setBroadcastBusy(true); setBroadcastMsg('')
    try {
      const res = await fetch('/api/admin-broadcasts/send', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ subject: broadcastSubject, previewText: broadcastPreview, text: broadcastBody, from: REPLY_FROM_OPTIONS[0].value }),
      })
      const d = await res.json() as { ok?: boolean; broadcastId?: string; message?: string }
      if (!res.ok) { setBroadcastMsg(d.message ?? 'Broadcast failed.'); setBroadcastBusy(false); return }
      setBroadcastMsg(`Broadcast sent! (id: ${d.broadcastId ?? '?'})`)
      setBroadcastSubject(''); setBroadcastPreview(''); setBroadcastBody('')
    } catch { setBroadcastMsg('Network error.') }
    setBroadcastBusy(false)
  }

  async function loadAuditLog() {
    try {
      const res = await fetch('/api/admin-audit-log', { credentials: 'include' })
      if (res.ok) {
        const d = await res.json() as { entries: { id: string; action: string; details: string; at: string }[] }
        setAuditEntries(d.entries ?? [])
      }
    } catch { /* silent */ }
  }

  async function loadWebhooks() {
    try {
      const res = await fetch('/api/admin-webhooks', { credentials: 'include' })
      if (res.ok) {
        const d = await res.json() as { webhooks: typeof webhooks }
        setWebhooks(d.webhooks ?? [])
      }
    } catch { /* silent */ }
  }

  async function handleAddWebhook(e: React.FormEvent) {
    e.preventDefault()
    setWAddBusy(true); setWAddMsg('')
    try {
      const res = await fetch('/api/admin-webhooks', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url: wNewUrl, label: wNewLabel, events: ['*'] }),
      })
      const d = await res.json() as { ok?: boolean; webhook?: typeof webhooks[0]; message?: string }
      if (!res.ok) { setWAddMsg(d.message ?? 'Failed to add webhook.'); setWAddBusy(false); return }
      setWebhooks(prev => [...prev, d.webhook!])
      setWNewUrl(''); setWNewLabel('')
      setWAddMsg('Webhook added.')
    } catch { setWAddMsg('Network error.') }
    setWAddBusy(false)
  }

  async function deleteWebhook(id: string) {
    try {
      await fetch(`/api/admin-webhooks/${encodeURIComponent(id)}`, { method: 'DELETE', credentials: 'include' })
      setWebhooks(prev => prev.filter(w => w.id !== id))
    } catch { /* silent */ }
  }

  async function testWebhook(id: string) {
    setWTestMsg(prev => ({ ...prev, [id]: 'Testing…' }))
    try {
      const res = await fetch(`/api/admin-webhooks/${encodeURIComponent(id)}/test`, { method: 'POST', credentials: 'include' })
      const d = await res.json() as { ok?: boolean; status?: number; message?: string }
      setWTestMsg(prev => ({ ...prev, [id]: res.ok ? `✓ ${d.status}` : (d.message ?? 'Failed') }))
    } catch { setWTestMsg(prev => ({ ...prev, [id]: 'Network error.' })) }
    setTimeout(() => setWTestMsg(prev => { const n = { ...prev }; delete n[id]; return n }), 4000)
  }

  async function handleAIDraft(submissionId: string) {
    setDraftBusy(true); setDraftMsg('')
    try {
      const res = await fetch(`/api/admin-contact-submissions/${encodeURIComponent(submissionId)}/draft-reply`, {
        method: 'POST', credentials: 'include',
      })
      const d = await res.json() as { ok?: boolean; draft?: string; message?: string }
      if (!res.ok) { setDraftMsg(d.message ?? 'Draft failed.'); setDraftBusy(false); return }
      if (d.draft) setReplyDraft(prev => prev ? { ...prev, message: d.draft! } : prev)
    } catch { setDraftMsg('Network error.') }
    setDraftBusy(false)
  }

  function openReply(submission: ContactSubmission) {
    const firstName = submission.name?.trim().split(/\s+/)[0] || 'there'
    const subject = extractSubject(submission.message)
    const inboundTo = submission.inboundTo ?? ''
    const defaultFrom = inboundTo.includes('nate@') ? REPLY_FROM_OPTIONS[1].value : REPLY_FROM_OPTIONS[0].value
    setReplyDraft({
      submissionId: submission.id,
      threadId: submission.threadId,
      recipientName: submission.name,
      recipientEmail: submission.email,
      subject: subject ? `Re: ${normalizeSubject(subject) ? subject : subject}` : 'Re: Your message',
      message: '',
      fromAddress: defaultFrom,
      scheduledAt: '',
    })
    setReplyMsg('')
    setShowSchedule(false)
    setScheduleCustom('')
    flash(`Composing reply to ${firstName}…`)
  }

  function openCompose() {
    setSelectedThreadId(null)
    setReplyDraft({
      submissionId: null,
      threadId: null,
      recipientName: '',
      recipientEmail: '',
      subject: '',
      message: '',
      fromAddress: REPLY_FROM_OPTIONS[0].value,
      scheduledAt: '',
    })
    setReplyMsg('')
    setShowSchedule(false)
    setScheduleCustom('')
  }

  function applyTemplate(templateId: string) {
    if (!replyDraft) return
    const tpl = templates.find(t => t.id === templateId)
    if (!tpl) return
    setReplyDraft({ ...replyDraft, message: tpl.message })
  }

  async function handleSend(overrideScheduledAt?: string) {
    if (!replyDraft) return
    setReplySending(true)
    setReplyMsg('')

    const scheduledAt = overrideScheduledAt ?? replyDraft.scheduledAt ?? ''

    try {
      if (replyDraft.submissionId) {
        const res = await fetch(`/api/admin-contact-submissions/${encodeURIComponent(replyDraft.submissionId)}/reply`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            subject: replyDraft.subject,
            message: replyDraft.message,
            fromAddress: replyDraft.fromAddress,
            scheduledAt: scheduledAt || null,
          }),
        })
        if (!res.ok) {
          const d = await res.json().catch(() => ({})) as { message?: string }
          throw new Error(d.message ?? 'Failed to send.')
        }
        const result = await res.json() as { scheduled?: boolean; scheduledAt?: string | null }
        if (result.scheduled && result.scheduledAt) {
          setReplyMsg(`Scheduled for ${formatDate(result.scheduledAt)}.`)
        } else {
          setReplyMsg(`Sent to ${replyDraft.recipientEmail}.`)
        }
      } else {
        const res = await fetch('/api/admin-email/compose', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            to: replyDraft.recipientEmail,
            toName: replyDraft.recipientName,
            subject: replyDraft.subject,
            message: replyDraft.message,
            fromAddress: replyDraft.fromAddress,
            scheduledAt: scheduledAt || null,
          }),
        })
        if (!res.ok) {
          const d = await res.json().catch(() => ({})) as { message?: string }
          throw new Error(d.message ?? 'Failed to send.')
        }
        const result = await res.json() as { scheduled?: boolean; scheduledAt?: string | null }
        if (result.scheduled && result.scheduledAt) {
          setReplyMsg(`Scheduled for ${formatDate(result.scheduledAt)}.`)
        } else {
          setReplyMsg(`Sent to ${replyDraft.recipientEmail}.`)
        }
      }

      setReplyDraft(null)
      setShowSchedule(false)
      fetch('/api/admin-contact-reply-history')
        .then(r => r.ok ? r.json() : null)
        .then(d => { if (d) setHistory((d as { items?: ReplyHistoryItem[] }).items ?? []) })
        .catch(() => {})
    } catch (err) {
      setReplyMsg(err instanceof Error ? err.message : 'Failed to send.')
    } finally {
      setReplySending(false)
    }
  }

  async function handleSaveTemplates() {
    setTemplateStatus('saving')
    try {
      const res = await fetch('/api/admin-contact-reply-templates', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ templates }),
      })
      if (!res.ok) throw new Error('Failed to save')
      setTemplateStatus('saved')
    } catch {
      setTemplateStatus('error')
    }
  }

  // ── Bulk selection helpers ──

  function toggleSelectAll() {
    if (selectedIds.size === filteredThreadIds.length) {
      setSelectedIds(new Set())
    } else {
      setSelectedIds(new Set(filteredThreadIds.map(t => t.threadId)))
    }
  }

  function toggleSelectThread(tid: string) {
    setSelectedIds(prev => {
      const n = new Set(prev)
      if (n.has(tid)) n.delete(tid)
      else n.add(tid)
      return n
    })
  }

  function getSelectedMessageIds(): string[] {
    return filteredThreadIds
      .filter(t => selectedIds.has(t.threadId))
      .flatMap(t => t.messages.map(m => m.id))
  }

  // ── Build sorted thread messages for detail view ──

  function getConversationItems(thread: Thread): Array<{ type: 'inbound'; msg: ContactSubmission } | { type: 'sent'; item: ReplyHistoryItem }> {
    const inbound = thread.messages.map(m => ({ type: 'inbound' as const, msg: m, date: new Date(m.submittedAt).getTime() }))
    const sent = thread.sentMessages.map(i => ({ type: 'sent' as const, item: i, date: new Date(i.sentAt).getTime() }))
    return [...inbound, ...sent].sort((a, b) => a.date - b.date)
  }

  // ── Render ──

  const allSelectedMessageIds = getSelectedMessageIds()

  return (
    <div className="em-app">
      {/* Header */}
      <header className="em-header">
        <div className="em-header-left">
          <span className="em-header-brand">✉ Email Center</span>
          {unreadCount > 0 && <span className="em-badge em-badge--alert">{unreadCount} new</span>}
        </div>
        <div className="em-header-actions">
          <button type="button" className="em-btn em-btn--primary" onClick={openCompose}>Compose</button>
          <button type="button" className="em-btn em-btn--ghost" onClick={() => { setShowBroadcasts(p => !p); setBroadcastMsg('') }}>📢 Broadcast</button>
          <button type="button" className="em-btn em-btn--ghost" onClick={() => { setShowAuditLog(p => !p); if (!showAuditLog) loadAuditLog() }}>📋 Audit</button>
          <button type="button" className="em-btn em-btn--ghost" onClick={() => { setShowWebhooks(p => !p); if (!showWebhooks) loadWebhooks() }}>⚡ Webhooks</button>
          <Link to="/contacts" className="em-btn em-btn--ghost">Contacts</Link>
          <Link to="/calendar" className="em-btn em-btn--ghost">Calendar</Link>
          <Link to="/admin" className="em-btn em-btn--ghost">← Admin</Link>
          <button type="button" className="em-btn em-btn--ghost" onClick={onLogout}>Log Out</button>
        </div>
      </header>

      {/* Bulk action bar */}
      {selectMode && selectedIds.size > 0 && (
        <div className="em-bulk-bar">
          <span className="em-bulk-count">{selectedIds.size} selected</span>
          <button type="button" className="em-btn em-btn--sm em-btn--ghost" onClick={() => handleBulkAction(allSelectedMessageIds, 'archive')}>Archive</button>
          <button type="button" className="em-btn em-btn--sm em-btn--ghost" onClick={() => handleBulkAction(allSelectedMessageIds, 'star')}>Star</button>
          <button type="button" className="em-btn em-btn--sm em-btn--danger" onClick={() => handleBulkAction(allSelectedMessageIds, 'delete')}>Delete</button>
          <button type="button" className="em-btn em-btn--sm em-btn--ghost" onClick={() => { setSelectedIds(new Set()); setSelectMode(false) }}>Cancel</button>
        </div>
      )}

      {/* Body */}
      <div className="em-body">
        {/* Sidebar */}
        <aside className="em-sidebar">
          <div className="em-search-wrap">
            <input className="em-search" type="search" placeholder="Search…" value={search} onChange={e => setSearch(e.target.value)} />
          </div>

          {/* Mailbox tabs */}
          <div className="em-mailbox-tabs">
            {([['inbox', 'Inbox'], ['starred', '★ Starred'], ['snoozed', 'Snoozed'], ['archived', 'Archived']] as [Mailbox, string][]).map(([id, label]) => (
              <button
                key={id}
                type="button"
                className={`em-mailbox-tab${mailbox === id ? ' em-mailbox-tab--active' : ''}`}
                onClick={() => { setMailbox(id); setSearch(''); setSelectMode(false); setSelectedIds(new Set()) }}
              >
                {label}
                {id === 'inbox' && inboxCount > 0 && (
                  <span className={`em-tab-badge${unreadCount > 0 ? ' em-tab-badge--unread' : ''}`}>{inboxCount}</span>
                )}
              </button>
            ))}
          </div>

          {/* Select mode toggle */}
          <div className="em-list-toolbar">
            <button type="button" className={`em-btn em-btn--sm ${selectMode ? 'em-btn--secondary' : 'em-btn--ghost'}`} onClick={() => { setSelectMode(p => !p); setSelectedIds(new Set()) }}>
              {selectMode ? 'Done' : 'Select'}
            </button>
            {selectMode && (
              <button type="button" className="em-btn em-btn--sm em-btn--ghost" onClick={toggleSelectAll}>
                {selectedIds.size === filteredThreadIds.length ? 'Deselect all' : 'Select all'}
              </button>
            )}
          </div>

          {/* Thread list */}
          <section className="em-list" ref={listRef} aria-label="Messages">
            {loadStatus === 'loading' && <p className="em-list-empty">Loading…</p>}
            {loadStatus === 'error' && <p className="em-list-empty">Could not load messages.</p>}
            {loadStatus === 'ready' && filteredThreadIds.length === 0 && (
              <p className="em-list-empty">{search ? 'No results.' : 'No messages here.'}</p>
            )}
            {filteredThreadIds.map(thread => {
              const { threadId, latest, messages, subject } = thread
              const isActive = selectedThreadId === threadId
              const isUnread = messages.some(m => !localReadIds.has(m.id))
              const isStarred = messages.some(m => m.starred)
              const isSnoozed = messages.some(m => m.snoozedUntil && new Date(m.snoozedUntil) > now)
              const preview = latest.source === 'inbound-email' ? extractBodyPreview(latest.message) : latest.message
              const isSelected = selectedIds.has(threadId)

              return (
                <button
                  key={threadId}
                  data-tid={threadId}
                  type="button"
                  className={`em-list-item${isActive && !selectMode ? ' em-list-item--active' : ''}${isUnread ? ' em-list-item--unread' : ''}${isSelected ? ' em-list-item--selected' : ''}`}
                  onClick={() => {
                    if (selectMode) { toggleSelectThread(threadId); return }
                    setSelectedThreadId(threadId)
                  }}
                >
                  {selectMode
                    ? <input type="checkbox" className="em-list-check" readOnly checked={isSelected} aria-label="Select" onClick={e => e.stopPropagation()} onChange={() => toggleSelectThread(threadId)} />
                    : isUnread && <span className="em-unread-dot" aria-label="Unread" />
                  }
                  <div className="em-list-item-head">
                    <strong className="em-list-item-name">
                      {isStarred && <span className="em-star-icon">★</span>}
                      {latest.name}
                      {messages.length > 1 && <span className="em-thread-count">{messages.length}</span>}
                    </strong>
                    <span className="em-list-item-date">
                      {isSnoozed ? '💤' : ''}{formatShortDate(latest.submittedAt)}
                    </span>
                  </div>
                  {subject && <div className="em-list-item-subject">{subject}</div>}
                  <div className="em-list-item-preview">{preview}</div>
                  {latest.source === 'inbound-email' && (
                    <span className="em-source-badge em-source-badge--inbound">
                      {latest.inboundTo?.includes('nate@') ? 'nate@' : 'hello@'}
                    </span>
                  )}
                </button>
              )
            })}
          </section>
        </aside>

        {/* Detail pane */}
        <main className="em-detail">
          {replyDraft && !replyDraft.submissionId ? (
            /* ── Compose New ── */
            <div className="em-compose-new">
              <div className="em-detail-header">
                <h2 className="em-detail-subject">New Message</h2>
              </div>
              <div className="em-compose-fields">
                <div className="em-compose-row">
                  <label className="em-compose-label">To</label>
                  <div className="em-to-field-wrap">
                    <input
                      className="em-compose-input"
                      type="text"
                      placeholder="recipient@example.com"
                      value={replyDraft.recipientEmail}
                      onChange={e => setReplyDraft({ ...replyDraft, recipientEmail: e.target.value })}
                    />
                    <button type="button" className="em-contact-pick-btn" title="Pick from contacts" onClick={() => { setShowContactPicker(p => !p); setContactPickerSearch('') }}>
                      👥
                    </button>
                  </div>
                  {showContactPicker && (() => {
                    const q = contactPickerSearch.toLowerCase()
                    const seen = new Set<string>()
                    const opts = submissions
                      .filter(s => s.email && !seen.has(s.email) && seen.add(s.email) as unknown as boolean)
                      .filter(s => !q || s.email.toLowerCase().includes(q) || (s.name || '').toLowerCase().includes(q))
                      .slice(0, 40)
                    return (
                      <div className="em-contact-picker">
                        <input className="em-contact-picker-search" type="text" placeholder="Search contacts…" autoFocus value={contactPickerSearch} onChange={e => setContactPickerSearch(e.target.value)} />
                        <div className="em-contact-picker-list">
                          {opts.length === 0 && <div className="em-contact-picker-empty">No contacts found</div>}
                          {opts.map(s => (
                            <button key={s.id} type="button" className="em-contact-picker-item"
                              onClick={() => { setReplyDraft({ ...replyDraft, recipientEmail: s.email, recipientName: s.name || replyDraft.recipientName }); setShowContactPicker(false) }}>
                              <span className="em-contact-picker-name">{s.name || <em>No name</em>}</span>
                              <span className="em-contact-picker-email">{s.email}</span>
                            </button>
                          ))}
                        </div>
                      </div>
                    )
                  })()}
                </div>
                <div className="em-compose-row">
                  <label className="em-compose-label">Name</label>
                  <input className="em-compose-input" type="text" placeholder="Recipient name (optional)" value={replyDraft.recipientName} onChange={e => setReplyDraft({ ...replyDraft, recipientName: e.target.value })} />
                </div>
                <div className="em-compose-row">
                  <label className="em-compose-label">From</label>
                  <select className="em-compose-select" value={replyDraft.fromAddress} onChange={e => setReplyDraft({ ...replyDraft, fromAddress: e.target.value })}>
                    {REPLY_FROM_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                  </select>
                </div>
                <div className="em-compose-row">
                  <label className="em-compose-label">Subject</label>
                  <input className="em-compose-input" type="text" value={replyDraft.subject} onChange={e => setReplyDraft({ ...replyDraft, subject: e.target.value })} />
                </div>
                {templates.length > 0 && (
                  <div className="em-compose-row">
                    <label className="em-compose-label">Template</label>
                    <select className="em-compose-select" defaultValue="" onChange={e => applyTemplate(e.target.value)}>
                      <option value="">Choose a template…</option>
                      {templates.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                    </select>
                  </div>
                )}
                <textarea ref={composeRef} className="em-compose-body" rows={12} placeholder="Type your message…" value={replyDraft.message} onChange={e => setReplyDraft({ ...replyDraft, message: e.target.value })} />
                <div className="em-compose-signature">
                  <span className="em-compose-signature-text">{emailSettings.signature}</span>
                  <span className="em-compose-signature-note">auto-appended · <button type="button" className="em-sig-edit-link" onClick={() => { setSettingsSig(emailSettings.signature); setShowSettings(true) }}>edit signature</button></span>
                </div>
                <div className="em-compose-actions">
                  <button type="button" className="em-btn em-btn--primary" onClick={() => handleSend()} disabled={replySending}>
                    {replySending ? 'Sending…' : 'Send'}
                  </button>
                  <div className="em-schedule-wrap">
                    <button type="button" className="em-btn em-btn--secondary" onClick={() => setShowSchedule(p => !p)}>
                      Schedule ▾
                    </button>
                    {showSchedule && (
                      <div className="em-schedule-picker">
                        <button type="button" className="em-schedule-option" onClick={() => handleSend(new Date(scheduleQuickDate('tomorrow-9')).toISOString())}>Tomorrow 9 AM</button>
                        <button type="button" className="em-schedule-option" onClick={() => handleSend(new Date(scheduleQuickDate('next-monday-9')).toISOString())}>Next Monday 9 AM</button>
                        <div className="em-schedule-custom">
                          <input type="datetime-local" className="em-compose-input" value={scheduleCustom} onChange={e => setScheduleCustom(e.target.value)} min={new Date().toISOString().slice(0, 16)} />
                          <button type="button" className="em-btn em-btn--primary em-btn--sm" disabled={!scheduleCustom} onClick={() => scheduleCustom && handleSend(new Date(scheduleCustom).toISOString())}>Schedule</button>
                        </div>
                      </div>
                    )}
                  </div>
                  <button type="button" className="em-btn em-btn--ghost" onClick={() => { setReplyDraft(null); setShowContactPicker(false); setShowSchedule(false) }}>Discard</button>
                </div>
                {replyMsg && <p className="em-status-msg">{replyMsg}</p>}
              </div>
            </div>

          ) : !selectedFullThread ? (
            /* ── Empty state ── */
            <div className="em-empty-state">
              <div className="em-empty-icon">✉</div>
              <p>Select a message to read it.</p>
              <p className="em-empty-hint">↑ ↓ or j / k to navigate · r to reply · e to archive · s to star</p>
            </div>

          ) : (
            /* ── Thread detail ── */
            <>
              <div className="em-detail-header">
                <div className="em-detail-meta-row">
                  <div className="em-detail-from">
                    <div className="em-detail-avatar">{(selectedFullThread.name || '?')[0].toUpperCase()}</div>
                    <div>
                      <div className="em-detail-from-name">{selectedFullThread.name}</div>
                      <div className="em-detail-from-email">{selectedFullThread.email}</div>
                    </div>
                  </div>
                  <div className="em-detail-meta-right">
                    {selectedFullThread.subject && <div className="em-detail-subject-line">{selectedFullThread.subject}</div>}
                    {selectedFullThread.messages.length > 1 && (
                      <span className="em-thread-badge">{selectedFullThread.messages.length + selectedFullThread.sentMessages.length} messages</span>
                    )}
                  </div>
                </div>
              </div>

              {/* Conversation */}
              <div className="em-conversation">
                {getConversationItems(selectedFullThread).map((item, i) => {
                  if (item.type === 'sent') {
                    const deliveryStatus = submissions.find(s => s.id === item.item.submissionId)?.emailStatus?.adminReply?.status
                    return (
                      <div key={item.item.id} className={`em-conv-bubble em-conv-bubble--sent${i === 0 ? ' em-conv-bubble--first' : ''}`}>
                        <div className="em-conv-meta">
                          <span className="em-conv-author">You → {item.item.toEmail}</span>
                          <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                            {deliveryStatus && deliveryStatus !== 'idle' && (
                              <span className={`em-delivery-pill em-delivery-pill--${deliveryStatus}`}>
                                {deliveryStatus === 'clicked' ? '🔗 clicked' : deliveryStatus === 'opened' ? '👁 opened' : deliveryStatus === 'delivered' ? '✓ delivered' : deliveryStatus === 'bounced' ? '⚠ bounced' : deliveryStatus === 'sent' ? '→ sent' : deliveryStatus}
                              </span>
                            )}
                            <span className="em-conv-date">
                              {item.item.scheduledAt ? `Scheduled: ${formatDate(item.item.scheduledAt)}` : formatDate(item.item.sentAt)}
                            </span>
                          </div>
                        </div>
                        <div className="em-conv-subject">{item.item.subject}</div>
                        <pre className="em-detail-plain em-conv-body">{item.item.preview}</pre>
                      </div>
                    )
                  }

                  const msg = item.msg
                  return (
                    <div key={msg.id} className={`em-conv-bubble em-conv-bubble--inbound${i === 0 ? ' em-conv-bubble--first' : ''}`}>
                      <div className="em-conv-meta">
                        <span className="em-conv-author">{msg.name}</span>
                        <div className="em-conv-meta-right">
                          {msg.source === 'inbound-email' && msg.inboundTo && (
                            <span className="em-detail-to">to: {msg.inboundTo}</span>
                          )}
                          <span className="em-conv-date">{formatDate(msg.submittedAt)}</span>
                        </div>
                      </div>
                      <div className="em-conv-body-wrap">
                        {msg.htmlBody ? (
                          <iframe
                            srcDoc={decodeHtmlBody(msg.htmlBody!)}
                            sandbox="allow-same-origin"
                            className="em-detail-iframe"
                            onLoad={e => {
                              const f = e.currentTarget
                              const h = f.contentDocument?.documentElement?.scrollHeight
                              if (h) f.style.height = `${h + 24}px`
                            }}
                          />
                        ) : (
                          <pre className="em-detail-plain em-conv-body">{
                            msg.source === 'inbound-email'
                              ? msg.message.replace(/^Subject:\s*.+\n+/m, '').trim()
                              : msg.message
                          }</pre>
                        )}
                        {/* Attachments */}
                        {(msg.attachments ?? []).length > 0 && (
                          <div className="em-attachments">
                            {(msg.attachments ?? []).map(att => (
                              <a
                                key={att.id}
                                href={`/api/admin-contact-submissions/${encodeURIComponent(msg.id)}/attachments/${encodeURIComponent(att.id)}`}
                                download={att.filename}
                                className="em-attachment-chip"
                                target="_blank"
                                rel="noreferrer"
                              >
                                <span className="em-attachment-icon">📎</span>
                                <span className="em-attachment-name">{att.filename}</span>
                                <span className="em-attachment-size">{formatBytes(att.size)}</span>
                              </a>
                            ))}
                          </div>
                        )}
                      </div>
                    </div>
                  )
                })}
              </div>

              {/* Actions */}
              <div className="em-detail-footer">
                <div className="em-detail-actions">
                  <button type="button" className="em-btn em-btn--primary" onClick={() => openReply(selectedFullThread.latest)}>
                    Reply
                  </button>
                  <button
                    type="button"
                    className="em-btn em-btn--secondary"
                    onClick={() => {
                      const ids = selectedFullThread.messages.map(m => m.id)
                      const isArchived = selectedFullThread.messages.some(m => m.archived)
                      handleBulkAction(ids, isArchived ? 'unarchive' : 'archive')
                    }}
                  >
                    {selectedFullThread.messages.some(m => m.archived) ? 'Move to Inbox' : 'Archive'}
                  </button>
                  <button
                    type="button"
                    className={`em-btn ${selectedFullThread.messages.some(m => m.starred) ? 'em-btn--star-active' : 'em-btn--ghost'}`}
                    onClick={() => {
                      const ids = selectedFullThread.messages.map(m => m.id)
                      const isStarred = selectedFullThread.messages.some(m => m.starred)
                      handleBulkAction(ids, isStarred ? 'unstar' : 'star')
                    }}
                  >
                    {selectedFullThread.messages.some(m => m.starred) ? '★ Starred' : '☆ Star'}
                  </button>
                  <div className="em-snooze-wrap">
                    <button
                      type="button"
                      className="em-btn em-btn--ghost"
                      onClick={() => setSnoozeTargetId(prev => prev === selectedFullThread.latest.id ? null : selectedFullThread.latest.id)}
                    >
                      💤 Snooze
                    </button>
                    {snoozeTargetId === selectedFullThread.latest.id && (
                      <div className="em-snooze-picker">
                        <button type="button" className="em-snooze-option" onClick={() => handleSnooze(selectedFullThread.latest.id, snoozeQuickDate('tomorrow'))}>Tomorrow 9 AM</button>
                        <button type="button" className="em-snooze-option" onClick={() => handleSnooze(selectedFullThread.latest.id, snoozeQuickDate('next-week'))}>Next week</button>
                        <button type="button" className="em-snooze-option" onClick={() => handleSnooze(selectedFullThread.latest.id, snoozeQuickDate('next-month'))}>Next month</button>
                        <div className="em-snooze-custom">
                          <input type="date" className="em-compose-input" value={snoozeCustom} min={new Date().toISOString().split('T')[0]} onChange={e => setSnoozeCustom(e.target.value)} />
                          <button type="button" className="em-btn em-btn--sm em-btn--primary" disabled={!snoozeCustom} onClick={() => snoozeCustom && handleSnooze(selectedFullThread.latest.id, new Date(snoozeCustom + 'T09:00:00').toISOString())}>Snooze</button>
                        </div>
                        {selectedFullThread.latest.snoozedUntil && (
                          <button type="button" className="em-snooze-option em-snooze-option--cancel" onClick={() => handleSnooze(selectedFullThread.latest.id, null)}>Cancel snooze</button>
                        )}
                      </div>
                    )}
                  </div>
                  <button type="button" className="em-btn em-btn--danger" onClick={() => handleDelete(selectedFullThread.latest.id)}>
                    Delete
                  </button>
                </div>
                {actionMsg && !replyDraft && <p className="em-status-msg">{actionMsg}</p>}
              </div>

              {/* Inline reply */}
              {replyDraft && replyDraft.threadId === selectedFullThread.threadId && replyDraft.submissionId && (
                <div className="em-reply-composer">
                  <div className="em-compose-header">
                    <span className="em-compose-header-label">Reply to {replyDraft.recipientName}</span>
                    <button type="button" className="em-compose-close" onClick={() => { setReplyDraft(null); setShowSchedule(false) }} aria-label="Close">×</button>
                  </div>
                  <div className="em-compose-fields">
                    <div className="em-compose-row em-compose-row--inline">
                      <label className="em-compose-label">From</label>
                      <select className="em-compose-select" value={replyDraft.fromAddress} onChange={e => setReplyDraft({ ...replyDraft, fromAddress: e.target.value })}>
                        {REPLY_FROM_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                      </select>
                      {templates.length > 0 && (
                        <>
                          <label className="em-compose-label">Template</label>
                          <select className="em-compose-select" defaultValue="" onChange={e => applyTemplate(e.target.value)}>
                            <option value="">Choose…</option>
                            {templates.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                          </select>
                        </>
                      )}
                    </div>
                    <div className="em-compose-row">
                      <label className="em-compose-label">Subject</label>
                      <input className="em-compose-input" type="text" value={replyDraft.subject} onChange={e => setReplyDraft({ ...replyDraft, subject: e.target.value })} />
                    </div>
                    <textarea ref={composeRef} className="em-compose-body" rows={7} placeholder="Type your reply…" value={replyDraft.message} onChange={e => setReplyDraft({ ...replyDraft, message: e.target.value })} />
                    <div className="em-compose-signature">Grace and peace, · Verse by Verse with Nate <span className="em-compose-signature-note">auto-appended</span></div>
                    <div className="em-compose-actions">
                      <button type="button" className="em-btn em-btn--primary" onClick={() => handleSend()} disabled={replySending}>
                        {replySending ? 'Sending…' : 'Send Reply'}
                      </button>
                      <div className="em-schedule-wrap">
                        <button type="button" className="em-btn em-btn--secondary" onClick={() => setShowSchedule(p => !p)}>
                          Schedule ▾
                        </button>
                        {showSchedule && (
                          <div className="em-schedule-picker">
                            <button type="button" className="em-schedule-option" onClick={() => handleSend(new Date(scheduleQuickDate('tomorrow-9')).toISOString())}>Tomorrow 9 AM</button>
                            <button type="button" className="em-schedule-option" onClick={() => handleSend(new Date(scheduleQuickDate('next-monday-9')).toISOString())}>Next Monday 9 AM</button>
                            <div className="em-schedule-custom">
                              <input type="datetime-local" className="em-compose-input" value={scheduleCustom} onChange={e => setScheduleCustom(e.target.value)} min={new Date().toISOString().slice(0, 16)} />
                              <button type="button" className="em-btn em-btn--primary em-btn--sm" disabled={!scheduleCustom} onClick={() => scheduleCustom && handleSend(new Date(scheduleCustom).toISOString())}>Schedule</button>
                            </div>
                          </div>
                        )}
                      </div>
                      <button type="button" className="em-btn em-btn--ghost" onClick={() => { setReplyDraft(null); setShowSchedule(false) }}>Cancel</button>
                      {replyDraft.submissionId && (
                        <button type="button" className="em-btn em-btn--ghost" onClick={() => handleAIDraft(replyDraft.submissionId!)} disabled={draftBusy}>
                          {draftBusy ? 'Drafting…' : '✦ AI Draft'}
                        </button>
                      )}
                      {(replyMsg || draftMsg) && <span className="em-status-msg em-status-msg--inline">{replyMsg || draftMsg}</span>}
                    </div>
                  </div>
                </div>
              )}
            </>
          )}
        </main>
      </div>

      {/* Templates drawer */}
      {showTemplatesMgr && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowTemplatesMgr(false) }}>
          <div className="em-drawer">
            <div className="em-drawer-header">
              <h3>Reply Templates</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowTemplatesMgr(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              {templates.length === 0 && <p className="em-list-empty">No templates yet.</p>}
              {templates.map((tpl, i) => (
                <div key={tpl.id} className="em-template-card">
                  <input className="em-compose-input" placeholder="Template label" value={tpl.label} onChange={e => setTemplates(prev => prev.map((t, j) => j === i ? { ...t, label: e.target.value } : t))} />
                  <input className="em-compose-input" placeholder="Subject (optional)" value={tpl.subject} onChange={e => setTemplates(prev => prev.map((t, j) => j === i ? { ...t, subject: e.target.value } : t))} />
                  <textarea className="em-compose-body" rows={4} placeholder="Message body" value={tpl.message} onChange={e => setTemplates(prev => prev.map((t, j) => j === i ? { ...t, message: e.target.value } : t))} />
                  <button type="button" className="em-btn em-btn--danger em-btn--sm" onClick={() => setTemplates(prev => prev.filter((_, j) => j !== i))}>Remove</button>
                </div>
              ))}
              <div className="em-drawer-actions">
                <button type="button" className="em-btn em-btn--ghost" onClick={() => setTemplates(prev => [...prev, { id: Date.now().toString(36), label: '', subject: '', message: '' }])}>Add Template</button>
                <button type="button" className="em-btn em-btn--primary" onClick={handleSaveTemplates} disabled={templateStatus === 'saving'}>
                  {templateStatus === 'saving' ? 'Saving…' : templateStatus === 'saved' ? 'Saved ✓' : 'Save Templates'}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Sent history drawer */}
      {showHistory && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowHistory(false) }}>
          <div className="em-drawer em-drawer--wide">
            <div className="em-drawer-header">
              <h3>Reply History</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowHistory(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              {history.length === 0 ? (
                <p className="em-list-empty">No replies sent yet.</p>
              ) : (
                <div className="em-history-list">
                  {history.map(item => (
                    <div key={item.id} className="em-history-item">
                      <div className="em-history-meta">
                        <span className="em-history-date">
                          {item.scheduledAt ? `Scheduled: ${formatDate(item.scheduledAt)}` : formatDate(item.sentAt)}
                        </span>
                        <span className="em-history-flow">from {item.fromEmail} → {item.toName} ({item.toEmail})</span>
                      </div>
                      <div className="em-history-subject">{item.subject}</div>
                      <div className="em-history-preview">{item.preview}</div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>
      )}

      {/* Settings drawer */}
      {showSettings && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowSettings(false) }}>
          <div className="em-drawer">
            <div className="em-drawer-header">
              <h3>Email Settings</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowSettings(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              <div className="em-settings-section">
                <label className="em-settings-label">
                  Outgoing Signature
                  <p className="em-settings-note">Appended to every reply and compose email you send.</p>
                  <textarea className="em-compose-body em-settings-sig-textarea" rows={4} value={settingsSig} onChange={e => { setSettingsSig(e.target.value); setSettingsSaved(false) }} placeholder="Grace and peace,&#10;Verse by Verse with Nate" />
                </label>
              </div>
              {config && (
                <div className="em-settings-section">
                  <p className="em-settings-label">
                    Email Configuration
                    <span className={`em-settings-status${config.canSendReplies ? ' em-settings-status--ok' : ' em-settings-status--warn'}`}>
                      {config.canSendReplies ? 'Configured' : 'Not configured'}
                    </span>
                  </p>
                  <p className="em-settings-note">{config.note}</p>
                  {config.fromEmail && <p className="em-settings-note">Reply-to: {config.fromEmail}</p>}
                </div>
              )}
            </div>
            <div className="em-drawer-actions">
              <button type="button" className="em-btn em-btn--primary" disabled={settingsSaving}
                onClick={async () => {
                  setSettingsSaving(true)
                  try {
                    const res = await fetch('/api/admin-email-settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ signature: settingsSig }) })
                    if (res.ok) {
                      const data = await res.json() as { settings?: EmailSettings }
                      if (data.settings) setEmailSettings(data.settings)
                      setSettingsSaved(true)
                    }
                  } catch { /* silent */ }
                  setSettingsSaving(false)
                }}
              >
                {settingsSaving ? 'Saving…' : settingsSaved ? 'Saved ✓' : 'Save Settings'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Broadcasts drawer */}
      {showBroadcasts && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowBroadcasts(false) }}>
          <div className="em-drawer em-drawer--wide">
            <div className="em-drawer-header">
              <h3>📢 Send Broadcast</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowBroadcasts(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              <p className="em-settings-note">Sends to all subscribers in your Resend audience via the Resend Broadcasts API. Resend handles unsubscribes and compliance.</p>
              <form className="em-broadcast-form" onSubmit={handleBroadcast}>
                <label className="em-compose-label">Subject *<input className="em-compose-input" type="text" required placeholder="New episode: …" value={broadcastSubject} onChange={e => setBroadcastSubject(e.target.value)} /></label>
                <label className="em-compose-label">Preview text<input className="em-compose-input" type="text" placeholder="Short preview shown in inbox (optional)" value={broadcastPreview} onChange={e => setBroadcastPreview(e.target.value)} /></label>
                <label className="em-compose-label">
                  Body *
                  <textarea className="em-compose-body em-broadcast-body" required rows={10} placeholder="Write your newsletter in plain text. Double line-break = new paragraph." value={broadcastBody} onChange={e => setBroadcastBody(e.target.value)} />
                </label>
                {broadcastMsg && <p className={`em-status-msg${broadcastMsg.startsWith('Broadcast sent') ? ' em-status-msg--ok' : ''}`}>{broadcastMsg}</p>}
                <div className="em-drawer-actions">
                  <button type="submit" className="em-btn em-btn--primary" disabled={broadcastBusy || !broadcastSubject || !broadcastBody}>
                    {broadcastBusy ? 'Sending…' : 'Send Broadcast'}
                  </button>
                  <button type="button" className="em-btn em-btn--ghost" onClick={() => setShowBroadcasts(false)}>Cancel</button>
                </div>
              </form>
            </div>
          </div>
        </div>
      )}

      {/* Audit log drawer */}
      {showAuditLog && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowAuditLog(false) }}>
          <div className="em-drawer em-drawer--wide">
            <div className="em-drawer-header">
              <h3>📋 Activity Log</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowAuditLog(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              {auditEntries.length === 0 && <p className="em-list-empty">No activity recorded yet.</p>}
              <div className="em-audit-list">
                {auditEntries.map(entry => (
                  <div key={entry.id} className="em-audit-item">
                    <span className="em-audit-action">{entry.action}</span>
                    <span className="em-audit-details">{entry.details}</span>
                    <span className="em-audit-date">{formatShortDate(entry.at)}</span>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Webhooks drawer */}
      {showWebhooks && (
        <div className="em-drawer-overlay" onClick={e => { if (e.target === e.currentTarget) setShowWebhooks(false) }}>
          <div className="em-drawer em-drawer--wide">
            <div className="em-drawer-header">
              <h3>⚡ Outbound Webhooks</h3>
              <button type="button" className="em-compose-close" onClick={() => setShowWebhooks(false)}>×</button>
            </div>
            <div className="em-drawer-body">
              <p className="em-settings-note">Webhooks fire on <code>contact.new</code> and <code>reply.sent</code> events. Connect Zapier or any endpoint.</p>
              <div className="em-webhook-list">
                {webhooks.length === 0 && <p className="em-list-empty">No webhooks configured.</p>}
                {webhooks.map(wh => (
                  <div key={wh.id} className="em-webhook-item">
                    <div className="em-webhook-info">
                      {wh.label && <span className="em-webhook-label">{wh.label}</span>}
                      <span className="em-webhook-url">{wh.url}</span>
                      <span className="em-webhook-events">{(wh.events ?? []).join(', ')}</span>
                    </div>
                    <div className="em-webhook-actions">
                      {wTestMsg[wh.id] && <span className="em-webhook-test-msg">{wTestMsg[wh.id]}</span>}
                      <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => testWebhook(wh.id)}>Test</button>
                      <button type="button" className="em-btn em-btn--danger em-btn--sm" onClick={() => deleteWebhook(wh.id)}>Delete</button>
                    </div>
                  </div>
                ))}
              </div>
              <form className="em-webhook-add-form" onSubmit={handleAddWebhook}>
                <h4 className="em-webhook-add-title">Add Webhook</h4>
                <label className="em-compose-label">Label (optional)<input className="em-compose-input" type="text" placeholder="My Zapier hook" value={wNewLabel} onChange={e => setWNewLabel(e.target.value)} /></label>
                <label className="em-compose-label">URL *<input className="em-compose-input" type="url" required placeholder="https://hooks.zapier.com/…" value={wNewUrl} onChange={e => setWNewUrl(e.target.value)} /></label>
                {wAddMsg && <p className={`em-status-msg${wAddMsg.startsWith('Webhook added') ? ' em-status-msg--ok' : ''}`}>{wAddMsg}</p>}
                <div className="em-drawer-actions">
                  <button type="submit" className="em-btn em-btn--primary" disabled={wAddBusy || !wNewUrl}>{wAddBusy ? 'Adding…' : 'Add Webhook'}</button>
                </div>
              </form>
            </div>
          </div>
        </div>
      )}

      {/* Footer toolbar */}
      <div className="em-footer-toolbar">
        {config && !config.canSendReplies && (
          <span className="em-footer-warn">⚠ RESEND_API_KEY not configured — sending is disabled</span>
        )}
        <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => setShowTemplatesMgr(true)}>Templates</button>
        <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => setShowHistory(true)}>Sent History</button>
        <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => { setSettingsSig(emailSettings.signature); setShowSettings(true) }}>Settings</button>
        <span className="em-footer-hint">↑ ↓ navigate · r reply · e archive · s star · Esc close</span>
      </div>
    </div>
  )
}

// ── Auth shell ───────────────────────────────────────────────────────────────

export default function EmailShell() {
  const [authStatus, setAuthStatus] = useState<'checking' | 'authenticated' | 'unauthenticated' | 'misconfigured'>('checking')
  const [password, setPassword] = useState('')
  const [errorMsg, setErrorMsg] = useState('')
  const [submitting, setSubmitting] = useState(false)
  const [totpRequired, setTotpRequired] = useState(false)
  const [pendingToken, setPendingToken] = useState('')
  const [totpCode, setTotpCode] = useState('')

  useEffect(() => {
    fetch('/api/admin-auth/status')
      .then(r => (r.ok ? r.json() : Promise.reject()))
      .then(data => {
        const d = data as { authenticated?: boolean; configured?: boolean }
        if (d.configured === false) { setAuthStatus('misconfigured'); return }
        setAuthStatus(d.authenticated ? 'authenticated' : 'unauthenticated')
      })
      .catch(() => setAuthStatus('unauthenticated'))
  }, [])

  async function handleLogin(e: React.FormEvent) {
    e.preventDefault()
    setSubmitting(true)
    setErrorMsg('')
    try {
      const res = await fetch('/api/admin-auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) })
      const data = await res.json().catch(() => ({})) as { ok?: boolean; totpRequired?: boolean; pendingToken?: string; message?: string }
      if (!res.ok) { setErrorMsg(data.message ?? 'Login failed.'); setSubmitting(false); return }
      if (data.totpRequired && data.pendingToken) { setPendingToken(data.pendingToken); setTotpRequired(true); setPassword(''); setSubmitting(false); return }
      setAuthStatus('authenticated'); setPassword('')
    } catch {
      setErrorMsg('Login failed.')
    } finally {
      setSubmitting(false)
    }
  }

  async function handleTotpVerify(e: React.FormEvent) {
    e.preventDefault()
    setSubmitting(true)
    setErrorMsg('')
    try {
      const res = await fetch('/api/admin-auth/totp-verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pendingToken, code: totpCode }) })
      if (!res.ok) {
        const d = await res.json().catch(() => ({})) as { message?: string }
        setErrorMsg(d.message ?? 'Invalid code.'); setSubmitting(false); return
      }
      setAuthStatus('authenticated'); setTotpCode('')
    } catch {
      setErrorMsg('Verification failed.')
    } finally {
      setSubmitting(false)
    }
  }

  async function handleLogout() {
    try { await fetch('/api/admin-auth/logout', { method: 'POST' }) } finally {
      setAuthStatus('unauthenticated'); setTotpRequired(false); setPendingToken('')
    }
  }

  if (authStatus === 'authenticated') return <EmailClient onLogout={handleLogout} />

  return (
    <main className="admin-auth-page" aria-label="Email sign in">
      <div className="admin-auth-card">
        <p className="eyebrow">Email Center</p>
        <h1>{authStatus === 'misconfigured' ? 'Not Configured' : 'Sign In'}</h1>
        {authStatus === 'checking' && <p className="admin-auth-note">Checking session…</p>}
        {authStatus === 'misconfigured' && <p className="admin-auth-note">Set the ADMIN_PASSWORD environment variable to enable access.</p>}
        {authStatus === 'unauthenticated' && !totpRequired && (
          <form className="admin-auth-form" onSubmit={handleLogin}>
            <label>Password<input type="password" value={password} onChange={e => setPassword(e.target.value)} autoComplete="current-password" required /></label>
            {errorMsg && <p className="admin-auth-error">{errorMsg}</p>}
            <button type="submit" className="btn-primary" disabled={submitting}>{submitting ? 'Signing In…' : 'Sign In'}</button>
            <Link to="/" className="btn-secondary">Back to Site</Link>
          </form>
        )}
        {authStatus === 'unauthenticated' && totpRequired && (
          <form className="admin-auth-form" onSubmit={handleTotpVerify}>
            <p className="admin-auth-note">Enter the 6-digit code from your authenticator app.</p>
            <label>Code<input type="text" inputMode="numeric" value={totpCode} onChange={e => setTotpCode(e.target.value)} autoComplete="one-time-code" placeholder="000000" autoFocus required /></label>
            {errorMsg && <p className="admin-auth-error">{errorMsg}</p>}
            <button type="submit" className="btn-primary" disabled={submitting}>{submitting ? 'Verifying…' : 'Verify'}</button>
            <button type="button" className="btn-secondary" onClick={() => { setTotpRequired(false); setPendingToken(''); setTotpCode(''); setErrorMsg('') }}>Back</button>
          </form>
        )}
      </div>
    </main>
  )
}
