import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useMemo, useState } from 'react'
import { m } from '@/i18n/messages'
import { useLocale, getLocale } from '@/i18n/config'
import { asI18n } from '@pikku/react'
import {
  Badge,
  Box,
  Button,
  Container,
  Group,
  Paper,
  Select,
  SimpleGrid,
  Stack,
  Text,
  Title,
  Tooltip,
} from '@pikku/mantine/core'
import { usePikkuQuery } from '@project/functions-sdk/pikku/api.gen'
import { MarketingShell } from '../components/MarketingShell'
import { PageTitle } from '../components/AppLayout'

// Brand-aligned: free is the inviting state (cream + plum text, clickable),
// confirmed is fully booked (filled plum), enquiry/reserved sit between.
const STATUS_BG: Record<string, { bg: string; fg: string }> = {
  free:      { bg: '#FBF8FA', fg: '#4E3149' },
  enquiry:   { bg: '#FFF8E0', fg: '#806600' },
  reserved:  { bg: '#FFEDDC', fg: '#8A4A1A' },
  confirmed: { bg: '#88557F', fg: '#FFFFFF' },
}
const STATUS_KEYS = ['free', 'enquiry', 'reserved', 'confirmed'] as const

type DayBooking = {
  status: typeof STATUS_KEYS[number]
  eventName: string | null
  halfHouse: boolean
}
type DayCell = {
  date: Date
  status: typeof STATUS_KEYS[number]
  eventName: string | null
  halfHouse: boolean
  bookings: DayBooking[]
}

// Berlin-local YYYY-MM-DD string from a Date
const berlinStr = (d: Date) => d.toLocaleDateString('sv-SE', { timeZone: 'Europe/Berlin' })

function groupByMonth(days: DayCell[]) {
  const months: Record<string, DayCell[]> = {}
  for (const d of days) {
    const key = berlinStr(d.date).slice(0, 7)
    ;(months[key] ??= []).push(d)
  }
  return Object.entries(months).map(([key, ds]) => ({ key, days: ds }))
}

function monthLabel(yyyymm: string, locale: string) {
  const [y, m] = yyyymm.split('-')
  const d = new Date(Number(y), Number(m) - 1, 1)
  return d.toLocaleString(locale, { month: 'long', year: 'numeric' })
}

function dowMonStart(d: Date) {
  return (d.getUTCDay() + 6) % 7
}

// Inclusive list of YYYY-MM-DD strings between two date strings (UTC day steps).
function rangeDates(start: string, end: string): string[] {
  const out: string[] = []
  const cur = new Date(`${start}T00:00:00Z`)
  const last = new Date(`${end}T00:00:00Z`)
  while (cur <= last) {
    out.push(cur.toISOString().slice(0, 10))
    cur.setUTCDate(cur.getUTCDate() + 1)
  }
  return out
}

const SELECT_BG = { bg: '#88557F', fg: '#FFFFFF' } // endpoint
const SELECT_MID = { bg: '#EFE3ED', fg: '#4E3149' } // in-between

function MonthGrid({
  days,
  dowLabels,
  statusLabels,
  halfHouseLabel,
  selected,
  endpoints,
  onDayClick,
}: {
  days: DayCell[]
  dowLabels: string[]
  statusLabels: Record<string, string>
  halfHouseLabel: string
  selected: Set<string>
  endpoints: Set<string>
  onDayClick: (dateStr: string) => void
}) {
  const first = days[0]
  if (!first) return null
  const leadingBlanks = dowMonStart(first.date)

  return (
    <Box>
      <SimpleGrid cols={7} spacing={4}>
        {dowLabels.map((d, i) => (
          <Text key={i} size="xs" c="dimmed" ta="center">{asI18n(`${d}`)}</Text>
        ))}
        {Array.from({ length: leadingBlanks }).map((_, i) => (
          <Box key={`blank-${i}`} />
        ))}
        {days.map((d) => {
          const dateStr = berlinStr(d.date)
          const isEndpoint = endpoints.has(dateStr)
          const isSelected = selected.has(dateStr)
          const style = isEndpoint ? SELECT_BG : isSelected ? SELECT_MID : STATUS_BG[d.status]
          const dayNum = Number(dateStr.slice(8, 10))

          // Two half-house bookings sharing a day → split the cell with a
          // diagonal line (top-left = first booking, bottom-right = second).
          // 3+ half-house bookings on one day (data error) intentionally fall
          // back to the single collapsed colour — don't "fix" that into an
          // N-way split without a real requirement.
          const isSplit =
            !isEndpoint && !isSelected &&
            d.bookings.length === 2 && d.bookings.every((b) => b.halfHouse)
          const splitBg = isSplit
            ? `linear-gradient(135deg, ${STATUS_BG[d.bookings[0].status].bg} 0 49%, #FFFFFF 49% 51%, ${STATUS_BG[d.bookings[1].status].bg} 51% 100%)`
            : undefined

          const bookingLabel = (b: DayBooking) =>
            `${b.eventName ?? statusLabels[b.status]}${b.halfHouse ? ` (${halfHouseLabel})` : ''}`
          const tooltipLabel = isSplit ? (
            <Stack gap={0}>
              <Text size="xs" fw={600}>{asI18n(`${dateStr}`)}</Text>
              {d.bookings.map((b, i) => (
                <Text key={i} size="xs">{asI18n(`${bookingLabel(b)}`)}</Text>
              ))}
            </Stack>
          ) : d.eventName
            ? asI18n(`${dateStr} — ${d.eventName}${d.halfHouse ? ` (${halfHouseLabel})` : ''}`)
            : asI18n(`${dateStr} — ${statusLabels[d.status]}${d.halfHouse ? ` (${halfHouseLabel})` : ''}`)

          return (
            <Tooltip key={dateStr} label={tooltipLabel} withArrow>
              <Box
                style={{
                  background: splitBg ?? style.bg,
                  color: isSplit ? '#4E3149' : style.fg,
                  borderRadius: 4,
                  textAlign: 'center',
                  padding: '6px 0',
                  fontSize: 13,
                  fontWeight: isEndpoint ? 700 : undefined,
                  boxShadow: isSelected && !isEndpoint ? 'inset 0 0 0 1px #88557F' : undefined,
                  cursor: d.status === 'free' ? 'pointer' : 'default',
                  position: 'relative',
                }}
                onClick={() => {
                  if (d.status === 'free') onDayClick(dateStr)
                }}
              >
                <span style={isSplit ? { textShadow: '0 0 3px rgba(255,255,255,0.95)' } : undefined}>
                  {dayNum}
                </span>
                {/* Single half-house booking keeps the corner dot; a split cell
                    already communicates the half-house state visually. */}
                {d.halfHouse && !isSplit && (
                  <Box
                    style={{
                      position: 'absolute',
                      top: 2,
                      right: 4,
                      width: 6,
                      height: 6,
                      borderRadius: 3,
                      background: 'currentColor',
                      opacity: 0.5,
                    }}
                  />
                )}
              </Box>
            </Tooltip>
          )
        })}
      </SimpleGrid>
    </Box>
  )
}

const THIS_YEAR = new Date().getFullYear()
const YEAR_OPTIONS = [THIS_YEAR, THIS_YEAR + 1, THIS_YEAR + 2].map((y) => ({
  value: String(y),
  label: String(y),
}))

function AvailabilityPage() {
  useLocale()
  const navigate = useNavigate()
  const [year, setYear] = useState(THIS_YEAR)
  // Two-click range selection: first free day sets `start`, second sets `end`.
  const [sel, setSel] = useState<{ start: string; end: string | null } | null>(null)

  const from = `${year}-01-01`
  const to = `${year}-12-31`

  const { data, isLoading, error } = usePikkuQuery('getAvailability', { venueSlug: 'drawehn', from, to })

  // Lookup of day status by Berlin-local date string, for validating ranges.
  const statusByDate = useMemo(() => {
    const m = new Map<string, DayCell['status']>()
    for (const d of (data?.days as DayCell[] | undefined) ?? []) {
      m.set(berlinStr(d.date), d.status)
    }
    return m
  }, [data])

  const handleDayClick = (ds: string) => {
    setSel((prev) => {
      // No selection yet, or a completed range → start fresh.
      if (!prev || prev.end) return { start: ds, end: null }
      // Re-click the start → deselect.
      if (ds === prev.start) return null
      // Earlier than start → move the start.
      if (ds < prev.start) return { start: ds, end: null }
      // Later than start → close the range only if every day in it is free.
      const allFree = rangeDates(prev.start, ds).every((x) => statusByDate.get(x) === 'free')
      return allFree ? { start: prev.start, end: ds } : { start: ds, end: null }
    })
  }

  const selectedDates = useMemo(() => {
    if (!sel) return new Set<string>()
    return new Set(rangeDates(sel.start, sel.end ?? sel.start))
  }, [sel])
  const endpointDates = useMemo(() => {
    if (!sel) return new Set<string>()
    return new Set([sel.start, sel.end ?? sel.start])
  }, [sel])

  const months = useMemo(() => (data ? groupByMonth(data.days as DayCell[]) : []), [data])
  const stats = useMemo(() => {
    if (!data) return { free: 0, total: 0 }
    const total = data.days.length
    const free = data.days.filter((d) => d.status === 'free').length
    return { free, total }
  }, [data])

  // Localised mon-start day-of-week labels.
  const dowLabels = useMemo(() => {
    const fmt = new Intl.DateTimeFormat(getLocale(), { weekday: 'short' })
    // 2024-01-01 was a Monday — convenient anchor.
    return Array.from({ length: 7 }, (_, i) => fmt.format(new Date(Date.UTC(2024, 0, 1 + i))))
  }, [getLocale()])

  if (isLoading) return <MarketingShell><Container py="xl"><Text>{m.common__states__loading()}</Text></Container></MarketingShell>
  if (error) return <MarketingShell><Container py="xl"><Text c="red">{m.common__states__error()}</Text></Container></MarketingShell>
  if (!data) return null

  const statusLabels: Record<string, string> = {
    free: m.common__pages__availability__status__free(),
    enquiry: m.common__pages__availability__status__enquiry(),
    reserved: m.common__pages__availability__status__reserved(),
    confirmed: m.common__pages__availability__status__confirmed(),
  }
  const halfHouseLabel = m.common__pages__availability__status__half_house()

  // `enquiry` is only surfaced to staff (the backend hides it from anonymous
  // viewers), so only show its legend swatch when such a day is actually
  // present in the data.
  const hasEnquiry = (data.days as DayCell[]).some((d) => d.status === 'enquiry')
  const legendKeys = hasEnquiry ? STATUS_KEYS : STATUS_KEYS.filter((k) => k !== 'enquiry')

  const fmtDate = (ds: string) =>
    new Date(`${ds}T00:00:00Z`).toLocaleDateString(getLocale(), {
      day: 'numeric',
      month: 'short',
      year: 'numeric',
      timeZone: 'UTC',
    })
  const selStart = sel?.start ?? null
  const selEnd = sel?.end ?? null
  const rangeLabel = selStart
    ? selEnd && selEnd !== selStart
      ? `${fmtDate(selStart)} – ${fmtDate(selEnd)}`
      : fmtDate(selStart)
    : ''
  const createEnquiry = () => {
    if (!selStart) return
    navigate({ to: '/enquiry', search: { from: selStart, to: selEnd ?? selStart } })
  }

  return (
    <MarketingShell>
    <Container size="lg" py="xl">
      <Stack gap="lg">
        <PageTitle title={m.common__pages__availability__title()} />
        <Group align="flex-end" gap="md">
          <Select
            label={m.common__pages__availability__year_label()}
            data={YEAR_OPTIONS}
            value={String(year)}
            onChange={(v) => {
              if (v) {
                setYear(Number(v))
                setSel(null)
              }
            }}
            allowDeselect={false}
            w={120}
          />
          <Text size="sm" pb={6}>
            {m.common__pages__availability__free_stat({
              free: stats.free,
              total: stats.total,
              percent: Math.round((stats.free / stats.total) * 100),
            })}
          </Text>
        </Group>

        <Group gap="md">
          {legendKeys.map((key) => (
            <Group key={key} gap={6} align="center">
              <Box w={14} h={14} style={{ background: STATUS_BG[key].bg, borderRadius: 3 }} />
              <Text size="xs">{asI18n(`${statusLabels[key]}`)}</Text>
            </Group>
          ))}
        </Group>

        <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="lg">
          {months.map((month) => (
            <Paper key={month.key} withBorder radius="md" p="md">
              <Stack gap="sm">
                <Group justify="space-between">
                  <Title order={4} tt="capitalize">{asI18n(`${monthLabel(month.key, getLocale())}`)}</Title>
                  <Badge variant="light">
                    {m.common__pages__availability__free_month_badge({
                      count: month.days.filter((d) => d.status === 'free').length,
                    })}
                  </Badge>
                </Group>
                <MonthGrid
                  days={month.days}
                  dowLabels={dowLabels}
                  statusLabels={statusLabels}
                  halfHouseLabel={halfHouseLabel}
                  selected={selectedDates}
                  endpoints={endpointDates}
                  onDayClick={handleDayClick}
                />
              </Stack>
            </Paper>
          ))}
        </SimpleGrid>

        {selStart && (
          <Paper
            withBorder
            shadow="md"
            radius="md"
            p="md"
            pos="sticky"
            bottom={16}
            style={{ zIndex: 2 }}
          >
            <Group justify="space-between" wrap="wrap" gap="sm">
              <Stack gap={2}>
                <Text fw={600}>
                  {m.common__pages__availability__selected_range({ range: rangeLabel })}
                </Text>
                {!selEnd && (
                  <Text size="xs" c="dimmed">
                    {m.common__pages__availability__select_end_hint()}
                  </Text>
                )}
              </Stack>
              <Group gap="xs">
                <Button variant="subtle" color="gray" onClick={() => setSel(null)}>
                  {m.common__pages__availability__clear_selection()}
                </Button>
                <Button data-testid="create-enquiry" onClick={createEnquiry}>
                  {m.common__pages__availability__create_enquiry()}
                </Button>
              </Group>
            </Group>
          </Paper>
        )}

        <Text size="xs" c="dimmed" ta="center">
          {m.common__pages__availability__footnote()}
        </Text>
      </Stack>
    </Container>
    </MarketingShell>
  )
}

export const Route = createFileRoute('/availability')({
  component: AvailabilityPage,
})
