import { Clock3, Film, Gamepad2, ListVideo, Search, Tv } from 'lucide-solid'
import { For, Show, createMemo, createResource, createSignal, type Component } 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 { Input } from '@/components/ui/input'
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 { searchService } from '@/services/search-service'
import { watchLaterService } from '@/services/watch-later-service'
import { useAuth } from '@/stores/auth-store'
import type { MediaType, SearchResult } from '@/types/domain'
import { formatDate } from '@/utils/format'
import { mediaBadgeVariant, mediaTypeLabel } from '@/utils/media'

type QueueScope = 'all' | MediaType
type IconComponent = Component<{ class?: string; size?: number | string }>

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

const mediaIcon = (value: MediaType): IconComponent =>
  value === 'movie' ? Film : value === 'show' ? Tv : Gamepad2

const SearchResultRow = (props: {
  result: SearchResult
  queued: boolean
  busy: boolean
  onToggle: () => void
}) => (
  <DataRow
    tone={props.queued ? 'secondary' : 'accent'}
    eyebrow={`Score ${props.result.score}`}
    title={props.result.title}
    description={props.result.subtitle}
    badges={<Badge variant={mediaBadgeVariant(props.result.mediaType)}>{mediaTypeLabel(props.result.mediaType)}</Badge>}
    trailing={
      <Button size="sm" variant={props.queued ? 'secondary' : 'primary'} disabled={props.busy} onClick={props.onToggle}>
        {props.busy ? 'Saving…' : props.queued ? 'Remove' : 'Queue'}
      </Button>
    }
  />
)

const queueScopeLabel = (value: QueueScope): string =>
  value === 'all' ? 'all media' : mediaTypeLabel(value).toLowerCase()

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

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

    if (!accessToken) {
      return []
    }

    return watchLaterService.getWatchLater(accessToken)
  })

  const [searchResults] = createResource(
    () => [query().trim(), scope()] as const,
    async ([term, currentScope]) => {
      if (term.length < 2) {
        return []
      }

      return searchService.search(term, { mediaType: currentScope })
    },
  )

  const queueItems = createMemo(() => watchLaterData() ?? [])
  const queueIds = createMemo(() => new Set(queueItems().map((item) => item.id)))
  const filteredQueueItems = createMemo(() =>
    scope() === 'all' ? queueItems() : queueItems().filter((item) => item.type === scope()),
  )
  const scopeCounts = createMemo(() => ({
    movie: queueItems().filter((item) => item.type === 'movie').length,
    show: queueItems().filter((item) => item.type === 'show').length,
    game: queueItems().filter((item) => item.type === 'game').length,
  }))
  const queueHours = createMemo(() =>
    Math.round(queueItems().reduce((total, item) => total + item.runtimeMinutes, 0) / 60),
  )
  const queryActive = createMemo(() => query().trim().length >= 2)
  const groupedQueue = createMemo(() =>
    (['movie', 'show', 'game'] as const)
      .map((type) => ({
        type,
        items: queueItems().filter((item) => item.type === type),
      }))
      .filter((group) => group.items.length > 0),
  )

  const refresh = (): void => {
    setReloadToken((value) => value + 1)
  }

  const toggleQueue = 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)
      }

      refresh()
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Unable to update the queue'
      setActionError(message)
    } finally {
      setBusyMediaId(null)
    }
  }

  return (
    <section class="space-y-6" data-testid="watch-later-page">
      <Card class="animate-stagger">
        <CardContent class="space-y-6 pt-6">
          <SectionHeading
            eyebrow="Queue"
            title="Keep the next few titles close and drop the rest of the noise."
            description="This queue is shared across movies, shows, and games, but the surface stays compact."
            badge={<Badge variant="neutral">{queueItems().length} saved</Badge>}
          />

          <div class="grid gap-4 xl:grid-cols-[minmax(0,1fr)_300px]">
            <div class="space-y-4">
              <div class="relative">
                <Search class="pointer-events-none absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-fg" />
                <Input
                  aria-label="Queue search"
                  placeholder="Search the catalog and save titles directly into the queue"
                  value={query()}
                  onInput={(event) => setQuery(event.currentTarget.value)}
                  class="h-12 pl-11"
                />
              </div>

              <Tabs
                label="Queue scope"
                value={scope()}
                onChange={(value) => setScope(value as QueueScope)}
                options={scopeOptions.map((option) => ({ value: option.value, label: option.label }))}
              />
            </div>

            <div class="rounded-[1.5rem] border border-border bg-muted/45 p-4">
              <p class="text-[11px] font-semibold uppercase tracking-[0.24em] text-muted-fg">Queue summary</p>
              <p class="mt-2 text-sm text-fg">
                {filteredQueueItems().length > 0
                  ? `${filteredQueueItems().length} ${scope() === 'all' ? 'titles' : queueScopeLabel(scope())} visible right now.`
                  : 'Nothing in the current scope yet.'}
              </p>
              <p class="mt-2 text-sm text-muted-fg">
                Total queue depth is about {queueItems().length > 0 ? `${Math.max(1, queueHours())} hours` : '0 hours'}.
              </p>
            </div>
          </div>

          <div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
            <MetricCard
              icon={ListVideo}
              label="Saved now"
              value={`${queueItems().length}`}
              detail="titles in the queue"
              tone="accent"
            />
            <MetricCard
              icon={Clock3}
              label="Depth"
              value={queueItems().length > 0 ? `${Math.max(1, queueHours())}h` : '0h'}
              detail="estimated queue time"
            />
            <MetricCard
              icon={Film}
              label="Movies"
              value={`${scopeCounts().movie}`}
              detail="saved films"
              tone="secondary"
            />
            <MetricCard
              icon={Gamepad2}
              label="Games"
              value={`${scopeCounts().game}`}
              detail="saved games"
            />
          </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.04fr)_380px]">
        <div class="space-y-6">
          <Card>
            <CardContent class="space-y-5 pt-6">
              <SectionHeading
                title="Saved in Queue"
                description="The titles currently sitting in this lane."
                badge={<Badge variant="accent">{filteredQueueItems().length} visible</Badge>}
              />

              <Show when={watchLaterData.loading && filteredQueueItems().length === 0}>
                <div class="grid gap-4 md:grid-cols-2">
                  <For each={Array.from({ length: 4 }, (_, index) => index)}>{() => <Skeleton class="h-[280px]" />}</For>
                </div>
              </Show>

              <Show
                when={filteredQueueItems().length > 0}
                fallback={
                  <EmptyState
                    title="Queue is empty"
                    description="Search above to add movies, shows, or games into the shared queue."
                  />
                }
              >
                <div class="grid gap-4 md:grid-cols-2">
                  <For each={filteredQueueItems().slice(0, 6)}>
                    {(item) => (
                      <div class="space-y-3">
                        <MediaCard item={item} />
                        <Button
                          size="sm"
                          variant="secondary"
                          disabled={busyMediaId() === item.id}
                          onClick={() => void toggleQueue(item.id)}
                        >
                          {busyMediaId() === item.id ? 'Saving…' : 'Remove from queue'}
                        </Button>
                      </div>
                    )}
                  </For>
                </div>
              </Show>
            </CardContent>
          </Card>

          <Show when={!queryActive()}>
            <Card>
              <CardContent class="space-y-5 pt-6">
                <SectionHeading
                  title="Queue by Type"
                  description="A compact breakdown of what is actually waiting."
                  badge={<Badge variant="neutral">{groupedQueue().length} active groups</Badge>}
                />

                <div class="space-y-5">
                  <For each={groupedQueue()}>
                    {(group) => {
                      const Icon = mediaIcon(group.type)

                      return (
                        <div class="space-y-3">
                          <div class="flex items-center gap-3">
                            <div class="flex h-9 w-9 items-center justify-center rounded-2xl border border-border bg-card text-fg">
                              <Icon class="h-4 w-4" />
                            </div>
                            <div>
                              <p class="text-sm font-semibold text-fg">{mediaTypeLabel(group.type)}</p>
                              <p class="text-xs text-muted-fg">{group.items.length} saved</p>
                            </div>
                          </div>

                          <div class="space-y-3">
                            <For each={group.items.slice(0, 3)}>
                              {(item) => (
                                <DataRow
                                  tone={mediaBadgeVariant(item.type)}
                                  eyebrow={mediaTypeLabel(item.type)}
                                  title={item.title}
                                  description={item.genres.slice(0, 2).join(' • ')}
                                  meta={formatDate(item.releaseDate)}
                                />
                              )}
                            </For>
                          </div>
                        </div>
                      )
                    }}
                  </For>
                </div>
              </CardContent>
            </Card>
          </Show>
        </div>

        <div class="space-y-6">
          <Card>
            <CardContent class="space-y-5 pt-6">
              <SectionHeading
                title={queryActive() ? 'Search Results' : 'Search to Add'}
                description={
                  queryActive()
                    ? 'Save new titles directly from search results.'
                    : 'Start typing above to search the full catalog.'
                }
                badge={<Badge variant="neutral">{(searchResults() ?? []).length} matches</Badge>}
              />

              <Show when={searchResults.loading}>
                <p class="text-sm text-muted-fg">Searching…</p>
              </Show>

              <Show
                when={(searchResults() ?? []).length > 0}
                fallback={
                  <Show when={!searchResults.loading}>
                    <EmptyState
                      title={queryActive() ? 'Nothing matched' : 'Search is idle'}
                      description={
                        queryActive()
                          ? 'Try a broader search term or switch the scope.'
                          : 'Use the search field to bring new titles into the queue.'
                      }
                    />
                  </Show>
                }
              >
                <div class="space-y-3">
                  <For each={searchResults()}>
                    {(result) => (
                      <SearchResultRow
                        result={result}
                        queued={queueIds().has(result.id)}
                        busy={busyMediaId() === result.id}
                        onToggle={() => void toggleQueue(result.id)}
                      />
                    )}
                  </For>
                </div>
              </Show>
            </CardContent>
          </Card>
        </div>
      </div>

      <Show when={watchLaterData.error}>
        <div class="rounded-[1.5rem] border border-secondary/24 bg-secondary/10 px-4 py-4 text-sm text-fg">
          {watchLaterData.error.message}
          <Button variant="secondary" size="sm" class="ml-3" onClick={() => refetchQueue()}>
            Retry
          </Button>
        </div>
      </Show>
    </section>
  )
}
