import type { MouseEvent } from 'react';
import type { Torrent, TorrentAction } from './shared';
import type { ColumnKey } from './columns';
import { useCallback, useMemo, useRef, useState } from 'react';
import { Loader2, PackageOpen } from 'lucide-react';
import { useLocalStorageState } from 'hooks';
import { ContextMenuItem, ContextMenuSeparator } from '@/components/ui/context-menu';
import { COLUMN_BY_KEY, DEFAULT_VISIBLE_COLUMNS, PINNED_COLUMN } from './columns';
import { buildFacets, selectTorrents, useTorrentFilters } from './useTorrentFilters';
import { useSession, useTorrentMutations, useTorrents } from './useTransmissionData';
import { TorrentToolbar } from './TorrentToolbar';
import { TorrentTable } from './TorrentTable';
import { TorrentDetail } from './TorrentDetail';
import { AddTorrentDialog } from './dialogs/AddTorrentDialog';
import { RemoveTorrentDialog } from './dialogs/RemoveTorrentDialog';
import { MoveTorrentDialog } from './dialogs/MoveTorrentDialog';
import { LabelsDialog } from './dialogs/LabelsDialog';
import { TorrentOptionsDialog } from './dialogs/TorrentOptionsDialog';

// The torrents section: toolbar, list, and the detail pane for ?selected=.
//
// Two kinds of selection live here and they are deliberately different things:
//   • the multi-selection (local state) is what the toolbar and the context menu act on. It is ephemeral —
//     a checkbox state, not an address, and putting forty ids in the query string would be absurd.
//   • ?selected= is the one torrent whose detail pane is open. That IS an address: it survives a reload
//     and can be sent to yourself.

const COLUMNS_STORAGE_KEY = 'transmission:columns';
/** How much of the pane the detail takes when open. */
const DETAIL_HEIGHT = '45%';

export const TorrentsView = () => {
  const { torrents, isLoading, error } = useTorrents();
  const { session, save } = useSession();
  const { act } = useTorrentMutations();
  const { filters, setSearch, setSort, selected, setSelected } = useTorrentFilters();

  const [visibleKeys, setVisibleKeys] = useLocalStorageState<ColumnKey[]>(COLUMNS_STORAGE_KEY, DEFAULT_VISIBLE_COLUMNS);
  const [selection, setSelection] = useState<Set<number>>(() => new Set());
  const anchorRef = useRef<number | null>(null);

  const [adding, setAdding] = useState(false);
  const [removing, setRemoving] = useState(false);
  const [moving, setMoving] = useState(false);
  const [labelling, setLabelling] = useState(false);
  const [optioning, setOptioning] = useState(false);

  const rows = useMemo(() => selectTorrents(torrents, filters), [torrents, filters]);
  const facets = useMemo(() => buildFacets(torrents), [torrents]);

  const columns = useMemo(
    () =>
      // Ordered by the catalogue, not by the stored array — a saved order from an older column set would
      // otherwise silently drop the columns added since.
      [...visibleKeys].map((key) => COLUMN_BY_KEY.get(key)).filter((c): c is NonNullable<typeof c> => !!c),
    [visibleKeys],
  );

  const byId = useMemo(() => new Map(torrents.map((t) => [t.id, t])), [torrents]);
  const selectedTorrents = useMemo(
    () => [...selection].map((id) => byId.get(id)).filter((t): t is Torrent => !!t),
    [selection, byId],
  );

  const onRowClick = useCallback(
    (torrent: Torrent, ev: MouseEvent) => {
      const index = rows.findIndex((r) => r.id === torrent.id);

      if (ev.shiftKey && anchorRef.current != null) {
        const anchorIndex = rows.findIndex((r) => r.id === anchorRef.current);
        if (anchorIndex >= 0) {
          const [from, to] = anchorIndex < index ? [anchorIndex, index] : [index, anchorIndex];
          setSelection(new Set(rows.slice(from, to + 1).map((r) => r.id)));
          return;
        }
      }

      if (ev.ctrlKey || ev.metaKey) {
        setSelection((prev) => {
          const next = new Set(prev);
          if (next.has(torrent.id)) next.delete(torrent.id);
          else next.add(torrent.id);
          return next;
        });
        anchorRef.current = torrent.id;
        return;
      }

      setSelection(new Set([torrent.id]));
      anchorRef.current = torrent.id;
      // A plain click both selects and opens — the detail pane is the point of clicking a row.
      setSelected(torrent.id);
    },
    [rows, setSelected],
  );

  const onRowContextMenu = useCallback((torrent: Torrent) => {
    // Right-clicking outside the selection replaces it. Right-clicking inside keeps it, so a menu action on
    // a 20-row selection doesn't silently collapse to the one row under the cursor.
    setSelection((prev) => (prev.has(torrent.id) ? prev : new Set([torrent.id])));
    anchorRef.current = torrent.id;
  }, []);

  const runAction = useCallback(
    (action: TorrentAction) => {
      if (selection.size === 0) return;
      act.mutate({ ids: [...selection], action });
    },
    [act, selection],
  );

  const toggleColumn = useCallback(
    (key: ColumnKey) => {
      if (key === PINNED_COLUMN) return;
      setVisibleKeys((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]));
    },
    [setVisibleKeys],
  );

  const altSpeed = session?.['alt-speed-enabled'] === true;

  if (isLoading && torrents.length === 0) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <Loader2 className="mr-2 h-4 w-4 animate-spin" />
        Loading torrents…
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-2 p-6 text-center">
        <div className="text-sm font-medium">Cannot reach Transmission</div>
        <p className="max-w-md text-xs text-muted-foreground">
          The officer-transmission sidecar answered with an error. Check that the daemon is running, and that the URL
          under Connection still points at it.
        </p>
      </div>
    );
  }

  return (
    <div className="flex h-full min-h-0 flex-col">
      <TorrentToolbar
        selected={selectedTorrents}
        search={filters.q}
        onSearch={setSearch}
        onAct={runAction}
        onAdd={() => setAdding(true)}
        onRemove={() => selection.size > 0 && setRemoving(true)}
        onMove={() => selection.size > 0 && setMoving(true)}
        onLabels={() => selection.size > 0 && setLabelling(true)}
        onOptions={() => selection.size > 0 && setOptioning(true)}
        altSpeed={altSpeed}
        onToggleAltSpeed={() => save.mutate({ 'alt-speed-enabled': !altSpeed })}
        total={torrents.length}
        shown={rows.length}
      />

      <div className="min-h-0 flex-1">
        {rows.length === 0 ? (
          <div className="flex h-full flex-col items-center justify-center gap-3 p-6 text-center">
            <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-muted text-muted-foreground">
              <PackageOpen className="h-6 w-6" />
            </div>
            <div>
              <div className="text-base font-semibold">{filters.isFiltered ? 'Nothing matches' : 'No torrents'}</div>
              <p className="mt-1 max-w-sm text-sm text-muted-foreground">
                {filters.isFiltered
                  ? 'Clear the filters in the sidebar to see everything again.'
                  : 'Add a magnet link or a .torrent file to get started.'}
              </p>
            </div>
          </div>
        ) : (
          <TorrentTable
            torrents={rows}
            columns={columns}
            selection={selection}
            focused={selected}
            onRowClick={onRowClick}
            sort={filters.sort}
            desc={filters.desc}
            onSort={(key) => setSort(key, filters.sort, filters.desc)}
            visibleKeys={visibleKeys}
            onToggleColumn={toggleColumn}
            onResetColumns={() => setVisibleKeys(DEFAULT_VISIBLE_COLUMNS)}
            onRowContextMenu={onRowContextMenu}
            rowMenu={() => (
              <>
                <ContextMenuItem onSelect={() => runAction('start')}>Start</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('start-now')}>Start now</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('stop')}>Pause</ContextMenuItem>
                <ContextMenuSeparator />
                <ContextMenuItem onSelect={() => runAction('verify')}>Verify local data</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('reannounce')}>Ask for more peers</ContextMenuItem>
                <ContextMenuSeparator />
                <ContextMenuItem onSelect={() => runAction('queue-top')}>Move to top of queue</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('queue-up')}>Move up</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('queue-down')}>Move down</ContextMenuItem>
                <ContextMenuItem onSelect={() => runAction('queue-bottom')}>Move to bottom</ContextMenuItem>
                <ContextMenuSeparator />
                <ContextMenuItem onSelect={() => setMoving(true)}>Set location…</ContextMenuItem>
                <ContextMenuItem onSelect={() => setLabelling(true)}>Labels…</ContextMenuItem>
                <ContextMenuItem onSelect={() => setOptioning(true)}>Torrent options…</ContextMenuItem>
                <ContextMenuSeparator />
                <ContextMenuItem className="text-destructive" onSelect={() => setRemoving(true)}>
                  Remove…
                </ContextMenuItem>
              </>
            )}
          />
        )}
      </div>

      {selected != null && (
        <div style={{ height: DETAIL_HEIGHT }} className="min-h-0 shrink-0">
          <TorrentDetail id={selected} onClose={() => setSelected(null)} />
        </div>
      )}

      <AddTorrentDialog open={adding} onOpenChange={setAdding} />
      <RemoveTorrentDialog
        open={removing}
        onOpenChange={setRemoving}
        targets={selectedTorrents}
        onRemoved={() => {
          setSelection(new Set());
          // The detail pane would keep polling a torrent that no longer exists and settle on "that torrent
          // is gone" — closing it is both correct and less alarming.
          if (selected != null && selection.has(selected)) setSelected(null);
        }}
      />
      <MoveTorrentDialog
        open={moving}
        onOpenChange={setMoving}
        targets={selectedTorrents}
        knownDirs={facets.dirs.map((d) => d.value)}
      />
      <LabelsDialog
        open={labelling}
        onOpenChange={setLabelling}
        targets={selectedTorrents}
        known={facets.labels.map((l) => l.value)}
      />
      <TorrentOptionsDialog open={optioning} onOpenChange={setOptioning} targets={selectedTorrents} />
    </div>
  );
};
