import { BookmarkPlus, Clock3, History, Star } from 'lucide-solid'
import { For, Show, createMemo, createResource, createSignal } from 'solid-js'
import { MediaCard } from '@/components/media/media-card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { DataRow } from '@/components/ui/data-row'
import { EmptyState } from '@/components/ui/empty-state'
import { MetricCard } from '@/components/ui/metric-card'
import { SectionHeading } from '@/components/ui/section-heading'
import { Skeleton } from '@/components/ui/skeleton'
import { Tabs } from '@/components/ui/tabs'
import { dashboardService } from '@/services/dashboard-service'
import { watchLaterService } from '@/services/watch-later-service'
import { useAuth } from '@/stores/auth-store'
import type { MediaItem, MediaType } from '@/types/domain'
import { formatDate, formatRating } from '@/utils/format'
import { mediaBadgeVariant, mediaTypeLabel } from '@/utils/media'

type HistoryScope = 'all' | MediaType

interface CompletionEntry {
  item: MediaItem
  watchedAt: string
  personalScore: number
  summary: string
}

const scopeOptions = [
  { value: 'all', label: 'All' },
  { value: 'movie', label: 'Movies' },
  { value: 'show', label: 'Shows' },
  { value: 'game', label: 'Games' },
] as const

const completionOffsets = [1, 3, 5, 8, 12, 16, 21, 27]
const scoreAdjustments = [0.4, 0.1, 0.3, 0.2, -0.1, 0.2, 0.1, 0.4]

const clampScore = (value: number): number => Math.max(6.5, Math.min(9.9, Number(value.toFixed(1))))

const completionSummary = (item: MediaItem): string =>
  item.type === 'movie'
    ? 'Closed cleanly and worth keeping in your replay lane.'
    : item.type === 'show'
      ? 'Finished a strong run of episodes and filed it for an easy revisit.'
      : 'Campaign wrapped with enough momentum to stay in the backlog conversation.'

const relativeTimeLabel = (isoDate: string): string => {
  const diffMs = Math.max(0, Date.now() - new Date(isoDate).getTime())
  const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
  const diffDays = Math.floor(diffHours / 24)

  if (diffHours < 24) {
    return diffHours <= 1 ? '1 hour ago' : `${diffHours} hours ago`
  }

  if (diffDays === 1) {
    return 'Yesterday'
  }

  if (diffDays < 14) {
    return `${diffDays} days ago`
  }

  return formatDate(isoDate)
}

const buildCompletionEntries = (items: MediaItem[]): CompletionEntry[] =>
  items
    .map((item, index) => {
      const watchedAt = new Date()
      watchedAt.setDate(watchedAt.getDate() - (completionOffsets[index] ?? (index + 1) * 4))
      watchedAt.setHours(item.type === 'game' ? 22 : 21 - (index % 2), 10 + ((index * 11) % 35), 0, 0)

      return {
        item,
        watchedAt: watchedAt.toISOString(),
        personalScore: clampScore(item.rating + (scoreAdjustments[index] ?? 0)),
        summary: completionSummary(item),
      }
    })
    .sort((left, right) => new Date(right.watchedAt).getTime() - new Date(left.watchedAt).getTime())

export const WatchedPage = () => {
  const auth = useAuth()
  const [scope, setScope] = createSignal<HistoryScope>('all')
  const [busyMediaId, setBusyMediaId] = createSignal<number | null>(null)
  const [actionError, setActionError] = createSignal<string | null>(null)
  const [reloadToken, setReloadToken] = createSignal(0)

  const [dashboardData, { refetch: refetchDashboard }] = createResource(reloadToken, () =>
    dashboardService.getDashboard(),
  )

  const [queueData, { refetch: refetchQueue }] = createResource(reloadToken, async () => {
    const accessToken = auth.accessToken()

    if (!accessToken) {
      return []
    }

    return watchLaterService.getWatchLater(accessToken)
  })

  const completionEntries = createMemo(() => buildCompletionEntries(dashboardData()?.recentlyWatched ?? []))
  const queueIds = createMemo(() => new Set((queueData() ?? []).map((item) => item.id)))
  const filteredEntries = createMemo(() =>
    scope() === 'all'
      ? completionEntries()
      : completionEntries().filter((entry) => entry.item.type === scope()),
  )
  const averageScore = createMemo(() => {
    const entries = filteredEntries()

    if (entries.length === 0) {
      return null
    }

    return formatRating(entries.reduce((total, entry) => total + entry.personalScore, 0) / entries.length)
  })

  const toggleReplayQueue = async (mediaId: number): Promise<void> => {
    const accessToken = auth.accessToken()

    if (!accessToken) {
      setActionError('Your session expired. Please sign in again.')
      return
    }

    setActionError(null)
    setBusyMediaId(mediaId)

    try {
      if (queueIds().has(mediaId)) {
        await watchLaterService.removeWatchLater(accessToken, mediaId)
      } else {
        await watchLaterService.addWatchLater(accessToken, mediaId)
      }

      setReloadToken((value) => value + 1)
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Unable to update replay queue'
      setActionError(message)
    } finally {
      setBusyMediaId(null)
    }
  }

  return (
    <section class="space-y-6" data-testid="watched-page">
      <Card class="animate-stagger">
        <CardContent class="space-y-6 pt-6">
          <SectionHeading
            eyebrow="Watched"
            title="A clean ledger of what you finished and what deserves a replay."
            description="History stays readable, with queue actions available only when you need them."
            badge={<Badge variant="neutral">{filteredEntries().length} entries</Badge>}
          />

          <div class="flex flex-wrap gap-3">
            <Tabs
              label="History scope"
              value={scope()}
              onChange={(value) => setScope(value as HistoryScope)}
              options={scopeOptions.map((option) => ({ value: option.value, label: option.label }))}
            />
          </div>

          <div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
            <MetricCard
              icon={History}
              label="Completed"
              value={`${filteredEntries().length}`}
              detail="logged finishes"
              tone="accent"
            />
            <MetricCard
              icon={Star}
              label="Average score"
              value={averageScore() ? `${averageScore()}` : '0.0'}
              detail="personal rating"
            />
            <MetricCard
              icon={BookmarkPlus}
              label="Queued again"
              value={`${filteredEntries().filter((entry) => queueIds().has(entry.item.id)).length}`}
              detail="replay candidates saved"
              tone="secondary"
            />
            <MetricCard
              icon={Clock3}
              label="Latest finish"
              value={filteredEntries()[0] ? relativeTimeLabel(filteredEntries()[0]!.watchedAt) : 'No history'}
              detail="most recent completion"
            />
          </div>

          <Show when={actionError()}>
            <div class="rounded-[1.4rem] border border-secondary/24 bg-secondary/10 px-4 py-3 text-sm text-fg">
              {actionError()}
            </div>
          </Show>
        </CardContent>
      </Card>

      <div class="grid gap-6 xl:grid-cols-[minmax(0,1.05fr)_380px]">
        <Card>
          <CardContent class="space-y-5 pt-6">
            <SectionHeading
              title="History"
              description="The most recent finishes across your selected scope."
              badge={<Badge variant="accent">{filteredEntries().length} logged</Badge>}
            />

            <Show when={dashboardData.loading && filteredEntries().length === 0}>
              <div class="space-y-3">
                <For each={Array.from({ length: 4 }, (_, index) => index)}>{() => <Skeleton class="h-24" />}</For>
              </div>
            </Show>

            <Show
              when={filteredEntries().length > 0}
              fallback={
                <EmptyState
                  title="No history yet"
                  description="Finished titles will appear here once your activity starts flowing."
                />
              }
            >
              <div class="space-y-3">
                <For each={filteredEntries()}>
                  {(entry) => (
                    <DataRow
                      tone={mediaBadgeVariant(entry.item.type)}
                      eyebrow={relativeTimeLabel(entry.watchedAt)}
                      title={entry.item.title}
                      description={entry.summary}
                      meta={`${formatDate(entry.watchedAt)} · Personal score ${formatRating(entry.personalScore)}`}
                      badges={
                        <Badge variant={mediaBadgeVariant(entry.item.type)}>
                          {mediaTypeLabel(entry.item.type)}
                        </Badge>
                      }
                      trailing={
                        <Button
                          size="sm"
                          variant={queueIds().has(entry.item.id) ? 'secondary' : 'primary'}
                          disabled={busyMediaId() === entry.item.id}
                          onClick={() => void toggleReplayQueue(entry.item.id)}
                        >
                          {busyMediaId() === entry.item.id
                            ? 'Saving…'
                            : queueIds().has(entry.item.id)
                              ? 'Queued'
                              : 'Replay'}
                        </Button>
                      }
                    />
                  )}
                </For>
              </div>
            </Show>
          </CardContent>
        </Card>

        <Card>
          <CardContent class="space-y-5 pt-6">
            <SectionHeading
              title="Replay Candidates"
              description="Strong recent finishes you might want to pull back into the queue."
              badge={<Badge variant="secondary">{filteredEntries().slice(0, 3).length} surfaced</Badge>}
            />

            <Show
              when={filteredEntries().length > 0}
              fallback={
                <EmptyState
                  title="No replay candidates"
                  description="Replay suggestions will show up after you finish a few more titles."
                />
              }
            >
              <div class="space-y-4">
                <For each={filteredEntries().slice(0, 3)}>
                  {(entry) => (
                    <div class="space-y-3">
                      <MediaCard item={entry.item} subtitle={entry.summary} />
                      <Button
                        size="sm"
                        variant={queueIds().has(entry.item.id) ? 'secondary' : 'primary'}
                        disabled={busyMediaId() === entry.item.id}
                        onClick={() => void toggleReplayQueue(entry.item.id)}
                      >
                        {busyMediaId() === entry.item.id
                          ? 'Saving…'
                          : queueIds().has(entry.item.id)
                            ? 'Remove replay'
                            : 'Queue replay'}
                      </Button>
                    </div>
                  )}
                </For>
              </div>
            </Show>
          </CardContent>
        </Card>
      </div>

      <Show when={dashboardData.error || queueData.error}>
        <div class="rounded-[1.5rem] border border-secondary/24 bg-secondary/10 px-4 py-4 text-sm text-fg">
          {dashboardData.error?.message ?? queueData.error?.message ?? 'Unable to load watched history.'}
          <Button
            variant="secondary"
            size="sm"
            class="ml-3"
            onClick={() => {
              refetchDashboard()
              refetchQueue()
            }}
          >
            Retry
          </Button>
        </div>
      </Show>
    </section>
  )
}
