import { randomUUID } from 'node:crypto'
import { state } from '../state.js'
import { queueCalendarEventsWrite } from '../data.js'
import { requireAdminAuth } from '../auth.js'

const VALID_TYPES = ['general', 'recording', 'social', 'task']
const VALID_FREQS = ['none', 'weekly', 'biweekly', 'monthly']
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/
const TIME_RE = /^\d{2}:\d{2}$/

function sanitizeRecurrence(raw) {
  if (!raw || typeof raw !== 'object') return null
  const freq = VALID_FREQS.includes(raw.freq) ? raw.freq : 'none'
  if (freq === 'none') return null
  const until = typeof raw.until === 'string' && DATE_RE.test(raw.until) ? raw.until : null
  return { freq, until }
}

function sanitize(ev) {
  const recurrence = sanitizeRecurrence(ev.recurrence)
  return {
    id: ev.id,
    type: VALID_TYPES.includes(ev.type) ? ev.type : 'general',
    title: typeof ev.title === 'string' ? ev.title.slice(0, 300) : '',
    date: typeof ev.date === 'string' && DATE_RE.test(ev.date) ? ev.date : '',
    startTime: typeof ev.startTime === 'string' && TIME_RE.test(ev.startTime) ? ev.startTime : undefined,
    notes: typeof ev.notes === 'string' ? ev.notes.slice(0, 2000) : '',
    completed: Boolean(ev.completed),
    reminderDays: Number.isFinite(ev.reminderDays) && ev.reminderDays > 0 ? ev.reminderDays : 0,
    reminderSentAt: typeof ev.reminderSentAt === 'string' ? ev.reminderSentAt : undefined,
    recurrence: recurrence ?? undefined,
    createdAt: typeof ev.createdAt === 'string' ? ev.createdAt : new Date().toISOString(),
  }
}

// ── iCal helpers ─────────────────────────────────────────────────────────────

function escapeIcs(s) {
  return String(s ?? '').replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n')
}

function foldIcsLine(line) {
  const chars = [...line]
  if (chars.length <= 75) return line
  const parts = []
  let current = ''
  for (const ch of chars) {
    if ((current + ch).length > 75) { parts.push(current); current = ' ' + ch }
    else current += ch
  }
  if (current) parts.push(current)
  return parts.join('\r\n')
}

function toIcsDatetime(dateStr, timeStr) {
  const d = dateStr.replace(/-/g, '')
  if (timeStr && TIME_RE.test(timeStr)) {
    const t = timeStr.replace(':', '') + '00'
    return `DTSTART:${d}T${t}`
  }
  return `DTSTART;VALUE=DATE:${d}`
}

function toIcsTimestamp(iso) {
  try {
    const d = new Date(iso)
    return d.toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '')
  } catch { return '' }
}

function rruleFor(recurrence) {
  if (!recurrence || recurrence.freq === 'none') return null
  const freqMap = { weekly: 'WEEKLY', biweekly: 'WEEKLY', monthly: 'MONTHLY' }
  const freq = freqMap[recurrence.freq]
  if (!freq) return null
  let rule = `RRULE:FREQ=${freq}`
  if (recurrence.freq === 'biweekly') rule += ';INTERVAL=2'
  if (recurrence.until) rule += `;UNTIL=${recurrence.until.replace(/-/g, '')}T235959Z`
  return rule
}

function generateIcal() {
  const lines = [
    'BEGIN:VCALENDAR',
    'VERSION:2.0',
    'PRODID:-//Siteforge//CalendarExport//EN',
    'CALSCALE:GREGORIAN',
    'METHOD:PUBLISH',
    'X-WR-CALNAME:Verse by Verse Calendar',
  ]

  for (const ep of (state.podcastChecklist?.episodes ?? [])) {
    if (!ep.datePublished) continue
    const summary = [ep.series, ep.episodeNumber != null ? `Ep ${ep.episodeNumber}` : null, ep.title].filter(Boolean).join(' · ')
    const dtstart = ep.startTime ? `DTSTART:${ep.datePublished.replace(/-/g, '')}T${ep.startTime.replace(':', '')}00` : `DTSTART;VALUE=DATE:${ep.datePublished.replace(/-/g, '')}`
    lines.push('BEGIN:VEVENT')
    lines.push(`UID:ep-${ep.id}@siteforge`)
    lines.push(dtstart)
    lines.push(`SUMMARY:${escapeIcs(summary || 'Episode')}`)
    if (ep.title) lines.push(`DESCRIPTION:${escapeIcs(ep.title)}`)
    lines.push('END:VEVENT')
  }

  for (const ev of (state.calendarEvents ?? [])) {
    if (!ev.date) continue
    lines.push('BEGIN:VEVENT')
    lines.push(`UID:ev-${ev.id}@siteforge`)
    lines.push(toIcsDatetime(ev.date, ev.startTime))
    lines.push(`SUMMARY:${escapeIcs(ev.title || '(Event)')}`)
    if (ev.notes) lines.push(`DESCRIPTION:${escapeIcs(ev.notes)}`)
    const rrule = rruleFor(ev.recurrence)
    if (rrule) lines.push(rrule)
    if (ev.createdAt) lines.push(`CREATED:${toIcsTimestamp(ev.createdAt)}`)
    lines.push('END:VEVENT')
  }

  lines.push('END:VCALENDAR')
  return lines.map(foldIcsLine).join('\r\n') + '\r\n'
}

// ── Routes ───────────────────────────────────────────────────────────────────

export function register(app) {
  app.get('/api/admin-calendar-events', requireAdminAuth, (_req, res) => {
    res.json({ events: state.calendarEvents })
  })

  app.get('/api/admin-calendar.ics', requireAdminAuth, (_req, res) => {
    const ical = generateIcal()
    res.setHeader('Content-Type', 'text/calendar; charset=utf-8')
    res.setHeader('Content-Disposition', 'attachment; filename="siteforge-calendar.ics"')
    res.send(ical)
  })

  app.post('/api/admin-calendar-events', requireAdminAuth, (req, res) => {
    const { type, title, date, startTime, notes, reminderDays, recurrence } = req.body ?? {}
    if (!title?.trim()) { res.status(400).json({ message: 'Title is required.' }); return }
    if (!date || !DATE_RE.test(date)) { res.status(400).json({ message: 'Valid date is required.' }); return }
    if (type && !VALID_TYPES.includes(type)) { res.status(400).json({ message: 'Invalid type.' }); return }

    const ev = sanitize({
      id: randomUUID(),
      type: type ?? 'general',
      title: String(title).trim(),
      date,
      startTime: startTime ?? undefined,
      notes: notes ?? '',
      completed: false,
      reminderDays: reminderDays ?? 0,
      recurrence: recurrence ?? null,
      createdAt: new Date().toISOString(),
    })

    state.calendarEvents.unshift(ev)
    queueCalendarEventsWrite()
    res.json({ ok: true, event: ev })
  })

  app.patch('/api/admin-calendar-events/:id', requireAdminAuth, (req, res) => {
    const { id } = req.params
    let found = false
    state.calendarEvents = state.calendarEvents.map(ev => {
      if (ev.id !== id) return ev
      found = true
      const patch = {}
      if (typeof req.body?.title === 'string') patch.title = req.body.title.trim().slice(0, 300)
      if (typeof req.body?.date === 'string' && DATE_RE.test(req.body.date)) patch.date = req.body.date
      if (typeof req.body?.startTime === 'string') patch.startTime = TIME_RE.test(req.body.startTime) ? req.body.startTime : undefined
      if (req.body?.startTime === null) patch.startTime = undefined
      if (typeof req.body?.notes === 'string') patch.notes = req.body.notes.trim().slice(0, 2000)
      if (typeof req.body?.completed === 'boolean') patch.completed = req.body.completed
      if (VALID_TYPES.includes(req.body?.type)) patch.type = req.body.type
      if (Number.isFinite(req.body?.reminderDays)) patch.reminderDays = req.body.reminderDays
      if ('recurrence' in (req.body ?? {})) patch.recurrence = sanitizeRecurrence(req.body.recurrence) ?? undefined
      const dateChanged = patch.date && patch.date !== ev.date
      const reminderChanged = 'reminderDays' in patch && patch.reminderDays !== ev.reminderDays
      if (dateChanged || reminderChanged) patch.reminderSentAt = undefined
      return { ...ev, ...patch }
    })
    if (!found) { res.status(404).json({ message: 'Event not found.' }); return }
    queueCalendarEventsWrite()
    res.json({ ok: true })
  })

  app.delete('/api/admin-calendar-events/:id', requireAdminAuth, (req, res) => {
    const before = state.calendarEvents.length
    state.calendarEvents = state.calendarEvents.filter(ev => ev.id !== req.params.id)
    if (state.calendarEvents.length === before) { res.status(404).json({ message: 'Event not found.' }); return }
    queueCalendarEventsWrite()
    res.json({ ok: true })
  })
}
