<script setup lang="ts">
// Tier 2 Block E — host analytics dashboard card. Shown on the event
// detail page; viewer+ role.
//
// Layout philosophy (post-UX-pass): hosts mostly want to answer three
// questions on a glance — "Are people coming?", "Who haven't I heard
// back from?", and "Was my last send-out worth it?". The hierarchy
// reflects that: hero metrics first, supporting charts grouped beneath,
// the long stale-guest list collapsed by default.
//
// Charts are inline SVG rather than chart.js — keeps the bundle lean and
// the page server-renderable.

interface Overview {
  invited: number
  attending: number
  declined: number
  maybe: number
  pending: number
  plus_ones_total: number
}

interface AnalyticsPayload {
  overview: Overview
  response_rate: { date: string; count: number }[]
  funnel: { invited: number; opened: number; responded: number }
  time_to_respond: { label: string; count: number }[]
  plus_ones: { label: string; count: number }[]
  channels: { source: string; invited: number; attending: number }[]
  stale_guests: {
    guest_id: string
    name: string
    email?: string | null
    has_opened: boolean
    invited_at: string
  }[]
  generated_at: string
}

const props = defineProps<{ eventId: string }>()
const config = useRuntimeConfig()
const auth = useAuth()

const data = ref<AnalyticsPayload | null>(null)
const loading = ref(true)
const error = ref<string | null>(null)

async function refresh() {
  loading.value = true
  error.value = null
  try {
    data.value = await useApi<AnalyticsPayload>(`/events/${props.eventId}/analytics?days=30`)
  } catch (e: any) {
    error.value = useErrMessage(e, 'Could not load analytics')
  } finally {
    loading.value = false
  }
}
onMounted(refresh)

// --- derived stats ---

const responseRatePct = computed(() => {
  const o = data.value?.overview
  if (!o || o.invited === 0) return 0
  return Math.round(((o.attending + o.declined + o.maybe) / o.invited) * 100)
})

const responseRateMax = computed(() => {
  const series = data.value?.response_rate ?? []
  return Math.max(1, ...series.map((p) => p.count))
})

const plusOnesMax = computed(() => {
  const series = data.value?.plus_ones ?? []
  return Math.max(1, ...series.map((p) => p.count))
})

const ttrMax = computed(() => {
  const series = data.value?.time_to_respond ?? []
  return Math.max(1, ...series.map((p) => p.count))
})

const funnelPct = computed(() => {
  const f = data.value?.funnel
  if (!f || f.invited === 0) return { opened: 0, responded: 0 }
  return {
    opened: Math.round((f.opened / f.invited) * 100),
    responded: Math.round((f.responded / f.invited) * 100),
  }
})

// Total headcount including plus-ones — the number caterers and venues
// actually need. More useful than "attending" alone, which understates the
// crowd.
const totalHeadcount = computed(() => {
  const o = data.value?.overview
  if (!o) return 0
  return o.attending + o.plus_ones_total
})

// SVG path for the 30-day response sparkline. Empty string when there's
// no data so the path renders as nothing instead of "M NaN,NaN".
const responseRateLineD = computed(() => {
  const series = data.value?.response_rate ?? []
  if (series.length === 0) return ''
  const w = 600
  const h = 80
  const max = responseRateMax.value
  const step = w / Math.max(1, series.length - 1)
  return series
    .map((p, i) => {
      const x = i * step
      const y = h - (p.count / max) * h
      return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`
    })
    .join(' ')
})

// Same series as a filled area underneath the line — gives the
// sparkline visual weight without dominating the surrounding text.
const responseRateAreaD = computed(() => {
  const d = responseRateLineD.value
  if (!d) return ''
  return `${d} L600,80 L0,80 Z`
})

function exportCSV() {
  const url = `${config.public.apiBase}/events/${props.eventId}/analytics/export.csv`
  fetch(url, {
    headers: auth.liveAccessToken() ? { Authorization: `Bearer ${auth.liveAccessToken()}` } : {},
    credentials: 'include',
  })
    .then((res) => {
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res.blob()
    })
    .then((blob) => {
      const a = document.createElement('a')
      a.href = URL.createObjectURL(blob)
      a.download = 'analytics.csv'
      a.click()
      setTimeout(() => URL.revokeObjectURL(a.href), 1000)
    })
    .catch((e) => {
      error.value = 'Could not export: ' + String(e)
    })
}

function fmtDate(iso?: string | null) {
  if (!iso) return ''
  try { return new Date(iso).toLocaleDateString() } catch { return iso }
}
</script>

<template>
  <section class="card">
    <header class="mb-5 flex flex-wrap items-center justify-between gap-3">
      <div>
        <h2 class="text-lg font-semibold">Analytics</h2>
        <p class="text-xs text-zinc-500">Last 30 days · {{ responseRatePct }}% response rate</p>
      </div>
      <button class="btn-ghost text-sm" :disabled="!data" @click="exportCSV">Export CSV</button>
    </header>

    <p v-if="loading" class="text-sm text-zinc-500">Loading analytics…</p>
    <p v-else-if="error" class="text-sm text-red-400">{{ error }}</p>

    <div v-else-if="data" class="space-y-6">
      <!-- Hero row — the three numbers a host opens this tab for. -->
      <div class="grid grid-cols-1 gap-3 md:grid-cols-3">
        <!-- Headcount: attending guests + their plus-ones. The big
             number is what the venue / caterer needs. -->
        <div class="relative overflow-hidden rounded-lg border border-brand-700/60 bg-brand-500/[0.06] p-4">
          <p class="text-xs font-medium uppercase tracking-wider text-brand-400">Headcount</p>
          <p class="mt-1 text-4xl font-semibold tabular-nums text-brand-200">{{ totalHeadcount }}</p>
          <p class="mt-1 text-xs text-zinc-400">
            {{ data.overview.attending }} attending
            <template v-if="data.overview.plus_ones_total > 0">
              · +{{ data.overview.plus_ones_total }} plus-ones
            </template>
          </p>
        </div>
        <!-- Waiting on: hosts can act on this number; the chase list is
             below. -->
        <div class="relative overflow-hidden rounded-lg border border-amber-800/40 bg-amber-950/10 p-4">
          <p class="text-xs font-medium uppercase tracking-wider text-amber-400">Awaiting reply</p>
          <p class="mt-1 text-4xl font-semibold tabular-nums text-amber-200">{{ data.overview.pending }}</p>
          <p class="mt-1 text-xs text-zinc-400">
            of {{ data.overview.invited }} invited
          </p>
        </div>
        <!-- Declined + Maybe stacked here; less prominent than the two
             above because a host can't act on them. -->
        <div class="relative overflow-hidden rounded-lg border border-zinc-800 bg-zinc-950 p-4">
          <p class="text-xs font-medium uppercase tracking-wider text-zinc-500">Won't make it</p>
          <p class="mt-1 text-4xl font-semibold tabular-nums text-zinc-200">{{ data.overview.declined }}</p>
          <p class="mt-1 text-xs text-zinc-400">
            <span v-if="data.overview.maybe > 0">+{{ data.overview.maybe }} maybe</span>
            <span v-else>declined</span>
          </p>
        </div>
      </div>

      <!-- Context row: how fast they're replying + the funnel. Side by
           side on desktop so they tell one coherent story. -->
      <div class="grid grid-cols-1 gap-5 md:grid-cols-2">
        <div class="rounded-lg border border-zinc-800 bg-zinc-950 p-4">
          <div class="mb-3 flex items-center justify-between">
            <p class="text-xs font-medium uppercase tracking-wider text-zinc-500">Replies, last 30 days</p>
          </div>
          <svg viewBox="0 0 600 80" preserveAspectRatio="none" class="h-20 w-full">
            <path :d="responseRateAreaD" fill="rgb(34 197 94 / 0.12)" />
            <path :d="responseRateLineD" fill="none" stroke="#22c55e" stroke-width="1.5" />
          </svg>
        </div>

        <div class="rounded-lg border border-zinc-800 bg-zinc-950 p-4">
          <p class="mb-3 text-xs font-medium uppercase tracking-wider text-zinc-500">Funnel</p>
          <div class="space-y-2">
            <div class="flex items-center gap-3">
              <span class="w-20 shrink-0 text-xs text-zinc-400">Invited</span>
              <div class="h-5 flex-1 overflow-hidden rounded bg-zinc-900">
                <div class="h-full rounded bg-brand-500" :style="{ width: '100%' }"></div>
              </div>
              <span class="w-10 shrink-0 text-right text-xs tabular-nums text-zinc-300">{{ data.funnel.invited }}</span>
            </div>
            <div class="flex items-center gap-3">
              <span class="w-20 shrink-0 text-xs text-zinc-400">Opened</span>
              <div class="h-5 flex-1 overflow-hidden rounded bg-zinc-900">
                <div class="h-full rounded bg-brand-400" :style="{ width: funnelPct.opened + '%' }"></div>
              </div>
              <span class="w-10 shrink-0 text-right text-xs tabular-nums text-zinc-300">{{ data.funnel.opened }}</span>
            </div>
            <div class="flex items-center gap-3">
              <span class="w-20 shrink-0 text-xs text-zinc-400">Responded</span>
              <div class="h-5 flex-1 overflow-hidden rounded bg-zinc-900">
                <div class="h-full rounded bg-brand-300" :style="{ width: funnelPct.responded + '%' }"></div>
              </div>
              <span class="w-10 shrink-0 text-right text-xs tabular-nums text-zinc-300">{{ data.funnel.responded }}</span>
            </div>
          </div>
        </div>
      </div>

      <!-- Distributions — always visible. Two compact charts side by
           side share the height of one tall chart so they cost no extra
           scroll. -->
      <div class="rounded-lg border border-zinc-800 bg-zinc-950">
        <div class="border-b border-zinc-900 px-4 py-3">
          <p class="text-sm font-medium text-zinc-200">Reply speed &amp; plus-ones</p>
        </div>
        <div class="grid grid-cols-1 gap-5 p-4 md:grid-cols-2">
          <div>
            <p class="mb-2 text-xs font-medium uppercase tracking-wider text-zinc-500">Time to respond</p>
            <div class="space-y-1.5">
              <div v-for="b in data.time_to_respond" :key="b.label" class="flex items-center gap-3">
                <span class="w-12 shrink-0 text-xs text-zinc-400">{{ b.label }}</span>
                <div class="h-4 flex-1 overflow-hidden rounded bg-zinc-900">
                  <div class="h-full rounded bg-brand-500" :style="{ width: ((b.count / ttrMax) * 100) + '%' }"></div>
                </div>
                <span class="w-8 shrink-0 text-right text-xs tabular-nums text-zinc-300">{{ b.count }}</span>
              </div>
            </div>
          </div>
          <div>
            <p class="mb-2 text-xs font-medium uppercase tracking-wider text-zinc-500">Plus-ones (attending)</p>
            <div class="space-y-1.5">
              <div v-for="b in data.plus_ones" :key="b.label" class="flex items-center gap-3">
                <span class="w-12 shrink-0 text-xs text-zinc-400">{{ b.label }}</span>
                <div class="h-4 flex-1 overflow-hidden rounded bg-zinc-900">
                  <div class="h-full rounded bg-brand-500" :style="{ width: ((b.count / plusOnesMax) * 100) + '%' }"></div>
                </div>
                <span class="w-8 shrink-0 text-right text-xs tabular-nums text-zinc-300">{{ b.count }}</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Stale guests — always visible. With 10+ rows we cap the list
           and point at the CSV export for the full count, so the panel
           doesn't dominate the page even on busy events. -->
      <div class="rounded-lg border border-zinc-800 bg-zinc-950">
        <div class="flex items-center gap-2 border-b border-zinc-900 px-4 py-3">
          <svg class="h-4 w-4 text-amber-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM10 4a1 1 0 011 1v5l3 1a1 1 0 11-.5 1.93l-3.5-1.16A1 1 0 019 10.83V5a1 1 0 011-1z" clip-rule="evenodd" />
          </svg>
          <p class="text-sm font-medium text-zinc-200">
            {{ data.stale_guests.length === 0 ? "Everyone's replied" : `${data.stale_guests.length} haven't replied yet` }}
          </p>
        </div>
        <ul v-if="data.stale_guests.length > 0" class="space-y-1.5 p-4">
          <li
            v-for="g in data.stale_guests.slice(0, 10)"
            :key="g.guest_id"
            class="flex items-center justify-between rounded-md border border-zinc-800 bg-zinc-900/40 px-3 py-2 text-sm"
          >
            <div class="min-w-0 flex-1">
              <div class="truncate font-medium text-zinc-100">{{ g.name }}</div>
              <div class="truncate text-xs text-zinc-500">
                {{ g.email || 'no email' }} · invited {{ fmtDate(g.invited_at) }}
              </div>
            </div>
            <span
              v-if="g.has_opened"
              class="ml-3 shrink-0 rounded-full bg-amber-900/30 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-amber-300"
              title="Opened their link but hasn't responded"
            >Opened</span>
          </li>
          <li v-if="data.stale_guests.length > 10" class="px-3 pt-1 text-xs text-zinc-500">
            and {{ data.stale_guests.length - 10 }} more — export the CSV for the full list.
          </li>
        </ul>
      </div>
    </div>
  </section>
</template>
