import { useRef, useState, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { ChevronUp, ChevronDown } from 'lucide-react';
import type { DirEntry } from '../../../../hooks/useFilesAPI';
import { getFileType } from '../../../FileViewer';
import type { UseFileBrowserAppType } from '../useFileBrowserApp';
import { FileItem } from './FileItem';

type SortField = 'name' | 'size' | 'type' | 'date';
type SortDirection = 'asc' | 'desc';

type FileGridProps = {
  fileBrowserManager: UseFileBrowserAppType;
};

const LIST_ROW_HEIGHT = 42;
const GRID_ROW_HEIGHT = 130;
const GRID_GAP = 12;
const HEADER_HEIGHT = 36;
// Type-ahead: keys pressed within this window accumulate into one burst. 1–2 chars jump to the first
// matching item; the 3rd char flips the burst into the search filter. Idle past the window resets it.
const TYPEAHEAD_RESET_MS = 1000;
const TYPEAHEAD_FILTER_AT = 3;

const useColumnCount = (scrollRef: React.RefObject<HTMLDivElement | null>) => {
  const [cols, setCols] = useState(4);

  useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const measure = () => {
      const width = el.clientWidth - 32; // subtract p-4 (16px each side)
      // Breakpoints matching grid-cols-2 sm:3 md:4 lg:5 xl:6
      if (width >= 1280) setCols(6);
      else if (width >= 1024) setCols(5);
      else if (width >= 768) setCols(4);
      else if (width >= 640) setCols(3);
      else setCols(2);
    };
    measure();
    const observer = new ResizeObserver(measure);
    observer.observe(el);
    return () => observer.disconnect();
  }, [scrollRef]);

  return cols;
};

export const FileGrid = ({ fileBrowserManager }: FileGridProps) => {
  const {
    visibleEntries: entries,
    viewMode,
    currentPath,
    selected,
    clipboard,
    handleOpen,
    handleDelete,
    handleRename,
    handleChat,
    handleCopyPath,
    handleDownload,
    setSelected,
    handleCut,
    handleCopy,
    renamingName,
    setRenamingName,
    handleReadAloud,
    handleExtract,
    getMatchingTaskGroups,
    getMatchingAgentGroups,
    handleRunTask,
    handleRunAgent,
    handleCreateDashboard,
    fileScrollRef: scrollRef,
    setSearchQuery,
    searchInputRef,
  } = fileBrowserManager;
  const lastClickedIdx = useRef<number>(-1);
  const [sortField, setSortField] = useState<SortField>('name');
  const [sortDirection, setSortDirection] = useState<SortDirection>('asc');

  const sorted = (() => {
    const compare = (a: DirEntry, b: DirEntry): number => {
      if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
      let result: number;
      switch (sortField) {
        case 'name':
          result = a.name.localeCompare(b.name);
          break;
        case 'size':
          result = a.size - b.size;
          if (result === 0) return a.name.localeCompare(b.name);
          break;
        case 'type': {
          const typeA = a.type === 'directory' ? 'directory' : getFileType(a.name);
          const typeB = b.type === 'directory' ? 'directory' : getFileType(b.name);
          result = typeA.localeCompare(typeB);
          if (result === 0) return a.name.localeCompare(b.name);
          break;
        }
        case 'date':
          result = a.modifiedAt - b.modifiedAt;
          if (result === 0) return a.name.localeCompare(b.name);
          break;
      }
      return sortDirection === 'asc' ? result : -result;
    };
    return [...entries].sort(compare);
  })();

  const toggleSort = (field: SortField) => {
    if (sortField === field) {
      setSortDirection((d) => (d === 'asc' ? 'desc' : 'asc'));
    } else {
      setSortField(field);
      setSortDirection('asc');
    }
  };

  const handleSelect = (entry: DirEntry, ev: React.MouseEvent) => {
    const idx = sorted.findIndex((e) => e.name === entry.name);

    if (ev.shiftKey && lastClickedIdx.current >= 0) {
      const start = Math.min(lastClickedIdx.current, idx);
      const end = Math.max(lastClickedIdx.current, idx);
      const next = new Set(selected);
      for (let i = start; i <= end; i++) {
        next.add(sorted[i]!.name);
      }
      setSelected(next);
    } else if (ev.ctrlKey || ev.metaKey) {
      const next = new Set(selected);
      if (next.has(entry.name)) {
        next.delete(entry.name);
      } else {
        next.add(entry.name);
      }
      setSelected(next);
      lastClickedIdx.current = idx;
    } else {
      setSelected(new Set([entry.name]));
      lastClickedIdx.current = idx;
    }
  };

  const cols = useColumnCount(scrollRef);
  const cutPaths = clipboard?.mode === 'cut' ? new Set(clipboard.paths) : new Set<string>();
  const anySelected = selected.size > 0;
  const entryPath = (name: string) => (currentPath === '/' ? `/${name}` : `${currentPath}/${name}`);

  const rowCount = viewMode === 'list' ? sorted.length : Math.ceil(sorted.length / cols);

  const virtualizer = useVirtualizer({
    count: rowCount,
    getScrollElement: () => scrollRef.current,
    estimateSize: () => (viewMode === 'list' ? LIST_ROW_HEIGHT : GRID_ROW_HEIGHT + GRID_GAP),
    overscan: 10,
    scrollMargin: HEADER_HEIGHT,
  });

  // Refs hold the latest values so the once-registered keydown listener always reads fresh state.
  const sortedRef = useRef(sorted);
  sortedRef.current = sorted;
  const colsRef = useRef(cols);
  colsRef.current = cols;
  const viewModeRef = useRef(viewMode);
  viewModeRef.current = viewMode;
  const virtualizerRef = useRef(virtualizer);
  virtualizerRef.current = virtualizer;

  // Type-ahead (see constants above): jump-to-item on a short burst, flip to the filter on the 3rd key.
  useEffect(() => {
    let buffer = '';
    let timer: ReturnType<typeof setTimeout> | undefined;
    const reset = () => {
      buffer = '';
      if (timer) clearTimeout(timer);
      timer = undefined;
    };
    const onKey = (ev: KeyboardEvent) => {
      const t = ev.target as HTMLElement;
      if (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.closest('.monaco-editor')) return;
      if (ev.ctrlKey || ev.metaKey || ev.altKey || ev.key.length !== 1) return; // printable, unmodified only

      ev.preventDefault();
      if (timer) clearTimeout(timer);
      buffer += ev.key;
      timer = setTimeout(reset, TYPEAHEAD_RESET_MS);
      const typed = buffer;

      // Kept typing → treat it as a filter: dump the burst into the search box and hand off focus.
      if (typed.length >= TYPEAHEAD_FILTER_AT) {
        reset();
        setSearchQuery(typed);
        requestAnimationFrame(() => {
          const el = searchInputRef.current;
          if (!el) return;
          el.focus();
          el.setSelectionRange(el.value.length, el.value.length); // cursor at the end, keep typing
        });
        return;
      }

      // Short burst → jump: select + scroll to the first item (folder or file) whose name starts with it.
      const prefix = typed.toLowerCase();
      const list = sortedRef.current;
      const idx = list.findIndex((e) => e.name.toLowerCase().startsWith(prefix));
      if (idx < 0) return;
      setSelected(new Set([list[idx]!.name]));
      const row = viewModeRef.current === 'list' ? idx : Math.floor(idx / Math.max(1, colsRef.current));
      virtualizerRef.current.scrollToIndex(row, { align: 'auto' });
    };
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('keydown', onKey);
      if (timer) clearTimeout(timer);
    };
  }, [setSearchQuery, searchInputRef, setSelected]);

  if (entries.length === 0) {
    return <div className="flex items-center justify-center py-20 text-duck-dark/40 text-sm">This folder is empty</div>;
  }

  const renderItem = (entry: DirEntry) => (
    <FileItem
      key={entry.name}
      entry={entry}
      viewMode={viewMode}
      selected={selected.has(entry.name)}
      anySelected={anySelected}
      selectedCount={selected.size}
      isCut={cutPaths.has(entryPath(entry.name))}
      onOpen={handleOpen}
      onDelete={handleDelete}
      onRename={handleRename}
      onChat={handleChat}
      onCopyPath={handleCopyPath}
      onDownload={handleDownload}
      onSelect={handleSelect}
      onCut={handleCut}
      onCopy={handleCopy}
      forceRename={renamingName === entry.name}
      onRenamingChange={setRenamingName}
      onReadAloud={handleReadAloud}
      onExtract={handleExtract}
      taskGroups={getMatchingTaskGroups(entry.name, entry.type)}
      onRunTask={handleRunTask}
      agentGroups={getMatchingAgentGroups(entry.name, entry.type)}
      onRunAgent={handleRunAgent}
      onCreateDashboard={handleCreateDashboard}
    />
  );

  const sortIcon = (field: SortField) =>
    sortField === field ? (
      sortDirection === 'asc' ? (
        <ChevronUp className="h-3 w-3" />
      ) : (
        <ChevronDown className="h-3 w-3" />
      )
    ) : null;

  const sortBtnClass = (field: SortField) =>
    `flex items-center gap-1 text-xs font-medium cursor-pointer transition-colors ${
      sortField === field ? 'text-duck-teal' : 'text-duck-dark/50 hover:text-duck-dark/80'
    }`;

  return (
    <>
      {viewMode === 'list' ? (
        <div
          className="sticky top-0 z-10 bg-background/95 backdrop-blur-sm border-b border-duck-dark/10 flex items-center gap-3 px-3"
          style={{ height: HEADER_HEIGHT }}
        >
          <span className="shrink-0 w-5" />
          <span className="shrink-0 w-5" />
          <button className={`flex-1 min-w-0 ${sortBtnClass('name')}`} onClick={() => toggleSort('name')}>
            Name {sortIcon('name')}
          </button>
          <button
            className={`hidden md:flex shrink-0 w-20 justify-end ${sortBtnClass('type')}`}
            onClick={() => toggleSort('type')}
          >
            Type {sortIcon('type')}
          </button>
          <button
            className={`hidden md:flex shrink-0 w-20 justify-end ${sortBtnClass('size')}`}
            onClick={() => toggleSort('size')}
          >
            Size {sortIcon('size')}
          </button>
          <button
            className={`hidden md:flex shrink-0 w-28 justify-end ${sortBtnClass('date')}`}
            onClick={() => toggleSort('date')}
          >
            Date {sortIcon('date')}
          </button>
          <span className="shrink-0 w-8" />
        </div>
      ) : (
        <div
          className="sticky top-0 z-10 bg-background/95 backdrop-blur-sm border-b border-duck-dark/10 flex items-center gap-2 px-3"
          style={{ height: HEADER_HEIGHT }}
        >
          <span className="text-xs text-duck-dark/40">Sort:</span>
          {(['name', 'size', 'type', 'date'] as SortField[]).map((field) => (
            <button
              key={field}
              onClick={() => toggleSort(field)}
              className={`flex items-center gap-1 px-2 py-1 rounded-md text-xs cursor-pointer transition-colors ${
                sortField === field
                  ? 'bg-duck-teal/10 text-duck-teal font-medium'
                  : 'text-duck-dark/50 hover:bg-duck-dark/5'
              }`}
            >
              {field.charAt(0).toUpperCase() + field.slice(1)}
              {sortIcon(field)}
            </button>
          ))}
        </div>
      )}
      <div className="file-grid relative w-full" style={{ height: virtualizer.getTotalSize() }}>
        {virtualizer.getVirtualItems().map((virtualRow) => {
          if (viewMode === 'list') {
            const entry = sorted[virtualRow.index]!;
            return (
              <div
                key={virtualRow.key}
                className="absolute left-0 w-full"
                style={{ top: virtualRow.start, height: virtualRow.size }}
              >
                {renderItem(entry)}
              </div>
            );
          }

          const startIdx = virtualRow.index * cols;
          const rowEntries = sorted.slice(startIdx, startIdx + cols);
          return (
            <div
              key={virtualRow.key}
              className="absolute left-0 w-full grid gap-3"
              style={{
                top: virtualRow.start,
                height: virtualRow.size - GRID_GAP,
                gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
              }}
            >
              {rowEntries.map(renderItem)}
            </div>
          );
        })}
      </div>
    </>
  );
};
