import { useEffect, useMemo, useState } from 'react'
import {
  Text,
  Caption1,
  Button,
  Checkbox,
  Input,
  makeStyles,
  mergeClasses,
  tokens,
  shorthands
} from '@fluentui/react-components'
import {
  OpenRegular,
  FolderRegular,
  DeleteRegular,
  HistoryRegular,
  SearchRegular,
  ArrowClockwiseRegular,
  CheckmarkSquareRegular,
  DismissRegular
} from '@fluentui/react-icons'
import type { HistoryEntry, MediaKind } from '@shared/ipc'
import { useHistory } from '../store/history'
import { formatWhen } from '../lib/datetime'
import { useResolvedDark } from '../store/systemTheme'
import { useDownloads } from '../store/downloads'
import { thumbColors } from '../lib/theme'
import { thumbUrl } from '../lib/thumb'
import { MediaThumb } from '../components/MediaThumb'
import { Hint } from '../components/Hint'
import { Select } from '../components/Select'
import { VirtualList } from '../components/VirtualList'
import { ScreenHeader, useScreenStyles } from '../components/ui/Screen'
import { EmptyState } from '../components/ui/EmptyState'
import { useFocusStyles } from '../components/ui/focusRing'
import { useTextStyles } from '../components/ui/text'
import { SPACE, ICON, META_SEP } from '../components/ui/tokens'
import { THUMB_SM } from '../lib/thumbSizes'

const useStyles = makeStyles({
  root: {
    display: 'flex',
    flexDirection: 'column',
    gap: SPACE.section
  },
  // Fills the `fill` shell (L161) so the virtualized list below owns the scroll.
  fill: {
    flexGrow: 1,
    minHeight: 0
  },
  listScroll: {
    flexGrow: 1,
    // The floor keeps the virtualized list visible on a very short window (the
    // `fill` shell scrolls the overflow); above it, the list shrinks below its
    // content height so it, not the page, owns the scrolling (same contract as
    // DownloadsView).
    minHeight: '160px',
    display: 'flex',
    flexDirection: 'column'
  },
  header: {
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    flexWrap: 'wrap'
  },
  count: {
    color: tokens.colorNeutralForeground3,
    flexShrink: 0
  },
  search: {
    minWidth: '180px',
    flexGrow: 1,
    maxWidth: '320px'
  },
  kindFilter: {
    width: '130px',
    flexShrink: 0
  },
  spacer: {
    flexGrow: 1
  },
  row: {
    display: 'flex',
    alignItems: 'center',
    gap: '12px',
    // List-row padding tier (UI3): snug vertical, cozy horizontal.
    padding: `${SPACE.snug} ${SPACE.cozy}`,
    backgroundColor: tokens.colorNeutralBackground1,
    ...shorthands.borderRadius(tokens.borderRadiusLarge),
    border: `1px solid ${tokens.colorNeutralStroke2}`
  },
  thumb: {
    flexShrink: 0,
    width: `${THUMB_SM.w}px`,
    height: `${THUMB_SM.h}px`,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    ...shorthands.borderRadius(tokens.borderRadiusMedium)
  },
  body: {
    flexGrow: 1,
    minWidth: 0,
    display: 'flex',
    flexDirection: 'column'
  },
  actions: {
    display: 'flex',
    gap: '4px',
    flexShrink: 0,
    // >=40px hit target for the icon-only row actions (W16).
    '& button': {
      minWidth: '40px',
      minHeight: '40px'
    }
  },
  emptyBadge: {
    width: '56px',
    height: '56px',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: tokens.borderRadiusCircular,
    // Empty-state badge glyph, snapped to the nearest ICON tier (UI11).
    fontSize: `${ICON.section}px`
  },
  noMatches: {
    padding: '32px 16px',
    textAlign: 'center',
    color: tokens.colorNeutralForeground3
  }
})

const KIND_FILTER_OPTIONS = [
  { value: 'all', label: 'All' },
  { value: 'video', label: 'Video' },
  { value: 'audio', label: 'Audio' }
]

// Hoisted so the virtualizer gets stable function identities (PERF5). The row
// renderer itself stays a component-scoped closure — it needs select-mode state.
const estimateRowSize = (): number => 76
const getEntryKey = (h: HistoryEntry): string => h.id

export function HistoryView(): React.JSX.Element {
  const styles = useStyles()
  const screen = useScreenStyles()
  const focus = useFocusStyles()
  const text = useTextStyles()
  const isDark = useResolvedDark()
  const tc = thumbColors[isDark ? 'dark' : 'light']
  const entries = useHistory((s) => s.entries)
  const openFile = useHistory((s) => s.openFile)
  const showInFolder = useHistory((s) => s.showInFolder)
  const remove = useHistory((s) => s.remove)
  const removeMany = useHistory((s) => s.removeMany)
  const clear = useHistory((s) => s.clear)
  const addFromUrl = useDownloads((s) => s.addFromUrl)

  const [query, setQuery] = useState('')
  const [kindFilter, setKindFilter] = useState<'all' | MediaKind>('all')
  const [selectMode, setSelectMode] = useState(false)
  const [selected, setSelected] = useState<Set<string>>(new Set())
  const [confirmClear, setConfirmClear] = useState(false)
  const [confirmDelete, setConfirmDelete] = useState(false)

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase()
    return entries.filter((h) => {
      if (kindFilter !== 'all' && h.kind !== kindFilter) return false
      if (!q) return true
      return (
        h.title.toLowerCase().includes(q) ||
        (h.channel?.toLowerCase().includes(q) ?? false) ||
        h.url.toLowerCase().includes(q)
      )
    })
  }, [entries, query, kindFilter])

  // Clear selections that are no longer visible after a filter change (L86).
  useEffect(() => {
    if (selected.size === 0) return
    const visible = new Set(filtered.map((h) => h.id))
    setSelected((prev) => {
      const next = new Set([...prev].filter((id) => visible.has(id)))
      return next.size === prev.size ? prev : next
    })
  }, [filtered])

  function redownload(h: HistoryEntry): void {
    // Strip compound format labels ("720p · mp4 · 184 MB" → "720p") when there is
    // no stored formatId, so videoFormat() can still match the preset (H5).
    const quality = (h.quality.split(' · ')[0] ?? h.quality).trim()
    addFromUrl(h.url, h.kind, quality, {
      title: h.title,
      channel: h.channel,
      thumbnail: h.thumbnail,
      // Replay the original post-processing options (L87); undefined falls back to
      // the global defaults for entries recorded before this was carried.
      options: h.options,
      format: h.formatId
        ? { id: h.formatId, hasAudio: h.formatHasAudio ?? false, label: h.quality }
        : undefined
    })
  }

  function toggleSelected(id: string, on: boolean): void {
    setSelected((prev) => {
      const next = new Set(prev)
      if (on) next.add(id)
      else next.delete(id)
      return next
    })
  }

  const allFilteredSelected = filtered.length > 0 && filtered.every((h) => selected.has(h.id))
  function toggleAll(): void {
    setSelected(allFilteredSelected ? new Set() : new Set(filtered.map((h) => h.id)))
  }

  function exitSelectMode(): void {
    setSelectMode(false)
    setSelected(new Set())
    setConfirmDelete(false)
  }

  function deleteSelected(): void {
    removeMany([...selected])
    exitSelectMode()
  }

  // One history row; rendered through the virtualized list (L104). The per-row
  // Delete key handling stays on the row itself, so it works wherever the row is
  // mounted in the window.
  function renderRow(h: HistoryEntry): React.JSX.Element {
    return (
      <div
        className={mergeClasses(styles.row, focus.focusRing)}
        tabIndex={0}
        onKeyDown={(e) => {
          // Delete removes the focused row (not when a child button is focused).
          if (e.key === 'Delete' && e.target === e.currentTarget) {
            e.preventDefault()
            remove(h.id)
          }
        }}
      >
        {selectMode && (
          <Checkbox
            checked={selected.has(h.id)}
            onChange={(_, d) => toggleSelected(h.id, !!d.checked)}
            aria-label={`Select ${h.title}`}
          />
        )}
        <MediaThumb
          className={styles.thumb}
          src={thumbUrl({ thumbnail: h.thumbnail, url: h.url })}
          kind={h.kind}
          iconSize={22}
        />
        <div className={styles.body}>
          <Text className={text.title}>{h.title}</Text>
          <Caption1 className={text.muted}>
            {[h.quality, h.sizeLabel, formatWhen(h.completedAt)].filter(Boolean).join(META_SEP)}
          </Caption1>
        </div>
        <div className={styles.actions}>
          <Hint label="Re-download" placement="top" align="end">
            <Button
              appearance="subtle"
              icon={<ArrowClockwiseRegular />}
              onClick={() => redownload(h)}
              aria-label="Re-download"
            />
          </Hint>
          <Hint label="Open file" placement="top" align="end">
            <Button
              appearance="subtle"
              icon={<OpenRegular />}
              onClick={() => openFile(h.id)}
              aria-label="Open file"
            />
          </Hint>
          <Hint label="Show in folder" placement="top" align="end">
            <Button
              appearance="subtle"
              icon={<FolderRegular />}
              onClick={() => showInFolder(h.id)}
              aria-label="Show in folder"
            />
          </Hint>
          <Hint label="Remove from history" placement="top" align="end">
            <Button
              appearance="subtle"
              icon={<DeleteRegular />}
              onClick={() => remove(h.id)}
              aria-label="Remove from history"
            />
          </Hint>
        </div>
      </div>
    )
  }

  if (entries.length === 0) {
    return (
      <div className={screen.page}>
        <div className={mergeClasses(styles.root, screen.width)}>
          <ScreenHeader title="History" description="Files you've finished downloading." />
          <EmptyState
            icon={
              <div
                className={styles.emptyBadge}
                style={{ backgroundColor: tc.video.bg, color: tc.video.fg }}
              >
                <HistoryRegular />
              </div>
            }
            message="No downloads yet."
            hint="Finished downloads will show up here."
          />
        </div>
      </div>
    )
  }

  return (
    <div className={screen.fill}>
      <div className={mergeClasses(styles.root, styles.fill, screen.width)}>
        <ScreenHeader title="History" description="Files you've finished downloading." />
        <div className={styles.header}>
          {selectMode ? (
            <>
              <Caption1 className={styles.count}>{selected.size} selected</Caption1>
              <Button size="small" appearance="subtle" onClick={toggleAll}>
                {allFilteredSelected ? 'Select none' : 'Select all'}
              </Button>
              <div className={styles.spacer} />
              {confirmDelete ? (
                <>
                  <Caption1 className={styles.count}>
                    Delete {selected.size} {selected.size === 1 ? 'entry' : 'entries'}?
                  </Caption1>
                  <Button
                    size="small"
                    appearance="primary"
                    icon={<DeleteRegular />}
                    onClick={deleteSelected}
                  >
                    Delete
                  </Button>
                  <Button size="small" appearance="subtle" onClick={() => setConfirmDelete(false)}>
                    Cancel
                  </Button>
                </>
              ) : (
                <>
                  <Button
                    size="small"
                    appearance="primary"
                    icon={<DeleteRegular />}
                    onClick={() => setConfirmDelete(true)}
                    disabled={selected.size === 0}
                  >
                    Delete selected
                  </Button>
                  <Button
                    size="small"
                    appearance="subtle"
                    icon={<DismissRegular />}
                    onClick={exitSelectMode}
                  >
                    Cancel
                  </Button>
                </>
              )}
            </>
          ) : (
            <>
              <Caption1 className={styles.count}>
                {entries.length} {entries.length === 1 ? 'download' : 'downloads'}
              </Caption1>
              <Input
                className={styles.search}
                input={{ 'aria-label': 'Search history' }}
                size="small"
                value={query}
                onChange={(_, d) => setQuery(d.value)}
                placeholder="Search title, channel, URL…"
                contentBefore={<SearchRegular />}
              />
              <Select
                className={styles.kindFilter}
                aria-label="Filter by type"
                value={kindFilter}
                options={KIND_FILTER_OPTIONS}
                onChange={(v) => setKindFilter(v as 'all' | MediaKind)}
              />
              <div className={styles.spacer} />
              <Button
                size="small"
                appearance="subtle"
                icon={<CheckmarkSquareRegular />}
                onClick={() => setSelectMode(true)}
              >
                Select
              </Button>
              {confirmClear ? (
                <>
                  <Caption1 className={styles.count}>
                    Clear all {entries.length} {entries.length === 1 ? 'entry' : 'entries'}?
                  </Caption1>
                  <Button
                    size="small"
                    appearance="primary"
                    icon={<DeleteRegular />}
                    onClick={() => {
                      clear()
                      setConfirmClear(false)
                    }}
                  >
                    Clear all
                  </Button>
                  <Button size="small" appearance="subtle" onClick={() => setConfirmClear(false)}>
                    Cancel
                  </Button>
                </>
              ) : (
                <Button
                  size="small"
                  appearance="subtle"
                  icon={<DeleteRegular />}
                  onClick={() => setConfirmClear(true)}
                >
                  Clear history
                </Button>
              )}
            </>
          )}
        </div>

        {filtered.length === 0 ? (
          <Caption1 className={styles.noMatches}>No downloads match your search.</Caption1>
        ) : (
          <div
            className={styles.listScroll}
            onKeyDown={(e) => {
              // W7: Ctrl/Cmd+A within the list selects every visible row (entering
              // select mode if needed). Scoped to the list, so it never hijacks
              // Ctrl+A in the search field, which lives in the header above. Lives
              // on this wrapper (key events bubble up from the rows) because the
              // virtualized scroller below renders rows through a render prop (L104).
              if ((e.ctrlKey || e.metaKey) && (e.key === 'a' || e.key === 'A')) {
                e.preventDefault()
                setSelectMode(true)
                setSelected(new Set(filtered.map((x) => x.id)))
              }
            }}
          >
            <VirtualList
              items={filtered}
              className={styles.listScroll}
              gap={8}
              overscan={6}
              estimateSize={estimateRowSize}
              getKey={getEntryKey}
              renderItem={renderRow}
            />
          </div>
        )}
      </div>
    </div>
  )
}
