<template>
  <IonPage>
    <IonHeader>
      <IonToolbar color="primary">
        <IonButtons slot="start">
          <IonMenuButton />
        </IonButtons>
        <IonTitle>Schedule</IonTitle>
        <IonButtons slot="end">
          <IonButton @click="goToday" :disabled="focusDate === todayToronto()">
            <IonIcon slot="icon-only" :icon="todayOutline" />
          </IonButton>
        </IonButtons>
      </IonToolbar>
    </IonHeader>

    <IonContent>
      <!-- Date navigation bar -->
      <div class="date-nav">
        <IonButton fill="clear" @click="navPrev">
          <IonIcon slot="icon-only" :icon="chevronBackOutline" />
        </IonButton>
        <span class="date-nav-label" v-if="isMobile">{{ fmtDateLong(focusDate) }}</span>
        <span class="date-nav-label" v-else>
          {{ fmtDayHeader(visibleDates[0]!).weekday }} {{ fmtDayHeader(visibleDates[0]!).short }}
          &nbsp;–&nbsp;
          {{ fmtDayHeader(visibleDates[6]!).weekday }} {{ fmtDayHeader(visibleDates[6]!).short }}
        </span>
        <IonButton fill="clear" @click="navNext">
          <IonIcon slot="icon-only" :icon="chevronForwardOutline" />
        </IonButton>
      </div>

      <div v-if="loading" class="ion-text-center ion-padding-top" style="padding: 3rem 0">
        <IonSpinner name="crescent" />
      </div>

      <!-- ── Mobile: single-day stacked view ─────────────────────── -->
      <div v-else-if="isMobile" class="mobile-schedule">
        <div v-for="boat in boats" :key="boat.id" class="mobile-boat-card">
          <div class="mobile-boat-header">
            <div class="mobile-boat-info">
              <span class="mobile-boat-name">{{ boat.display_name || boat.name }}</span>
              <span v-if="boat.class" class="mobile-boat-class">{{ boat.class }}</span>
            </div>
            <IonBadge v-if="!boat.booking_available" color="medium">Out of service</IonBadge>
          </div>

          <div v-if="!boat.booking_available" class="slots-empty">Not available for booking.</div>
          <div v-else-if="daySlots(boat.id, focusDate).length === 0" class="slots-empty">No slots today.</div>
          <div v-else class="mobile-slots">
            <div
              v-for="slot in daySlots(boat.id, focusDate)"
              :key="slot.id"
              class="slot-block"
              :class="[slotClass(slot), (slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 'slot-tappable' : '']"
              :title="slotTitle(slot)"
              :role="(slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 'button' : undefined"
              :tabindex="(slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 0 : undefined"
              @click="handleSlotClick(slot)"
              @keydown.enter="handleSlotClick(slot)"
            >
              <div class="slot-main-row">
                <span class="slot-time">{{ fmtTime(slot.startTime) }}–{{ fmtTime(slot.endTime) }}</span>
                <IonIcon v-if="slot.type === 'available'" :icon="chevronForwardOutline" class="slot-chevron" />
              </div>
              <span class="slot-status">{{ slotLabel(slot) }}</span>
              <span class="slot-member">{{ slot.memberName ?? '' }}</span>
            </div>
          </div>
        </div>
      </div>

      <!-- ── Desktop: week grid ───────────────────────────────────── -->
      <div v-else class="desktop-schedule-wrap">
        <div class="desktop-schedule">
          <!-- Header row -->
          <div class="grid-header-row">
            <div class="grid-boat-col grid-header-cell"></div>
            <div
              v-for="date in visibleDates"
              :key="date"
              class="grid-day-col grid-header-cell"
              :class="{ 'is-today': date === todayToronto() }"
            >
              <span class="grid-day-weekday">{{ fmtDayHeader(date).weekday }}</span>
              <span class="grid-day-short">{{ fmtDayHeader(date).short }}</span>
            </div>
          </div>

          <!-- One row per boat -->
          <div v-for="boat in boats" :key="boat.id" class="grid-data-row">
            <div class="grid-boat-col grid-boat-label">
              <div class="grid-boat-name">{{ boat.display_name || boat.name }}</div>
              <div v-if="boat.class" class="grid-boat-class">{{ boat.class }}</div>
              <IonBadge v-if="!boat.booking_available" color="medium" class="oos-badge">OOS</IonBadge>
            </div>
            <div
              v-for="date in visibleDates"
              :key="date"
              class="grid-day-col grid-cell"
              :class="{ 'is-today': date === todayToronto(), 'cell-oos': !boat.booking_available }"
            >
              <template v-if="boat.booking_available">
                <div v-if="daySlots(boat.id, date).length === 0" class="cell-empty">—</div>
                <div v-else class="cell-slots">
                  <div
                    v-for="slot in daySlots(boat.id, date)"
                    :key="slot.id"
                    class="slot-block slot-sm"
                    :class="[slotClass(slot), (slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 'slot-tappable' : '']"
                    :title="slotTitle(slot)"
                    :role="(slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 'button' : undefined"
                    :tabindex="(slot.type === 'available' || slot.isOwn || authStore.isAdmin) ? 0 : undefined"
                    @click="handleSlotClick(slot)"
                    @keydown.enter="handleSlotClick(slot)"
                  >
                    <span class="slot-time-sm">{{ fmtTime(slot.startTime) }}-{{ fmtTime(slot.endTime) }}</span>
                    <span class="slot-status-sm">{{ slotLabelShort(slot) }}</span>
                    <span class="slot-member-sm">{{ slot.memberName ?? '' }}</span>
                  </div>
                </div>
              </template>
              <div v-else class="cell-empty">—</div>
            </div>
          </div>
        </div>
      </div>

      <!-- Legend -->
      <div class="legend">
        <span class="legend-item slot-block slot-available">Available</span>
        <span class="legend-item slot-block slot-confirmed">Confirmed</span>
        <span class="legend-item slot-block slot-tentative">Tentative</span>
        <span class="legend-item slot-block slot-pending">Pending</span>
      </div>
    </IonContent>
  </IonPage>
</template>

<script setup lang="ts">
import {
  IonPage, IonHeader, IonToolbar, IonTitle, IonContent,
  IonButtons, IonMenuButton, IonButton, IonIcon, IonBadge, IonSpinner,
  actionSheetController, alertController, onIonViewWillEnter,
} from '@ionic/vue'
import {
  chevronBackOutline, chevronForwardOutline, todayOutline,
  createOutline, trashOutline,
} from 'ionicons/icons'

import { useRouter } from 'vue-router'
const router = useRouter()
import type { Database, ReservationStatus } from '~/types/supabase'
import { useAuthStore } from '~/stores/auth'
import {
  todayToronto, toDateToronto, addDays,
  fmtTime, fmtDateLong, fmtDayHeader, weekDates, utcRange,
} from '~/utils/toronto'
import { useAppCache } from '~/composables/useAppCache'
import { useOfflineStatus } from '~/composables/useOfflineStatus'
import { useBookingDraft } from '~/composables/useBookingDraft'

type Boat        = Database['public']['Tables']['boats']['Row']
type Interval    = Database['public']['Tables']['intervals']['Row']
type SlotView    = Database['public']['Views']['reservation_slots']['Row']

interface SlotBlock {
  id: string
  boatId: string
  startTime: string
  endTime: string
  type: 'available' | 'booked'
  status: ReservationStatus | null
  isOwn: boolean
  memberName: string | null
}

definePageMeta({ layout: false, middleware: ['auth'] })

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const supabase = useSupabaseClient() as any
const user = useSupabaseUser()
const cache = useAppCache()
const { isOnline } = useOfflineStatus()
const { set: setDraft } = useBookingDraft()
const authStore = useAuthStore()

// ── Responsive ──────────────────────────────────────────────
const isMobile = ref(true)
function checkBreakpoint() { isMobile.value = window.innerWidth < 768 }
onMounted(() => { checkBreakpoint(); window.addEventListener('resize', checkBreakpoint) })
onUnmounted(() => window.removeEventListener('resize', checkBreakpoint))

// ── Date state ──────────────────────────────────────────────
const focusDate = ref(todayToronto())

const visibleDates = computed(() =>
  isMobile.value ? [focusDate.value] : weekDates(focusDate.value)
)

function navPrev() {
  focusDate.value = isMobile.value
    ? addDays(focusDate.value, -1)
    : addDays(focusDate.value, -7)
}
function navNext() {
  focusDate.value = isMobile.value
    ? addDays(focusDate.value, +1)
    : addDays(focusDate.value, +7)
}
function goToday() { focusDate.value = todayToronto() }

// ── Data ────────────────────────────────────────────────────
const loading          = ref(true)
const boats            = ref<Boat[]>([])
const intervals        = ref<Interval[]>([])
const slotViews        = ref<SlotView[]>([])
const myReservationIds = ref(new Set<string>())

async function fetchBoats() {
  if (!isOnline.value) {
    const cached = cache.peek<Boat[]>('boats')
    if (cached) boats.value = cached
    return
  }
  const { data } = await supabase.from('boats').select('*').order('name')
  boats.value = data ?? []
  if (data) cache.set('boats', data)
}

async function fetchSchedule() {
  loading.value = true
  const dates = visibleDates.value
  const wk = cache.weekKey(dates[0]!)

  if (!isOnline.value) {
    const cachedIntervals = cache.peek<Interval[]>(`intervals:${wk}`)
    const cachedSlots     = cache.peek<SlotView[]>(`slots:${wk}`)
    if (cachedIntervals) intervals.value = cachedIntervals
    if (cachedSlots)     slotViews.value = cachedSlots
    loading.value = false
    return
  }

  const { from, to } = utcRange(dates[0]!, dates[dates.length - 1]!)
  const [intRes, slotRes, myRes] = await Promise.all([
    supabase.from('intervals').select('*').gte('start_time', from).lte('start_time', to),
    supabase.from('reservation_slots').select('*').gte('start_time', from).lte('start_time', to),
    supabase.from('reservations').select('id').eq('user_id', user.value?.id).gte('start_time', from).lte('start_time', to).neq('status', 'cancelled'),
  ])

  intervals.value = intRes.data  ?? []
  slotViews.value = slotRes.data ?? []
  myReservationIds.value = new Set((myRes.data ?? []).map((r: { id: string }) => r.id))
  loading.value   = false

  if (intRes.data)  cache.set(`intervals:${wk}`, intRes.data)
  if (slotRes.data) cache.set(`slots:${wk}`, slotRes.data)
}

async function loadAll() {
  await fetchBoats()
  await fetchSchedule()
}

watch(user, (val) => { if (val) loadAll() }, { immediate: true })
onIonViewWillEnter(() => { if (user.value) loadAll() })

// Re-fetch when the visible date range changes (user-driven navigation)
watch(visibleDates, () => { if (user.value) fetchSchedule() })

// ── Slot computation ─────────────────────────────────────────
function daySlots(boatId: string, dateIso: string): SlotBlock[] {
  const dayIntervals = intervals.value
    .filter(i =>
      i.boat_id === boatId &&
      toDateToronto(new Date(i.start_time)) === dateIso
    )
    .sort((a, b) => a.start_time.localeCompare(b.start_time))

  return dayIntervals.map(interval => {
    const res = slotViews.value.find(r =>
      r.boat_id === boatId &&
      r.start_time >= interval.start_time &&
      r.start_time <  interval.end_time
    )
    if (res) {
      return {
        id:        res.id,
        boatId,
        startTime: interval.start_time,
        endTime:   interval.end_time,
        type:      'booked' as const,
        status:     res.status,
        isOwn:      myReservationIds.value.has(res.id),
        memberName: res.member_name ?? null,
      }
    }
    return {
      id:        interval.id,
      boatId,
      startTime: interval.start_time,
      endTime:   interval.end_time,
      type:      'available' as const,
      status:    null,
      isOwn:     false,
      memberName: null,
    }
  })
}

// ── Display helpers ──────────────────────────────────────────
function slotClass(slot: SlotBlock) {
  return slot.type === 'available' ? 'slot-available' : `slot-${slot.status}`
}

function slotLabel(slot: SlotBlock): string {
  if (slot.type === 'available') return 'Available'
  const labels: Record<string, string> = {
    confirmed: 'Confirmed',
    tentative: 'Tentative',
    pending:   'Pending',
  }
  return labels[slot.status ?? ''] ?? 'Booked'
}

function slotLabelShort(slot: SlotBlock): string {
  if (slot.type === 'available') return 'Free'
  return slotLabel(slot)
}

function bookSlot(slot: SlotBlock) {
  const boat = boats.value.find(b => b.id === slot.boatId)
  if (boat) setDraft(boat, slot.startTime, slot.endTime)
  router.push('/reservations/create')
}

function handleSlotClick(slot: SlotBlock) {
  if (slot.type === 'available') {
    bookSlot(slot)
  } else if (slot.isOwn || authStore.isAdmin) {
    openReservationSheet(slot)
  }
}

async function openReservationSheet(slot: SlotBlock) {
  const isFuture = new Date(slot.startTime) > new Date()
  const memberName = slot.memberName ?? ''
  const header = memberName
    ? `${memberName} · ${fmtTime(slot.startTime)}–${fmtTime(slot.endTime)}`
    : `${fmtTime(slot.startTime)}–${fmtTime(slot.endTime)}`
  const sheet = await actionSheetController.create({
    header,
    buttons: [
      ...(isFuture ? [
        {
          text: 'Edit',
          icon: createOutline,
          handler: () => void router.push(`/reservations/edit/${slot.id}`),
        },
        {
          text: 'Cancel Reservation',
          role: 'destructive',
          icon: trashOutline,
          handler: () => void confirmCancelSlot(slot.id),
        },
      ] : []),
      { text: 'Dismiss', role: 'cancel' },
    ],
  })
  await sheet.present()
}

async function confirmCancelSlot(id: string) {
  const alert = await alertController.create({
    header: 'Cancel Reservation',
    message: 'Are you sure you want to cancel this reservation?',
    buttons: [
      { text: 'Keep it', role: 'cancel' },
      { text: 'Cancel Reservation', role: 'destructive', handler: () => void cancelSlot(id) },
    ],
  })
  await alert.present()
}

async function cancelSlot(id: string) {
  await supabase.from('reservations').update({ status: 'cancelled' }).eq('id', id)
  slotViews.value = slotViews.value.filter(s => s.id !== id)
  myReservationIds.value.delete(id)
}

function slotTitle(slot: SlotBlock): string {
  return `${fmtTime(slot.startTime)}–${fmtTime(slot.endTime)} · ${slotLabel(slot)}`
}
</script>

<style scoped>
/* ── Date navigation bar ───────────────────────────────────── */
.date-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.25rem 0.25rem;
  border-bottom: 1px solid var(--ion-color-light-shade);
}
.date-nav-label {
  font-weight: 600;
  font-size: 0.95rem;
  flex: 1;
  text-align: center;
}

/* ── Slot block base ────────────────────────────────────────── */
.slot-block {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.65rem 0.75rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
  font-weight: 500;
  min-height: 56px;
  justify-content: center;
  user-select: none;
}
.slot-available  { background: var(--ion-color-success);  color: #fff; }
.slot-confirmed  { background: var(--ion-color-medium);   color: #fff; }
.slot-tentative  { background: var(--ion-color-primary);  color: #fff; }
.slot-pending    { background: var(--ion-color-warning);  color: #333; }

.slot-main-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.slot-time   { font-variant-numeric: tabular-nums; font-size: 0.9rem; }
.slot-status { font-size: 0.75rem; opacity: 0.85; }
.slot-member { font-size: 0.72rem; opacity: 0.8; font-style: italic; min-height: 1em; }
.slot-chevron { font-size: 1rem; opacity: 0.8; flex-shrink: 0; }
.slot-member-sm { font-size: 0.62rem; opacity: 0.8; font-style: italic; }

.slot-tappable {
  cursor: pointer;
  transition: filter 0.12s, transform 0.12s;
}
.slot-tappable:active {
  filter: brightness(0.88);
  transform: scale(0.98);
}

/* ── Mobile schedule ────────────────────────────────────────── */
.mobile-schedule { padding: 0.75rem; display: flex; flex-direction: column; gap: 0.75rem; }

.mobile-boat-card {
  border: 1px solid var(--ion-color-light-shade);
  border-radius: 0.75rem;
  overflow: hidden;
}
.mobile-boat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.6rem 0.85rem;
  background: var(--ion-color-light);
  border-bottom: 1px solid var(--ion-color-light-shade);
}
.mobile-boat-info { display: flex; align-items: baseline; gap: 0.5rem; }
.mobile-boat-name { font-weight: 600; font-size: 0.95rem; }
.mobile-boat-class { font-size: 0.78rem; color: var(--ion-color-medium); }

.mobile-slots {
  padding: 0.6rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.slots-empty {
  padding: 0.6rem 0.85rem;
  font-size: 0.82rem;
  color: var(--ion-color-medium);
}

/* ── Desktop schedule ───────────────────────────────────────── */
.desktop-schedule-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.desktop-schedule {
  display: grid;
  /* boat-label column + 7 day columns */
  grid-template-columns: 130px repeat(7, minmax(110px, 1fr));
  min-width: 900px;
  border-top: 1px solid var(--ion-color-light-shade);
}

.grid-header-row,
.grid-data-row {
  display: contents;
}

.grid-header-cell {
  padding: 0.5rem 0.5rem;
  background: var(--ion-color-light);
  border-bottom: 2px solid var(--ion-color-light-shade);
  border-right: 1px solid var(--ion-color-light-shade);
  position: sticky;
  top: 0;
  z-index: 1;
}
.grid-day-col.grid-header-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
}
.grid-day-weekday { font-size: 0.72rem; color: var(--ion-color-medium); text-transform: uppercase; font-weight: 600; }
.grid-day-short   { font-size: 0.85rem; font-weight: 600; }

.grid-header-cell.is-today,
.grid-cell.is-today {
  background: color-mix(in srgb, var(--ion-color-primary) 8%, transparent);
}

.grid-boat-col {
  border-right: 1px solid var(--ion-color-light-shade);
}
.grid-boat-label {
  padding: 0.6rem 0.65rem;
  background: var(--ion-color-light-tint);
  border-bottom: 1px solid var(--ion-color-light-shade);
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  position: sticky;
  left: 0;
  z-index: 1;
}
.grid-boat-name  { font-weight: 600; font-size: 0.85rem; }
.grid-boat-class { font-size: 0.72rem; color: var(--ion-color-medium); }
.oos-badge       { font-size: 0.65rem; margin-top: 0.2rem; }

.grid-cell {
  padding: 0.4rem 0.35rem;
  border-bottom: 1px solid var(--ion-color-light-shade);
  border-right: 1px solid var(--ion-color-light-shade);
  vertical-align: top;
}
.cell-empty  { color: var(--ion-color-light-shade); text-align: center; font-size: 1.1rem; padding: 0.5rem 0; }
.cell-oos    { background: repeating-linear-gradient(135deg, transparent, transparent 4px, rgba(0,0,0,0.03) 4px, rgba(0,0,0,0.03) 8px); }
.cell-slots  { display: flex; flex-direction: column; gap: 0.3rem; }

/* Compact slot variant for desktop cells */
.slot-sm {
  padding: 0.25rem 0.4rem;
  font-size: 0.72rem;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  border-radius: 0.3rem;
  min-height: 0;
}
.slot-time-sm   { font-variant-numeric: tabular-nums; font-size: 0.72rem; line-height: 1.3; font-weight: 600; }
.slot-status-sm { font-size: 0.65rem; opacity: 0.85; line-height: 1.3; }
.slot-member-sm { font-size: 0.65rem; opacity: 0.8; font-style: italic; line-height: 1.3; min-height: 0.9em; }

/* ── Legend ─────────────────────────────────────────────────── */
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--ion-color-light-shade);
}
.legend-item { font-size: 0.75rem; padding: 0.2rem 0.6rem; }
</style>
