<script setup lang="ts">
interface ExistingRSVP {
  id: string
  response: 'attending' | 'declined' | 'maybe'
  plus_ones: number
  dietary_notes?: string | null
  submitted_at: string
  edit_count: number
}

interface CalendarLinks {
  google: string
  outlook: string
  yahoo: string
  ics: string
}

interface AccessResponse {
  guest: { id: string; name: string; email?: string | null; plus_ones: number }
  event: { id: string; name: string; venue: string; event_date: string }
  token: { id: string; status: string; expires_at: string }
  access_log_id: string
  rsvp?: ExistingRSVP | null
  calendar?: CalendarLinks
}

interface RSVPSubmitResponse {
  rsvp?: ExistingRSVP & { risk_score?: number }
  fraud: { score: number; risk: string; reasons: string[]; used: boolean }
  blocked: boolean
  edited?: boolean
}

const MAX_EDITS = 5

const route = useRoute()
const token = route.params.token as string

const loading = ref(true)
const access = ref<AccessResponse | null>(null)
const loadError = ref<string | null>(null)

const response = ref<'attending' | 'declined' | 'maybe'>('attending')
const plusOnes = ref(0)
const dietary = ref('')

const submitting = ref(false)
const result = ref<RSVPSubmitResponse | null>(null)
const submitError = ref<string | null>(null)

// existing tracks the RSVP that was on file when the page loaded.
// editing toggles the form open over the "already responded" summary.
const existing = ref<ExistingRSVP | null>(null)
const editing = ref(false)

const editsRemaining = computed(() => {
  const used = existing.value?.edit_count ?? 0
  return Math.max(0, MAX_EDITS - used)
})
const editLimitReached = computed(() => editsRemaining.value <= 0)

function prefillFromRSVP(rsvp: ExistingRSVP) {
  response.value = rsvp.response
  plusOnes.value = rsvp.plus_ones
  dietary.value = rsvp.dietary_notes ?? ''
}

onMounted(async () => {
  try {
    access.value = await useApi<AccessResponse>(`/access/${token}`)
    if (!access.value) return
    plusOnes.value = access.value.guest.plus_ones || 0
    if (access.value.rsvp) {
      existing.value = access.value.rsvp
      prefillFromRSVP(access.value.rsvp)
    }
  } catch (e: any) {
    loadError.value = e?.data?.error || e?.message || 'Invitation not found'
  } finally {
    loading.value = false
  }
})

async function submit() {
  submitting.value = true
  submitError.value = null
  const isEdit = !!existing.value
  try {
    const fp = useFingerprint()
    result.value = await useApi<RSVPSubmitResponse>(`/rsvp/${token}`, {
      method: isEdit ? 'PATCH' : 'POST',
      body: {
        response: response.value,
        plus_ones: plusOnes.value,
        dietary_notes: dietary.value || null,
        fingerprint: fp,
      },
    })
    // Refresh the cached "existing" view so a back-to-summary toggle shows
    // the new state and the edit counter without a page reload.
    if (result.value?.rsvp) {
      existing.value = {
        id: result.value.rsvp.id,
        response: result.value.rsvp.response,
        plus_ones: result.value.rsvp.plus_ones,
        dietary_notes: result.value.rsvp.dietary_notes ?? null,
        submitted_at: result.value.rsvp.submitted_at,
        edit_count: result.value.rsvp.edit_count,
      }
    }
    editing.value = false
  } catch (e: any) {
    // BLOCK band returns 403 with the fraud decision; surface it the same
    // way the first-submit path does.
    if (e?.data?.fraud) {
      result.value = e.data
    } else {
      submitError.value = e?.data?.error || e?.message || 'Could not submit RSVP'
    }
  } finally {
    submitting.value = false
  }
}

function startEditing() {
  if (existing.value) prefillFromRSVP(existing.value)
  result.value = null
  editing.value = true
}

function cancelEditing() {
  if (existing.value) prefillFromRSVP(existing.value)
  editing.value = false
}

function fmtDate(iso?: string) {
  if (!iso) return ''
  try { return new Date(iso).toLocaleString() } catch { return iso }
}

// Calendar links come from the backend so we don't reimplement Google /
// Outlook / Yahoo encoding rules per provider. They're available as soon
// as /access loads — the guest can grab them before submitting.
const calendar = computed<CalendarLinks | null>(() => access.value?.calendar ?? null)

// showForm = no prior submission, or the guest has clicked "Change my response"
const showForm = computed(() => editing.value || !existing.value)
const submitLabel = computed(() => {
  if (submitting.value) return existing.value ? 'Updating…' : 'Submitting…'
  return existing.value ? 'Update RSVP' : 'Submit RSVP'
})
</script>

<template>
  <section class="mx-auto max-w-xl py-8">
    <div v-if="loading" class="text-sm text-zinc-500">Looking up your invitation…</div>

    <div v-else-if="loadError" class="card border-red-900/60 bg-red-950/30">
      <h1 class="mb-2 text-xl font-semibold text-red-200">Invitation unavailable</h1>
      <p class="text-sm text-red-300">{{ loadError }}</p>
    </div>

    <div v-else-if="result?.blocked" class="card border-red-900/60 bg-red-950/30">
      <h1 class="mb-2 text-xl font-semibold text-red-200">This invitation cannot be used</h1>
      <p class="text-sm text-red-300">
        The host has been notified of a suspicious access attempt.
      </p>
      <p class="mt-3 text-xs text-red-400">
        Risk score {{ result.fraud.score }} · {{ result.fraud.risk }}
      </p>
    </div>

    <div v-else-if="result?.rsvp && !editing" class="card border-brand-900/60 bg-brand-950/20">
      <h1 class="mb-2 text-xl font-semibold text-brand-200">
        {{ result.edited ? 'Your response has been updated' : "You're confirmed" }}
      </h1>
      <p class="text-sm text-brand-300">
        Response recorded as <strong>{{ result.rsvp.response }}</strong> with
        +{{ result.rsvp.plus_ones }} plus-ones.
      </p>
      <p class="mt-3 text-xs text-zinc-500">
        Risk score {{ result.fraud.score }} · {{ result.fraud.risk }}
        <span v-if="!result.fraud.used"> · fallback</span>
      </p>

      <AddToCalendar
        v-if="calendar && result.rsvp.response === 'attending'"
        :links="calendar"
        class="mt-5 border-t border-zinc-800 pt-4"
      />

      <div v-if="!editLimitReached" class="mt-5 flex items-center justify-between gap-3 border-t border-zinc-800 pt-4">
        <p class="text-xs text-zinc-500">
          Need to change something? You have {{ editsRemaining }}
          {{ editsRemaining === 1 ? 'edit' : 'edits' }} left.
        </p>
        <button type="button" class="btn-ghost text-sm" @click="startEditing">Change my response</button>
      </div>
      <p v-else class="mt-5 border-t border-zinc-800 pt-4 text-xs text-zinc-500">
        You've used all {{ MAX_EDITS }} edits on this invitation — contact your host if you need
        to change anything else.
      </p>
    </div>

    <div v-else-if="existing && !editing" class="card border-brand-900/60 bg-brand-950/20">
      <p class="text-xs uppercase tracking-widest text-brand-500">RSVP on file</p>
      <h1 class="mb-1 text-2xl font-semibold">{{ access?.event.name }}</h1>
      <p class="mb-5 text-sm text-zinc-400">
        {{ access?.event.venue }} · {{ fmtDate(access?.event.event_date) }}
      </p>
      <p class="mb-4 text-sm">
        You responded <strong class="capitalize">{{ existing.response }}</strong>
        <span v-if="existing.plus_ones > 0"> with +{{ existing.plus_ones }} plus-ones</span>
        on {{ fmtDate(existing.submitted_at) }}.
      </p>

      <AddToCalendar
        v-if="calendar && existing.response === 'attending'"
        :links="calendar"
        class="mb-4 border-t border-zinc-800 pt-4"
      />

      <div v-if="!editLimitReached" class="flex items-center justify-between gap-3 border-t border-zinc-800 pt-4">
        <p class="text-xs text-zinc-500">
          {{ editsRemaining }} {{ editsRemaining === 1 ? 'edit' : 'edits' }} remaining.
        </p>
        <button type="button" class="btn-ghost text-sm" @click="startEditing">Change my response</button>
      </div>
      <p v-else class="border-t border-zinc-800 pt-4 text-xs text-zinc-500">
        You've used all {{ MAX_EDITS }} edits on this invitation.
      </p>
    </div>

    <div v-else-if="access && showForm" class="card">
      <p class="text-xs uppercase tracking-widest text-brand-500">
        {{ existing ? 'Update your response' : 'Invitation' }}
      </p>
      <h1 class="mb-1 text-2xl font-semibold">{{ access.event.name }}</h1>
      <p class="mb-6 text-sm text-zinc-400">
        {{ access.event.venue }} · {{ fmtDate(access.event.event_date) }}
      </p>

      <p class="mb-6 text-sm">
        Hi <span class="font-medium text-zinc-100">{{ access.guest.name }}</span> —
        <template v-if="existing">change your response below — {{ editsRemaining }}
          {{ editsRemaining === 1 ? 'edit' : 'edits' }} remaining.</template>
        <template v-else>please confirm your response below.</template>
      </p>

      <div class="mb-4">
        <label class="label">Response</label>
        <div class="flex gap-2">
          <button
            v-for="opt in (['attending', 'declined', 'maybe'] as const)"
            :key="opt"
            type="button"
            class="btn-ghost flex-1 capitalize"
            :class="response === opt ? 'border border-brand-500 text-brand-300' : 'border border-zinc-800'"
            @click="response = opt"
          >{{ opt }}</button>
        </div>
      </div>

      <div v-if="access.guest.plus_ones > 0" class="mb-4">
        <label class="label">
          Plus-ones
          <span class="ml-1 font-normal normal-case text-zinc-500">
            (you may bring up to {{ access.guest.plus_ones }})
          </span>
        </label>
        <div class="flex items-center overflow-hidden rounded-lg border border-zinc-700 bg-zinc-900">
          <button
            type="button"
            class="flex h-11 w-12 shrink-0 items-center justify-center text-lg text-zinc-400 transition hover:bg-zinc-800 hover:text-zinc-100 disabled:opacity-30"
            :disabled="plusOnes <= 0"
            @click="plusOnes = Math.max(0, plusOnes - 1)"
          >−</button>
          <span class="flex-1 text-center text-base font-semibold tabular-nums text-zinc-100">{{ plusOnes }}</span>
          <button
            type="button"
            class="flex h-11 w-12 shrink-0 items-center justify-center text-lg text-zinc-400 transition hover:bg-zinc-800 hover:text-zinc-100 disabled:opacity-30"
            :disabled="plusOnes >= access.guest.plus_ones"
            @click="plusOnes = Math.min(access.guest.plus_ones, plusOnes + 1)"
          >+</button>
        </div>
      </div>
      <p v-else class="mb-4 text-xs text-zinc-500">
        This invitation is for one person only — no plus-ones for this one.
      </p>

      <div class="mb-6">
        <label class="label">Dietary notes (optional)</label>
        <input v-model="dietary" class="input" placeholder="e.g. vegetarian" />
      </div>

      <div class="flex items-center gap-3">
        <button class="btn-primary flex-1" :disabled="submitting" @click="submit">
          {{ submitLabel }}
        </button>
        <button v-if="existing" type="button" class="btn-ghost" :disabled="submitting" @click="cancelEditing">
          Cancel
        </button>
      </div>

      <p v-if="submitError" class="mt-3 text-sm text-red-400">{{ submitError }}</p>
    </div>
  </section>
</template>
