import { Search } from 'lucide-solid'
import { For, Show, createEffect, createMemo, createResource, createSignal, on } 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 { Skeleton } from '@/components/ui/skeleton'
import { Tabs } from '@/components/ui/tabs'
import { discoverService } from '@/services/discover-service'
import { genres } from '@/services/mock-data'
import { searchService } from '@/services/search-service'
import type { DiscoverSection } from '@/types/domain'
import { mediaBadgeVariant, mediaTypeLabel } from '@/utils/media'
import { cn } from '@/utils/cn'

const PAGE_SIZE = 4
const DEFAULT_VISIBLE_GENRES = 6

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

const mergeSections = (current: DiscoverSection[], incoming: DiscoverSection[]): DiscoverSection[] => {
  const incomingByKind = new Map(incoming.map((section) => [section.kind, section]))

  const mergedCurrent = current.map((section) => {
    const nextSection = incomingByKind.get(section.kind)

    if (!nextSection) {
      return section
    }

    const knownIds = new Set(section.items.map((item) => item.id))
    const additionalItems = nextSection.items.filter((item) => !knownIds.has(item.id))

    return {
      ...nextSection,
      items: [...section.items, ...additionalItems],
    }
  })

  const appendedSections = incoming.filter(
    (section) => !current.some((currentSection) => currentSection.kind === section.kind),
  )

  return [...mergedCurrent, ...appendedSections]
}

const FilterChip = (props: { active: boolean; onClick: () => void; children: string }) => (
  <button
    type="button"
    class={cn(
      'rounded-lg px-3 py-1.5 text-xs font-medium transition-all duration-200',
      props.active
        ? 'bg-primary/15 text-primary'
        : 'bg-surface-low text-muted-fg hover:text-fg hover:bg-surface-container',
    )}
    onClick={props.onClick}
  >
    {props.children}
  </button>
)

const discoverScopeLabel = (value: 'all' | 'movie' | 'show' | 'game'): string =>
  value === 'all' ? 'All media' : mediaTypeLabel(value)

export const DiscoverPage = () => {
  const [query, setQuery] = createSignal('')
  const [genre, setGenre] = createSignal<string>('all')
  const [mediaType, setMediaType] = createSignal<'all' | 'movie' | 'show' | 'game'>('all')
  const [page, setPage] = createSignal(1)
  const [sections, setSections] = createSignal<DiscoverSection[]>([])
  const [showAllGenres, setShowAllGenres] = createSignal(false)

  const params = createMemo(() => ({
    page: page(),
    pageSize: PAGE_SIZE,
    query: query().trim(),
    genre: genre() === 'all' ? undefined : genre(),
    mediaType: mediaType(),
  }))

  const [fetchedSections, { refetch: refetchSections }] = createResource(params, (next) =>
    discoverService.getSections(next),
  )

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

      return searchService.search(term, {
        genre: genre() === 'all' ? undefined : genre(),
        mediaType: mediaType(),
      })
    },
  )

  createEffect(
    on(
      () => [query().trim(), genre(), mediaType()],
      () => {
        setPage(1)
        setSections([])
      },
      { defer: true },
    ),
  )

  createEffect(() => {
    const incoming = fetchedSections()

    if (!incoming) {
      return
    }

    if (page() === 1) {
      setSections(incoming)
      return
    }

    setSections((current) => mergeSections(current, incoming))
  })

  const visibleGenres = createMemo(() =>
    showAllGenres() ? genres : genres.slice(0, DEFAULT_VISIBLE_GENRES),
  )
  const queryActive = createMemo(() => query().trim().length >= 2)
  const searchLead = createMemo(() => searchResults()?.[0] ?? null)
  const searchRest = createMemo(() => (searchResults() ?? []).slice(1, 4))
  const canLoadMore = createMemo(() =>
    (sections().length > 0 || fetchedSections.loading) && !queryActive(),
  )

  return (
    <section class="space-y-8" data-testid="discover-page">
      <div class="space-y-6">
        <div>
          <h1 class="text-2xl font-semibold text-fg">Discover</h1>
          <p class="text-sm text-muted-fg mt-1">Find your next watch or play</p>
        </div>

        <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          <div class="relative w-full sm:w-80">
            <Search class="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-fg" />
            <Input
              aria-label="Discover search"
              placeholder="Search titles..."
              value={query()}
              onInput={(event) => setQuery(event.currentTarget.value)}
              class="h-10 pl-10"
            />
          </div>

          <div class="flex items-center gap-2">
            <Tabs
              label="Media type"
              value={mediaType()}
              onChange={(value) => setMediaType(value as typeof mediaTypeOptions[number]['value'])}
              options={mediaTypeOptions.map((option) => ({ value: option.value, label: option.label }))}
            />
            <Show when={queryActive()}>
              <Button variant="ghost" size="sm" onClick={() => setQuery('')}>
                Clear
              </Button>
            </Show>
          </div>
        </div>

        <div class="flex flex-wrap gap-2">
          <FilterChip active={genre() === 'all'} onClick={() => setGenre('all')}>
            All
          </FilterChip>
          <For each={visibleGenres()}>
            {(genreName) => (
              <FilterChip active={genre() === genreName} onClick={() => setGenre(genreName)}>
                {genreName}
              </FilterChip>
            )}
          </For>
          <Show when={genres.length > DEFAULT_VISIBLE_GENRES && !showAllGenres()}>
            <Button variant="ghost" size="sm" onClick={() => setShowAllGenres(true)}>
              More
            </Button>
          </Show>
        </div>
      </div>

      <Show when={queryActive()}>
        <Card>
          <CardContent class="space-y-5 pt-6" data-testid="discover-search-results">
            <div class="flex items-center justify-between">
              <div>
                <h2 class="text-lg font-semibold text-fg">Search Results</h2>
                <p class="text-sm text-muted-fg mt-1">Found {(searchResults() ?? []).length} matches</p>
              </div>
              <Badge variant="accent">{discoverScopeLabel(mediaType())}</Badge>
            </div>

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

            <Show
              when={searchLead()}
              fallback={
                <Show when={!searchResults.loading}>
                  <EmptyState
                    title="Nothing matched"
                    description="Try a different search term."
                  />
                </Show>
              }
            >
              {(lead) => (
                <div class="grid gap-5 lg:grid-cols-[1fr_280px]">
                  <MediaCard
                    item={{
                      id: lead().id,
                      artworkKey: lead().title,
                      genres: lead().genres,
                      overview: lead().subtitle,
                      platforms: [],
                      provider: 'tmdb',
                      providerId: lead().id,
                      rating: Math.max(7, Math.min(9.5, lead().score / 10)),
                      releaseDate: '2026-03-12',
                      runtimeMinutes: 120,
                      title: lead().title,
                      type: lead().mediaType,
                    }}
                    subtitle={lead().subtitle}
                  />

                  <div class="space-y-3">
                    <For each={searchRest()}>
                      {(result) => (
                        <DataRow
                          tone={mediaBadgeVariant(result.mediaType)}
                          eyebrow={`Score ${result.score}`}
                          title={result.title}
                          description={result.subtitle}
                          badges={
                            <Badge variant={mediaBadgeVariant(result.mediaType)}>
                              {mediaTypeLabel(result.mediaType)}
                            </Badge>
                          }
                        />
                      )}
                    </For>
                  </div>
                </div>
              )}
            </Show>
          </CardContent>
        </Card>
      </Show>

      <Show when={fetchedSections.error}>
        <div class="flex items-center justify-between rounded-xl bg-danger/15 px-4 py-3 text-sm text-danger">
          <span>{fetchedSections.error.message}</span>
          <Button variant="secondary" size="sm" onClick={() => refetchSections()}>
            Retry
          </Button>
        </div>
      </Show>

      <Show when={!queryActive()}>
        <For each={sections().slice(0, 3)}>
          {(section) => (
            <Card>
              <CardContent class="space-y-5 pt-6">
                <div class="flex items-center justify-between">
                  <div>
                    <h2 class="text-lg font-semibold text-fg">{section.title}</h2>
                    <p class="text-sm text-muted-fg mt-1">{section.subtitle}</p>
                  </div>
                  <Badge variant="neutral">{section.items.length}</Badge>
                </div>

                <div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
                  <For each={section.items}>
                    {(item) => <MediaCard item={item} subtitle={`${mediaTypeLabel(item.type)} · ${item.genres.slice(0, 2).join(' • ')}`} />}
                  </For>
                </div>
              </CardContent>
            </Card>
          )}
        </For>
      </Show>

      <Show when={fetchedSections.loading && sections().length === 0}>
        <div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
          <For each={Array.from({ length: 4 }, (_, index) => index)}>{() => <Skeleton class="h-64" />}</For>
        </div>
      </Show>

      <Show when={sections().length === 0 && !fetchedSections.loading && !fetchedSections.error && !queryActive()}>
        <EmptyState
          title="Nothing to show"
          description="Try a different filter."
        />
      </Show>

      <Show when={canLoadMore()}>
        <div class="flex justify-center">
          <Button
            variant="secondary"
            onClick={() => setPage((value) => value + 1)}
            disabled={fetchedSections.loading}
          >
            {fetchedSections.loading ? 'Loading…' : 'Load More'}
          </Button>
        </div>
      </Show>
    </section>
  )
}
