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

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

interface PodcastChecklistTask {
  id: string
  label: string
  required: boolean
}

type ProductionStatus = 'idea' | 'recorded' | 'edited' | 'scheduled' | 'published'

const PROD_STATUS_OPTIONS: { value: ProductionStatus; label: string; color: string }[] = [
  { value: 'idea',      label: 'Idea',      color: '#4a4040' },
  { value: 'recorded',  label: 'Recorded',  color: '#2a3a5a' },
  { value: 'edited',    label: 'Edited',    color: '#3a2a5a' },
  { value: 'scheduled', label: 'Scheduled', color: '#1a3a4a' },
  { value: 'published', label: 'Published', color: '#1a3a1a' },
]

interface PodcastChecklistEpisode {
  id: string
  series: string
  episodeNumber: number | null
  title: string
  datePublished: string
  startTime?: string
  expanded: boolean
  tasks: Record<string, boolean>
  reminderDays?: number
  reminderSentAt?: string
  productionStatus?: ProductionStatus
}

type RecurrenceFreq = 'none' | 'weekly' | 'biweekly' | 'monthly'

interface Recurrence {
  freq: RecurrenceFreq
  until: string | null
}

type CalendarEventType = 'general' | 'recording' | 'social' | 'task'

interface CalendarEvent {
  id: string
  type: CalendarEventType
  title: string
  date: string
  startTime?: string
  notes: string
  completed: boolean
  reminderDays: number
  reminderSentAt?: string
  recurrence?: Recurrence
  createdAt: string
  recurrenceOf?: string   // runtime only: id of base event if this is an expanded instance
}

interface PodcastChecklistData {
  tasks: PodcastChecklistTask[]
  episodes: PodcastChecklistEpisode[]
}

type ViewMode = 'month' | 'week' | 'day'

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

const REMINDER_OPTIONS = [
  { value: '0', label: 'No reminder' },
  { value: '1', label: '1 day before' },
  { value: '2', label: '2 days before' },
  { value: '3', label: '3 days before' },
  { value: '7', label: '1 week before' },
  { value: '14', label: '2 weeks before' },
]

const EVENT_TYPE_OPTIONS: { value: CalendarEventType; label: string; icon: string }[] = [
  { value: 'general',   label: 'General',   icon: '📌' },
  { value: 'recording', label: 'Recording', icon: '🎙️' },
  { value: 'social',    label: 'Social',    icon: '📱' },
  { value: 'task',      label: 'Task',      icon: '✅' },
]

const RECURRENCE_OPTIONS: { value: RecurrenceFreq; label: string }[] = [
  { value: 'none',     label: 'Does not repeat' },
  { value: 'weekly',   label: 'Weekly' },
  { value: 'biweekly', label: 'Every 2 weeks' },
  { value: 'monthly',  label: 'Monthly' },
]

const MONTH_NAMES = [
  'January', 'February', 'March', 'April', 'May', 'June',
  'July', 'August', 'September', 'October', 'November', 'December',
]

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

function toDateKey(date: Date) {
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
}

function getMondayOf(date: Date): Date {
  const d = new Date(date)
  const dow = d.getDay()
  const diff = dow === 0 ? -6 : 1 - dow
  d.setDate(d.getDate() + diff)
  return d
}

function addDays(date: Date, n: number): Date {
  const d = new Date(date)
  d.setDate(d.getDate() + n)
  return d
}

function addMonths(date: Date, n: number): Date {
  const d = new Date(date)
  d.setMonth(d.getMonth() + n)
  return d
}

function formatTimeDisplay(t: string | undefined): string {
  if (!t) return ''
  const [h, m] = t.split(':').map(Number)
  if (isNaN(h)) return ''
  const ampm = h >= 12 ? 'PM' : 'AM'
  const h12 = h % 12 || 12
  return `${h12}:${String(m).padStart(2, '0')} ${ampm}`
}

function expandRecurring(events: CalendarEvent[], firstKey: string, lastKey: string): CalendarEvent[] {
  const result: CalendarEvent[] = []
  for (const ev of events) {
    // always include base event if it falls in range
    if (ev.date >= firstKey && ev.date <= lastKey) result.push(ev)

    if (!ev.recurrence || ev.recurrence.freq === 'none') continue

    const base = new Date(ev.date + 'T12:00:00')
    const untilDate = ev.recurrence.until
      ? new Date(ev.recurrence.until + 'T23:59:59')
      : new Date(base.getFullYear() + 2, base.getMonth(), base.getDate()) // 2-year horizon

    let d = new Date(base)
    for (let i = 0; i < 1000; i++) {
      if (ev.recurrence.freq === 'weekly')   d = addDays(d, 7)
      else if (ev.recurrence.freq === 'biweekly') d = addDays(d, 14)
      else d = new Date(d.getFullYear(), d.getMonth() + 1, d.getDate())

      if (d > untilDate) break
      const dk = toDateKey(d)
      if (dk > lastKey) break      // past visible range
      if (dk === ev.date) continue // skip base date duplicate
      if (dk < firstKey) continue  // before visible range, keep iterating

      // Don't add if it's the same as a date that's already included as a non-recurring event
      result.push({ ...ev, id: `${ev.id}:${dk}`, date: dk, recurrenceOf: ev.id })
    }
  }
  return result
}

// ── Auth Shell ────────────────────────────────────────────────────────────────

export default function CalendarShell() {
  const [authState, setAuthState] = useState<'checking' | 'needs-password' | 'needs-totp' | 'ok'>('checking')
  const [password, setPassword] = useState('')
  const [totp, setTotp] = useState('')
  const [authError, setAuthError] = useState('')
  const [authBusy, setAuthBusy] = useState(false)
  const [pendingToken, setPendingToken] = useState('')

  useEffect(() => {
    fetch('/api/admin-auth/status', { credentials: 'include' })
      .then(r => r.json())
      .then((data: { authenticated?: boolean }) => {
        setAuthState(data.authenticated ? 'ok' : 'needs-password')
      })
      .catch(() => setAuthState('needs-password'))
  }, [])

  async function handleLogin(e: React.FormEvent) {
    e.preventDefault()
    setAuthBusy(true)
    setAuthError('')
    try {
      const res = await fetch('/api/admin-auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ password }),
        credentials: 'include',
      })
      const data = await res.json() as { ok?: boolean; totpRequired?: boolean; pendingToken?: string; message?: string }
      if (!res.ok) { setAuthError(data.message ?? 'Invalid password.'); setAuthBusy(false); return }
      if (data.totpRequired && data.pendingToken) { setPendingToken(data.pendingToken); setAuthState('needs-totp'); setAuthBusy(false); return }
      setAuthState('ok')
    } catch { setAuthError('Login failed.') }
    setAuthBusy(false)
  }

  async function handleTotp(e: React.FormEvent) {
    e.preventDefault()
    setAuthBusy(true)
    setAuthError('')
    try {
      const res = await fetch('/api/admin-auth/totp-verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ pendingToken, code: totp }),
        credentials: 'include',
      })
      const data = await res.json() as { ok?: boolean; message?: string }
      if (!res.ok) { setAuthError(data.message ?? 'Invalid code.'); setAuthBusy(false); return }
      setAuthState('ok')
    } catch { setAuthError('Verification failed.') }
    setAuthBusy(false)
  }

  if (authState === 'checking') return <div className="em-auth-loading">Loading…</div>

  if (authState === 'needs-password') {
    return (
      <div className="em-auth-wrap">
        <form className="em-auth-form" onSubmit={handleLogin}>
          <h1 className="em-auth-title">Release Calendar</h1>
          <label className="em-auth-label">Admin password
            <input type="password" className="em-auth-input" value={password} onChange={e => setPassword(e.target.value)} autoFocus />
          </label>
          {authError && <p className="em-auth-error">{authError}</p>}
          <button type="submit" className="em-btn em-btn--primary" disabled={authBusy}>
            {authBusy ? 'Signing in…' : 'Sign in'}
          </button>
        </form>
      </div>
    )
  }

  if (authState === 'needs-totp') {
    return (
      <div className="em-auth-wrap">
        <form className="em-auth-form" onSubmit={handleTotp}>
          <h1 className="em-auth-title">Two-factor code</h1>
          <label className="em-auth-label">Authenticator code
            <input type="text" className="em-auth-input" inputMode="numeric" pattern="[0-9]*" maxLength={6} value={totp} onChange={e => setTotp(e.target.value)} autoFocus />
          </label>
          {authError && <p className="em-auth-error">{authError}</p>}
          <button type="submit" className="em-btn em-btn--primary" disabled={authBusy}>
            {authBusy ? 'Verifying…' : 'Verify'}
          </button>
        </form>
      </div>
    )
  }

  return <CalendarClient />
}

// ── Calendar Client ───────────────────────────────────────────────────────────

function CalendarClient() {
  const navigate = useNavigate()
  const today = useMemo(() => new Date(), [])
  const [checklist, setChecklist] = useState<PodcastChecklistData | null>(null)
  const [loading, setLoading] = useState(true)
  const [saving, setSaving] = useState(false)
  const [events, setEvents] = useState<CalendarEvent[]>([])

  // View
  const [viewMode, setViewMode] = useState<ViewMode>('month')
  const [viewDate, setViewDate] = useState<Date>(() => new Date())

  // Episode scheduling (click-to-place)
  const [selectedEpisodeId, setSelectedEpisodeId] = useState<string | null>(null)

  // Episode edit popover
  const [editEp, setEditEp] = useState<PodcastChecklistEpisode | null>(null)
  const [editForm, setEditForm] = useState({ series: '', episodeNumber: '', title: '', datePublished: '', startTime: '', reminderDays: '0', productionStatus: '' as ProductionStatus | '' })

  // New episode form
  const [newEpOpen, setNewEpOpen] = useState(false)
  const [newForm, setNewForm] = useState({ series: '', episodeNumber: '', title: '', datePublished: '', startTime: '', reminderDays: '0' })
  const [newBusy, setNewBusy] = useState(false)

  // New event form
  const [newEvOpen, setNewEvOpen] = useState(false)
  const [newEvForm, setNewEvForm] = useState({ type: 'general' as CalendarEventType, title: '', date: '', startTime: '', notes: '', reminderDays: '0', recurrenceFreq: 'none' as RecurrenceFreq, recurrenceUntil: '' })
  const [newEvBusy, setNewEvBusy] = useState(false)

  // Event edit popover
  const [editEv, setEditEv] = useState<CalendarEvent | null>(null)
  const [editEvForm, setEditEvForm] = useState({ type: 'general' as CalendarEventType, title: '', date: '', startTime: '', notes: '', reminderDays: '0', recurrenceFreq: 'none' as RecurrenceFreq, recurrenceUntil: '' })
  const [evSaving, setEvSaving] = useState(false)

  // Drag state
  const [dragOverKey, setDragOverKey] = useState<string | null>(null)
  const [calFlash, setCalFlash] = useState('')
  const dragDataRef = useRef<{ type: 'episode' | 'event'; id: string } | null>(null)

  // ── Load ──

  const load = useCallback(async () => {
    try {
      const [clRes, evRes] = await Promise.all([
        fetch('/api/admin-podcast-checklist', { credentials: 'include' }),
        fetch('/api/admin-calendar-events', { credentials: 'include' }),
      ])
      if (clRes.ok) {
        const data = await clRes.json() as { checklist: PodcastChecklistData }
        setChecklist(data.checklist)
      }
      if (evRes.ok) {
        const data = await evRes.json() as { events: CalendarEvent[] }
        setEvents(Array.isArray(data.events) ? data.events : [])
      }
    } catch { /* silent */ }
    setLoading(false)
  }, [])

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

  // ── Navigation ──

  const viewYear = viewDate.getFullYear()
  const viewMonth = viewDate.getMonth()

  function prevPeriod() {
    if (viewMode === 'month') setViewDate(d => addMonths(d, -1))
    else if (viewMode === 'week') setViewDate(d => addDays(d, -7))
    else setViewDate(d => addDays(d, -1))
  }

  function nextPeriod() {
    if (viewMode === 'month') setViewDate(d => addMonths(d, 1))
    else if (viewMode === 'week') setViewDate(d => addDays(d, 7))
    else setViewDate(d => addDays(d, 1))
  }

  function gotoToday() { setViewDate(new Date(today)) }

  // ── Computed days ──

  const calDays = useMemo(() => {
    if (viewMode === 'week') {
      const monday = getMondayOf(viewDate)
      return Array.from({ length: 7 }, (_, i) => ({ date: addDays(monday, i), inMonth: true }))
    }
    if (viewMode === 'day') {
      return [{ date: new Date(viewDate), inMonth: true }]
    }
    // month
    const first = new Date(viewYear, viewMonth, 1)
    const last = new Date(viewYear, viewMonth + 1, 0)
    const days: Array<{ date: Date; inMonth: boolean }> = []
    const startDow = (first.getDay() + 6) % 7
    for (let i = startDow - 1; i >= 0; i--) {
      days.push({ date: new Date(viewYear, viewMonth, -i), inMonth: false })
    }
    for (let d = 1; d <= last.getDate(); d++) {
      days.push({ date: new Date(viewYear, viewMonth, d), inMonth: true })
    }
    const rem = (7 - (days.length % 7)) % 7
    for (let i = 1; i <= rem; i++) {
      days.push({ date: new Date(viewYear, viewMonth + 1, i), inMonth: false })
    }
    return days
  }, [viewMode, viewDate, viewYear, viewMonth])

  const firstKey = calDays.length > 0 ? toDateKey(calDays[0].date) : ''
  const lastKey = calDays.length > 0 ? toDateKey(calDays[calDays.length - 1].date) : ''

  // ── Expanded events (with recurring instances) ──

  const expandedEventsByDate = useMemo(() => {
    const all = expandRecurring(events, firstKey, lastKey)
    const map = new Map<string, CalendarEvent[]>()
    for (const ev of all) {
      const arr = map.get(ev.date) ?? []
      arr.push(ev)
      map.set(ev.date, arr)
    }
    return map
  }, [events, firstKey, lastKey])

  const episodesByDate = useMemo(() => {
    const map = new Map<string, PodcastChecklistEpisode[]>()
    for (const ep of (checklist?.episodes ?? [])) {
      if (!ep.datePublished?.trim()) continue
      const key = ep.datePublished.trim().slice(0, 10)
      const arr = map.get(key) ?? []
      arr.push(ep)
      map.set(key, arr)
    }
    return map
  }, [checklist])

  const unscheduled = useMemo(
    () => (checklist?.episodes ?? []).filter(ep => !ep.datePublished?.trim()),
    [checklist]
  )

  const todayKey = toDateKey(today)

  // ── View header label ──

  const viewLabel = useMemo(() => {
    if (viewMode === 'month') return `${MONTH_NAMES[viewMonth]} ${viewYear}`
    if (viewMode === 'week') {
      const monday = getMondayOf(viewDate)
      const sunday = addDays(monday, 6)
      if (monday.getMonth() === sunday.getMonth()) {
        return `${MONTH_NAMES[monday.getMonth()]} ${monday.getDate()}–${sunday.getDate()}, ${monday.getFullYear()}`
      }
      return `${MONTH_NAMES[monday.getMonth()]} ${monday.getDate()} – ${MONTH_NAMES[sunday.getMonth()]} ${sunday.getDate()}, ${monday.getFullYear()}`
    }
    return `${MONTH_NAMES[viewDate.getMonth()]} ${viewDate.getDate()}, ${viewDate.getFullYear()}`
  }, [viewMode, viewDate, viewYear, viewMonth])

  // ── Checklist save ──

  async function saveChecklist(episodes: PodcastChecklistEpisode[]) {
    if (!checklist) return
    setSaving(true)
    try {
      const updated = { ...checklist, episodes }
      const res = await fetch('/api/admin-podcast-checklist', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ checklist: updated }),
      })
      if (res.ok) setChecklist(updated)
    } catch { /* silent */ }
    setSaving(false)
  }

  // ── Episode actions ──

  function handleDayClick(date: Date, inMonth: boolean) {
    if (!inMonth || !selectedEpisodeId || !checklist) return
    const dk = toDateKey(date)
    const updated = checklist.episodes.map(ep =>
      ep.id === selectedEpisodeId ? { ...ep, datePublished: dk } : ep
    )
    saveChecklist(updated)
    setSelectedEpisodeId(null)
  }

  function announceEpisode(ep: PodcastChecklistEpisode) {
    const parts = [ep.series, ep.episodeNumber ? `Episode ${ep.episodeNumber}` : null, ep.title].filter(Boolean)
    const subject = `New Episode: ${parts.join(' – ')}`
    const dateStr = ep.datePublished
      ? new Date(ep.datePublished + 'T12:00:00').toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })
      : ''
    const body = [parts.join(' – '), dateStr ? `Published ${dateStr}` : ''].filter(Boolean).join('\n\n')
    setEditEp(null)
    navigate('/email', { state: { compose: true, subject, body } })
  }

  function openEdit(ep: PodcastChecklistEpisode) {
    setEditEp(ep)
    setEditForm({
      series: ep.series,
      episodeNumber: ep.episodeNumber != null ? String(ep.episodeNumber) : '',
      title: ep.title,
      datePublished: ep.datePublished?.trim() ?? '',
      startTime: ep.startTime ?? '',
      reminderDays: String(ep.reminderDays ?? 0),
      productionStatus: ep.productionStatus ?? '',
    })
  }

  function saveEdit() {
    if (!editEp || !checklist) return
    const newDate = editForm.datePublished.trim()
    const newReminder = Number(editForm.reminderDays)
    const dateChanged = newDate !== (editEp.datePublished?.trim() ?? '')
    const reminderChanged = newReminder !== (editEp.reminderDays ?? 0)

    let nextStatus = (editForm.productionStatus as ProductionStatus) || undefined
    if (dateChanged && newDate) {
      const today = new Date().toISOString().slice(0, 10)
      const isPast = newDate <= today
      if (!nextStatus || nextStatus === 'idea') {
        nextStatus = isPast ? 'published' : 'scheduled'
      }
    }

    const updated = checklist.episodes.map(ep =>
      ep.id === editEp.id
        ? {
            ...ep,
            series: editForm.series.trim(),
            episodeNumber: editForm.episodeNumber.trim() ? Number(editForm.episodeNumber) : null,
            title: editForm.title.trim(),
            datePublished: newDate,
            startTime: editForm.startTime || undefined,
            reminderDays: newReminder,
            reminderSentAt: dateChanged || reminderChanged ? undefined : ep.reminderSentAt,
            productionStatus: nextStatus,
          }
        : ep
    )
    saveChecklist(updated)
    setEditEp(null)
  }

  async function generateTasks(ep: PodcastChecklistEpisode) {
    if (!ep.datePublished) return
    const epDate = new Date(ep.datePublished + 'T12:00:00')
    const label = ep.title || [ep.series, ep.episodeNumber != null ? `Ep. ${ep.episodeNumber}` : null].filter(Boolean).join(' ')
    const tasks = [
      { offsetDays: -14, title: `Record: ${label}` },
      { offsetDays: -7, title: `Edit: ${label}` },
    ]
    for (const t of tasks) {
      const d = new Date(epDate)
      d.setDate(d.getDate() + t.offsetDays)
      const dateKey = d.toISOString().slice(0, 10)
      try {
        const res = await fetch('/api/admin-calendar-events', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'include',
          body: JSON.stringify({ type: 'task', title: t.title, date: dateKey, notes: '', reminderDays: 0 }),
        })
        if (res.ok) {
          const data = await res.json() as { event: CalendarEvent }
          setEvents(prev => [data.event, ...prev])
        }
      } catch { /* silent */ }
    }
    setCalFlash(`Tasks generated for "${label}"`)
    setTimeout(() => setCalFlash(''), 3000)
    setEditEp(null)
  }

  function unschedule(ep: PodcastChecklistEpisode) {
    if (!checklist) return
    const updated = checklist.episodes.map(e => e.id === ep.id ? { ...e, datePublished: '' } : e)
    saveChecklist(updated)
    setEditEp(null)
  }

  async function handleNewEpisode(e: React.FormEvent) {
    e.preventDefault()
    if (!checklist) return
    setNewBusy(true)
    const id = typeof crypto?.randomUUID === 'function'
      ? crypto.randomUUID()
      : `ep-${Date.now()}-${Math.random().toString(36).slice(2)}`
    const ep: PodcastChecklistEpisode = {
      id,
      series: newForm.series.trim(),
      episodeNumber: newForm.episodeNumber.trim() ? Number(newForm.episodeNumber) : null,
      title: newForm.title.trim(),
      datePublished: newForm.datePublished.trim(),
      startTime: newForm.startTime || undefined,
      reminderDays: Number(newForm.reminderDays),
      expanded: false,
      tasks: {},
    }
    await saveChecklist([...checklist.episodes, ep])
    setNewForm({ series: '', episodeNumber: '', title: '', datePublished: '', startTime: '', reminderDays: '0' })
    setNewEpOpen(false)
    setNewBusy(false)
  }

  // ── Event actions ──

  async function handleNewEvent(e: React.FormEvent) {
    e.preventDefault()
    if (!newEvForm.title.trim() || !newEvForm.date) return
    setNewEvBusy(true)
    try {
      const recurrence = newEvForm.recurrenceFreq !== 'none'
        ? { freq: newEvForm.recurrenceFreq, until: newEvForm.recurrenceUntil || null }
        : null
      const res = await fetch('/api/admin-calendar-events', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({
          type: newEvForm.type,
          title: newEvForm.title.trim(),
          date: newEvForm.date,
          startTime: newEvForm.startTime || undefined,
          notes: newEvForm.notes.trim(),
          reminderDays: Number(newEvForm.reminderDays),
          recurrence,
        }),
      })
      if (res.ok) {
        const data = await res.json() as { event: CalendarEvent }
        setEvents(prev => [data.event, ...prev])
        setNewEvForm({ type: 'general', title: '', date: '', startTime: '', notes: '', reminderDays: '0', recurrenceFreq: 'none', recurrenceUntil: '' })
        setNewEvOpen(false)
      }
    } catch { /* silent */ }
    setNewEvBusy(false)
  }

  function openEditEvent(ev: CalendarEvent) {
    // If clicking a recurring instance, edit the base event
    const baseId = ev.recurrenceOf ?? ev.id
    const base = events.find(e => e.id === baseId) ?? ev
    setEditEv(base)
    setEditEvForm({
      type: base.type,
      title: base.title,
      date: base.date,
      startTime: base.startTime ?? '',
      notes: base.notes,
      reminderDays: String(base.reminderDays),
      recurrenceFreq: base.recurrence?.freq ?? 'none',
      recurrenceUntil: base.recurrence?.until ?? '',
    })
  }

  async function saveEditEvent() {
    if (!editEv) return
    setEvSaving(true)
    try {
      const recurrence = editEvForm.recurrenceFreq !== 'none'
        ? { freq: editEvForm.recurrenceFreq, until: editEvForm.recurrenceUntil || null }
        : null
      const res = await fetch(`/api/admin-calendar-events/${editEv.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({
          type: editEvForm.type,
          title: editEvForm.title.trim(),
          date: editEvForm.date,
          startTime: editEvForm.startTime || null,
          notes: editEvForm.notes.trim(),
          reminderDays: Number(editEvForm.reminderDays),
          recurrence,
        }),
      })
      if (res.ok) {
        setEvents(prev => prev.map(ev => {
          if (ev.id !== editEv.id) return ev
          const dateChanged = editEvForm.date !== ev.date
          const reminderChanged = Number(editEvForm.reminderDays) !== ev.reminderDays
          return {
            ...ev,
            type: editEvForm.type,
            title: editEvForm.title.trim(),
            date: editEvForm.date,
            startTime: editEvForm.startTime || undefined,
            notes: editEvForm.notes.trim(),
            reminderDays: Number(editEvForm.reminderDays),
            recurrence: recurrence ?? undefined,
            reminderSentAt: (dateChanged || reminderChanged) ? undefined : ev.reminderSentAt,
          }
        }))
        setEditEv(null)
      }
    } catch { /* silent */ }
    setEvSaving(false)
  }

  async function deleteEvent(ev: CalendarEvent) {
    const baseId = ev.recurrenceOf ?? ev.id
    try {
      const res = await fetch(`/api/admin-calendar-events/${baseId}`, { method: 'DELETE', credentials: 'include' })
      if (res.ok) { setEvents(prev => prev.filter(e => e.id !== baseId)); setEditEv(null) }
    } catch { /* silent */ }
  }

  async function toggleComplete(ev: CalendarEvent) {
    const next = !ev.completed
    try {
      const res = await fetch(`/api/admin-calendar-events/${ev.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ completed: next }),
      })
      if (res.ok) setEvents(prev => prev.map(e => e.id === ev.id ? { ...e, completed: next } : e))
    } catch { /* silent */ }
  }

  // ── Drag-to-reschedule ──

  function onChipDragStart(e: React.DragEvent, type: 'episode' | 'event', id: string) {
    dragDataRef.current = { type, id }
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', JSON.stringify({ type, id }))
  }

  async function onDayDrop(e: React.DragEvent, date: Date, inMonth: boolean) {
    e.preventDefault()
    setDragOverKey(null)
    if (!inMonth && viewMode === 'month') return
    const dk = toDateKey(date)
    let raw: { type: string; id: string } | null = null
    try { raw = JSON.parse(e.dataTransfer.getData('text/plain')) } catch {}
    const dragged = raw ?? dragDataRef.current
    dragDataRef.current = null
    if (!dragged) return

    if (dragged.type === 'episode' && checklist) {
      const updated = checklist.episodes.map(ep =>
        ep.id === dragged.id ? { ...ep, datePublished: dk } : ep
      )
      saveChecklist(updated)
    } else if (dragged.type === 'event') {
      // Strip recurrenceOf prefix if this is an instance id (e.g. "baseId:2026-08-01")
      const baseId = dragged.id.includes(':') ? dragged.id.split(':')[0] : dragged.id
      try {
        const res = await fetch(`/api/admin-calendar-events/${baseId}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'include',
          body: JSON.stringify({ date: dk }),
        })
        if (res.ok) setEvents(prev => prev.map(ev => ev.id === baseId ? { ...ev, date: dk } : ev))
      } catch { /* silent */ }
    }
  }

  // ── Render chip ──

  function renderEpChip(ep: PodcastChecklistEpisode, _dk: string) {
    const statusOpt = PROD_STATUS_OPTIONS.find(o => o.value === ep.productionStatus)
    return (
      <button
        key={ep.id}
        type="button"
        className="cal-ep-chip"
        draggable
        onDragStart={e => onChipDragStart(e, 'episode', ep.id)}
        title={[ep.series, ep.episodeNumber ? `Ep ${ep.episodeNumber}` : null, ep.title, ep.startTime ? formatTimeDisplay(ep.startTime) : null, statusOpt ? statusOpt.label : null].filter(Boolean).join(' · ')}
        style={statusOpt ? { borderLeft: `3px solid ${statusOpt.color}` } : undefined}
        onClick={e => { e.stopPropagation(); openEdit(ep) }}
      >
        {ep.episodeNumber ? `Ep ${ep.episodeNumber}` : ep.series?.slice(0, 6) ?? '—'}
        {ep.title && <span className="cal-ep-chip-title"> {ep.title.slice(0, 18)}{ep.title.length > 18 ? '…' : ''}</span>}
        {ep.startTime && <span className="cal-chip-time">{formatTimeDisplay(ep.startTime)}</span>}
        {statusOpt && <span className="cal-prod-status-dot" style={{ background: statusOpt.color }} title={statusOpt.label} />}
        {(ep.reminderDays ?? 0) > 0 && <span className="cal-ep-bell" title={`Reminder: ${ep.reminderDays}d before${ep.reminderSentAt ? ' (sent)' : ''}`}>{ep.reminderSentAt ? '✓' : '🔔'}</span>}
      </button>
    )
  }

  function renderEvChip(ev: CalendarEvent) {
    const opt = EVENT_TYPE_OPTIONS.find(o => o.value === ev.type)
    const isInstance = Boolean(ev.recurrenceOf)
    return (
      <button
        key={ev.id}
        type="button"
        className={`cal-ev-chip cal-ev-chip--${ev.type}${ev.completed ? ' cal-ev-chip--done' : ''}${isInstance ? ' cal-ev-chip--recurring' : ''}`}
        draggable
        onDragStart={e => onChipDragStart(e, 'event', ev.id)}
        title={[ev.title, ev.startTime ? formatTimeDisplay(ev.startTime) : null, ev.notes || null, isInstance ? '(recurring)' : null].filter(Boolean).join(' · ')}
        onClick={e => { e.stopPropagation(); openEditEvent(ev) }}
      >
        <span className="cal-ev-icon">{opt?.icon}</span>
        <span className="cal-ev-title">{ev.title.slice(0, 20)}{ev.title.length > 20 ? '…' : ''}</span>
        {ev.startTime && <span className="cal-chip-time">{formatTimeDisplay(ev.startTime)}</span>}
        {isInstance && <span className="cal-recur-icon" title="Recurring">🔁</span>}
        {(ev.reminderDays ?? 0) > 0 && <span className="cal-ep-bell">{ev.reminderSentAt ? '✓' : '🔔'}</span>}
      </button>
    )
  }

  // ── Day cell renderer ──

  function renderDayCell({ date, inMonth }: { date: Date; inMonth: boolean }, i: number) {
    const dk = toDateKey(date)
    const eps = episodesByDate.get(dk) ?? []
    const evs = expandedEventsByDate.get(dk) ?? []
    const isToday = dk === todayKey
    const isSelecting = Boolean(selectedEpisodeId)
    const isDragOver = dragOverKey === dk

    return (
      <div
        key={`${i}-${dk}`}
        className={[
          'cal-day',
          !inMonth && viewMode === 'month' ? 'cal-day--out' : '',
          isToday ? 'cal-day--today' : '',
          isSelecting && inMonth ? 'cal-day--selectable' : '',
          isDragOver ? 'cal-day--drag-over' : '',
          viewMode === 'week' ? 'cal-day--week' : '',
          viewMode === 'day' ? 'cal-day--day-view' : '',
        ].filter(Boolean).join(' ')}
        onClick={() => handleDayClick(date, inMonth)}
        onDragOver={e => { if (inMonth || viewMode !== 'month') { e.preventDefault(); setDragOverKey(dk) } }}
        onDragLeave={() => setDragOverKey(null)}
        onDrop={e => onDayDrop(e, date, inMonth)}
      >
        <div className="cal-day-head">
          <span
            className={`cal-day-num${isToday ? ' cal-day-num--today' : ''}`}
            onClick={e => { e.stopPropagation(); setViewDate(date); setViewMode('day') }}
            title="Go to day view"
          >
            {viewMode !== 'month' ? `${MONTH_NAMES[date.getMonth()].slice(0,3)} ${date.getDate()}` : date.getDate()}
          </span>
        </div>
        <div className="cal-day-events">
          {eps.map(ep => renderEpChip(ep, dk))}
          {evs.map(ev => renderEvChip(ev))}
        </div>
      </div>
    )
  }

  // ── Main render ──

  return (
    <div className="cal-app">
      {/* Header */}
      <header className="cal-header">
        <div className="cal-header-left">
          <button type="button" className="cal-nav-btn" onClick={prevPeriod} aria-label="Previous">‹</button>
          <h1 className="cal-month-title">{viewLabel}</h1>
          <button type="button" className="cal-nav-btn" onClick={nextPeriod} aria-label="Next">›</button>
          <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={gotoToday}>Today</button>
          {saving && <span className="cal-saving">Saving…</span>}
          {calFlash && <span className="cal-flash-msg">{calFlash}</span>}
        </div>
        <div className="cal-header-center">
          <div className="cal-view-toggle">
            {(['month', 'week', 'day'] as ViewMode[]).map(mode => (
              <button
                key={mode}
                type="button"
                className={`cal-view-btn${viewMode === mode ? ' cal-view-btn--active' : ''}`}
                onClick={() => setViewMode(mode)}
              >
                {mode.charAt(0).toUpperCase() + mode.slice(1)}
              </button>
            ))}
          </div>
        </div>
        <div className="cal-header-right">
          {selectedEpisodeId && (
            <span className="cal-scheduling-hint">
              Click a date to schedule ·{' '}
              <button type="button" className="cal-cancel-link" onClick={() => setSelectedEpisodeId(null)}>cancel</button>
            </span>
          )}
          <button type="button" className="em-btn em-btn--secondary em-btn--sm" onClick={() => { setNewEpOpen(o => !o); setNewEvOpen(false) }}>
            {newEpOpen ? 'Cancel' : '+ Episode'}
          </button>
          <button type="button" className="em-btn em-btn--secondary em-btn--sm" onClick={() => { setNewEvOpen(o => !o); setNewEpOpen(false) }}>
            {newEvOpen ? 'Cancel' : '+ Event'}
          </button>
          <a href="/api/admin-calendar.ics" download="calendar.ics" className="em-btn em-btn--ghost em-btn--sm" title="Export to Apple Calendar / Google Calendar">
            📅 iCal
          </a>
          <Link to="/email" className="em-btn em-btn--ghost em-btn--sm">✉ Email</Link>
          <Link to="/admin" className="em-btn em-btn--ghost em-btn--sm">← Admin</Link>
        </div>
      </header>

      {/* New episode banner */}
      {newEpOpen && (
        <div className="cal-new-ep-banner">
          <form className="cal-new-ep-form" onSubmit={handleNewEpisode}>
            <h3 className="cal-new-ep-title">New Episode</h3>
            <div className="cal-new-ep-row">
              <label className="cal-new-ep-label">Series<input className="ct-input" type="text" placeholder="Colossians" value={newForm.series} onChange={e => setNewForm(f => ({ ...f, series: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Episode #<input className="ct-input" type="number" placeholder="42" value={newForm.episodeNumber} onChange={e => setNewForm(f => ({ ...f, episodeNumber: e.target.value }))} /></label>
              <label className="cal-new-ep-label" style={{ flex: 2 }}>Title<input className="ct-input" type="text" placeholder="Episode title" value={newForm.title} onChange={e => setNewForm(f => ({ ...f, title: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Date<input className="ct-input" type="date" value={newForm.datePublished} onChange={e => setNewForm(f => ({ ...f, datePublished: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Time<input className="ct-input" type="time" value={newForm.startTime} onChange={e => setNewForm(f => ({ ...f, startTime: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Reminder<select className="ct-input" value={newForm.reminderDays} onChange={e => setNewForm(f => ({ ...f, reminderDays: e.target.value }))}>{REMINDER_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}</select></label>
            </div>
            <div className="cal-new-ep-actions">
              <button type="submit" className="em-btn em-btn--primary em-btn--sm" disabled={newBusy}>{newBusy ? 'Adding…' : 'Add Episode'}</button>
            </div>
          </form>
        </div>
      )}

      {/* New event banner */}
      {newEvOpen && (
        <div className="cal-new-ep-banner">
          <form className="cal-new-ep-form" onSubmit={handleNewEvent}>
            <h3 className="cal-new-ep-title">New Event</h3>
            <div className="cal-new-ep-row">
              <label className="cal-new-ep-label">Type<select className="ct-input" value={newEvForm.type} onChange={e => setNewEvForm(f => ({ ...f, type: e.target.value as CalendarEventType }))}>{EVENT_TYPE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.icon} {o.label}</option>)}</select></label>
              <label className="cal-new-ep-label" style={{ flex: 2 }}>Title<input className="ct-input" type="text" required placeholder="Event title" value={newEvForm.title} onChange={e => setNewEvForm(f => ({ ...f, title: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Date<input className="ct-input" type="date" required value={newEvForm.date} onChange={e => setNewEvForm(f => ({ ...f, date: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Time<input className="ct-input" type="time" value={newEvForm.startTime} onChange={e => setNewEvForm(f => ({ ...f, startTime: e.target.value }))} /></label>
              <label className="cal-new-ep-label">Reminder<select className="ct-input" value={newEvForm.reminderDays} onChange={e => setNewEvForm(f => ({ ...f, reminderDays: e.target.value }))}>{REMINDER_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}</select></label>
            </div>
            <div className="cal-new-ep-row">
              <label className="cal-new-ep-label">Repeats<select className="ct-input" value={newEvForm.recurrenceFreq} onChange={e => setNewEvForm(f => ({ ...f, recurrenceFreq: e.target.value as RecurrenceFreq }))}>{RECURRENCE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}</select></label>
              {newEvForm.recurrenceFreq !== 'none' && (
                <label className="cal-new-ep-label">Until (optional)<input className="ct-input" type="date" value={newEvForm.recurrenceUntil} onChange={e => setNewEvForm(f => ({ ...f, recurrenceUntil: e.target.value }))} /></label>
              )}
              <label className="cal-new-ep-label" style={{ flex: 1 }}>Notes<input className="ct-input" type="text" placeholder="Optional notes" value={newEvForm.notes} onChange={e => setNewEvForm(f => ({ ...f, notes: e.target.value }))} /></label>
            </div>
            <div className="cal-new-ep-actions">
              <button type="submit" className="em-btn em-btn--primary em-btn--sm" disabled={newEvBusy}>{newEvBusy ? 'Adding…' : 'Add Event'}</button>
            </div>
          </form>
        </div>
      )}

      {/* Publishing today banner */}
      {!loading && (() => {
        const todayEps = (checklist?.episodes ?? []).filter(ep => ep.datePublished === todayKey)
        if (todayEps.length === 0) return null
        return (
          <div className="cal-publish-banner">
            <span className="cal-publish-banner-icon">📣</span>
            <span className="cal-publish-banner-text">
              {todayEps.length === 1
                ? `"${todayEps[0].title || `Ep ${todayEps[0].episodeNumber}`}" publishes today`
                : `${todayEps.length} episodes publish today`}
            </span>
            {todayEps.map(ep => (
              <button key={ep.id} type="button" className="em-btn em-btn--sm em-btn--secondary" onClick={() => announceEpisode(ep)}>
                ✉ Draft announcement
              </button>
            ))}
          </div>
        )
      })()}

      {/* Calendar body */}
      <div className="cal-body">
        <div className="cal-main">
          {loading ? (
            <p className="cal-loading">Loading calendar…</p>
          ) : (
            <>
              {/* Day-of-week headers */}
              {viewMode !== 'day' && (
                <div className="cal-dow-row">
                  {['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map(d => (
                    <div key={d} className="cal-dow">{d}</div>
                  ))}
                </div>
              )}
              {/* Grid */}
              <div className={`cal-grid${viewMode === 'week' ? ' cal-grid--week' : ''}${viewMode === 'day' ? ' cal-grid--day' : ''}`}>
                {calDays.map((day, i) => renderDayCell(day, i))}
              </div>
            </>
          )}
        </div>

        {/* Sidebar — unscheduled */}
        <aside className="cal-sidebar">
          <h2 className="cal-sidebar-title">
            Unscheduled
            {unscheduled.length > 0 && <span className="cal-sidebar-count">{unscheduled.length}</span>}
          </h2>
          {selectedEpisodeId && <p className="cal-sidebar-hint">Click a date on the calendar to schedule.</p>}
          {unscheduled.length === 0 ? (
            <p className="cal-sidebar-empty">All episodes are scheduled.</p>
          ) : (
            <div className="cal-sidebar-list">
              {unscheduled.map(ep => (
                <div
                  key={ep.id}
                  className={`cal-sidebar-ep${selectedEpisodeId === ep.id ? ' cal-sidebar-ep--selected' : ''}`}
                  draggable
                  onDragStart={e => onChipDragStart(e, 'episode', ep.id)}
                  onClick={() => setSelectedEpisodeId(id => id === ep.id ? null : ep.id)}
                  role="button"
                  tabIndex={0}
                  onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') setSelectedEpisodeId(id => id === ep.id ? null : ep.id) }}
                >
                  <div className="cal-sidebar-ep-header">
                    <span className="cal-sidebar-ep-num">
                      {ep.episodeNumber ? `Ep ${ep.episodeNumber}` : ep.series}
                    </span>
                    <button type="button" className="cal-sidebar-ep-edit" title="Edit" onClick={e => { e.stopPropagation(); openEdit(ep) }}>✎</button>
                  </div>
                  <div className="cal-sidebar-ep-title">{ep.title || <em>Untitled</em>}</div>
                  {ep.series && ep.episodeNumber && <div className="cal-sidebar-ep-series">{ep.series}</div>}
                </div>
              ))}
            </div>
          )}
        </aside>
      </div>

      {/* Episode edit popover */}
      {editEp && (
        <div className="cal-popover-overlay" onClick={() => setEditEp(null)}>
          <div className="cal-popover" onClick={e => e.stopPropagation()}>
            <div className="cal-popover-head">
              <h3>Edit Episode</h3>
              <button type="button" className="cal-popover-close" onClick={() => setEditEp(null)}>×</button>
            </div>
            <div className="cal-popover-body">
              <label className="cal-popover-label">Series<input className="ct-input" type="text" value={editForm.series} onChange={e => setEditForm(f => ({ ...f, series: e.target.value }))} /></label>
              <label className="cal-popover-label">Episode #<input className="ct-input" type="number" value={editForm.episodeNumber} onChange={e => setEditForm(f => ({ ...f, episodeNumber: e.target.value }))} /></label>
              <label className="cal-popover-label">Title<input className="ct-input" type="text" value={editForm.title} onChange={e => setEditForm(f => ({ ...f, title: e.target.value }))} /></label>
              <label className="cal-popover-label">Date Published<input className="ct-input" type="date" value={editForm.datePublished} onChange={e => setEditForm(f => ({ ...f, datePublished: e.target.value }))} /></label>
              <label className="cal-popover-label">Start Time (optional)<input className="ct-input" type="time" value={editForm.startTime} onChange={e => setEditForm(f => ({ ...f, startTime: e.target.value }))} /></label>
              <label className="cal-popover-label">
                Production Status
                <select className="ct-input" value={editForm.productionStatus} onChange={e => setEditForm(f => ({ ...f, productionStatus: e.target.value as ProductionStatus | '' }))}>
                  <option value="">— not set —</option>
                  {PROD_STATUS_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
              </label>
              <label className="cal-popover-label">
                Email Reminder
                <select className="ct-input" value={editForm.reminderDays} onChange={e => setEditForm(f => ({ ...f, reminderDays: e.target.value }))}>
                  {REMINDER_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
                {editEp.reminderSentAt && <span className="cal-reminder-sent">Reminder sent {new Date(editEp.reminderSentAt).toLocaleDateString()}</span>}
              </label>
            </div>
            <div className="cal-popover-actions">
              <button type="button" className="em-btn em-btn--primary em-btn--sm" onClick={saveEdit}>Save</button>
              <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => announceEpisode(editEp)} title="Open email compose pre-filled to announce this episode">✉ Announce</button>
              {editEp.datePublished && <button type="button" className="em-btn em-btn--ghost em-btn--sm" title="Create Record (−14d) and Edit (−7d) task events" onClick={() => generateTasks(editEp)}>⚙ Tasks</button>}
              <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => unschedule(editEp)}>Unschedule</button>
              <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => setEditEp(null)}>Cancel</button>
            </div>
          </div>
        </div>
      )}

      {/* Event edit popover */}
      {editEv && (
        <div className="cal-popover-overlay" onClick={() => setEditEv(null)}>
          <div className="cal-popover" onClick={e => e.stopPropagation()}>
            <div className="cal-popover-head">
              <h3>Edit Event</h3>
              <button type="button" className="cal-popover-close" onClick={() => setEditEv(null)}>×</button>
            </div>
            <div className="cal-popover-body">
              <label className="cal-popover-label">Type<select className="ct-input" value={editEvForm.type} onChange={e => setEditEvForm(f => ({ ...f, type: e.target.value as CalendarEventType }))}>{EVENT_TYPE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.icon} {o.label}</option>)}</select></label>
              <label className="cal-popover-label">Title<input className="ct-input" type="text" value={editEvForm.title} onChange={e => setEditEvForm(f => ({ ...f, title: e.target.value }))} /></label>
              <label className="cal-popover-label">Date<input className="ct-input" type="date" value={editEvForm.date} onChange={e => setEditEvForm(f => ({ ...f, date: e.target.value }))} /></label>
              <label className="cal-popover-label">Start Time (optional)<input className="ct-input" type="time" value={editEvForm.startTime} onChange={e => setEditEvForm(f => ({ ...f, startTime: e.target.value }))} /></label>
              <label className="cal-popover-label">Notes<input className="ct-input" type="text" value={editEvForm.notes} onChange={e => setEditEvForm(f => ({ ...f, notes: e.target.value }))} /></label>
              <label className="cal-popover-label">
                Email Reminder
                <select className="ct-input" value={editEvForm.reminderDays} onChange={e => setEditEvForm(f => ({ ...f, reminderDays: e.target.value }))}>
                  {REMINDER_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
                {editEv.reminderSentAt && <span className="cal-reminder-sent">Reminder sent {new Date(editEv.reminderSentAt).toLocaleDateString()}</span>}
              </label>
              <label className="cal-popover-label">
                Repeats
                <select className="ct-input" value={editEvForm.recurrenceFreq} onChange={e => setEditEvForm(f => ({ ...f, recurrenceFreq: e.target.value as RecurrenceFreq }))}>
                  {RECURRENCE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
              </label>
              {editEvForm.recurrenceFreq !== 'none' && (
                <label className="cal-popover-label">
                  Until (optional)
                  <input className="ct-input" type="date" value={editEvForm.recurrenceUntil} onChange={e => setEditEvForm(f => ({ ...f, recurrenceUntil: e.target.value }))} />
                </label>
              )}
              {editEv.type === 'task' && (
                <label className="cal-popover-label cal-popover-label--row">
                  <input type="checkbox" checked={editEv.completed} onChange={() => toggleComplete(editEv)} style={{ width: 'auto' }} />
                  Mark as completed
                </label>
              )}
            </div>
            <div className="cal-popover-actions">
              <button type="button" className="em-btn em-btn--primary em-btn--sm" onClick={saveEditEvent} disabled={evSaving}>{evSaving ? 'Saving…' : 'Save'}</button>
              <button type="button" className="em-btn em-btn--danger em-btn--sm" onClick={() => deleteEvent(editEv)}>Delete</button>
              <button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => setEditEv(null)}>Cancel</button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
