import { useCallback, useEffect, useMemo, useState } from 'react';
import { ChevronRight, ChevronDown, FileText, Folder, PanelRightClose, PanelRightOpen } from 'lucide-react';
import { api } from '@/api/client';
import type { FileEntry } from '@/api/types';
import { inferLanguage } from '@/lib/attachments';
import { sessionEvents } from '@/hooks/sessionEvents';
import { FileViewerOverlay } from '@/components/FileViewerOverlay';
import { Input } from '@/components/ui/input';

interface Props {
  projectId: string;
}

const STORAGE_KEY = 'boocode.rightrail';

function basename(path: string): string {
  if (!path) return '';
  const parts = path.split('/');
  return parts[parts.length - 1] ?? path;
}

function joinPath(parent: string, name: string): string {
  if (!parent || parent === '.' || parent === '') return name;
  return `${parent}/${name}`;
}

export function RightRail({ projectId }: Props) {
  const [open, setOpen] = useState(() => {
    try { return localStorage.getItem(`${STORAGE_KEY}.open`) !== 'false'; } catch { return true; }
  });
  const [filter, setFilter] = useState('');
  const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set());
  const [cache, setCache] = useState<Map<string, FileEntry[]>>(new Map());
  const [fullFileList, setFullFileList] = useState<string[] | null>(null);
  const [viewerFile, setViewerFile] = useState<{ path: string; content: string } | null>(null);

  useEffect(() => {
    // best-effort; ignore failure because localStorage may be unavailable (quota, private mode)
    try { localStorage.setItem(`${STORAGE_KEY}.open`, String(open)); } catch {}
  }, [open]);

  useEffect(() => {
    let cancelled = false;
    api.projects.files(projectId).then((r) => {
      if (!cancelled) setFullFileList(r.files);
    }).catch(() => {});
    return () => { cancelled = true; };
  }, [projectId]);

  const loadDir = useCallback(async (dirPath: string) => {
    const apiPath = dirPath === '' ? '.' : dirPath;
    try {
      const result = await api.projects.listDir(projectId, apiPath);
      setCache((prev) => { const next = new Map(prev); next.set(dirPath, result.entries); return next; });
    } catch { /* ignore */ }
  }, [projectId]);

  useEffect(() => {
    if (!open) return;
    if (!cache.has('')) void loadDir('');
  }, [open, cache, loadDir]);

  function toggleDir(dirPath: string) {
    setExpandedDirs((prev) => {
      const next = new Set(prev);
      if (next.has(dirPath)) {
        next.delete(dirPath);
      } else {
        next.add(dirPath);
        if (!cache.has(dirPath)) void loadDir(dirPath);
      }
      return next;
    });
  }

  async function openFile(path: string) {
    try {
      const result = await api.projects.viewFile(projectId, path);
      setViewerFile({ path, content: result.content });
    } catch { /* ignore */ }
  }

  // Filter results
  const trimmed = filter.trim().toLowerCase();
  const filterActive = trimmed.length > 0;

  interface FilterResult { path: string; name: string; }

  const filterResults = useMemo<FilterResult[]>(() => {
    if (!filterActive) return [];
    if (fullFileList) {
      const filenameMatches: string[] = [];
      const pathOnly: string[] = [];
      for (const p of fullFileList) {
        const lp = p.toLowerCase();
        if (!lp.includes(trimmed)) continue;
        if (basename(p).toLowerCase().includes(trimmed)) filenameMatches.push(p);
        else pathOnly.push(p);
      }
      filenameMatches.sort((a, b) => a.localeCompare(b));
      pathOnly.sort((a, b) => a.localeCompare(b));
      return [...filenameMatches, ...pathOnly].slice(0, 50).map((p) => ({ path: p, name: basename(p) }));
    }
    return [];
  }, [filterActive, trimmed, fullFileList]);

  // Listen for open_file_in_browser events
  useEffect(() => {
    return sessionEvents.subscribe((event) => {
      if (event.type !== 'open_file_in_browser') return;
      if (!open) setOpen(true);
      void openFile(event.path);
    });
  }, [open, projectId]);

  if (!open) {
    return (
      <button
        type="button"
        onClick={() => setOpen(true)}
        className="shrink-0 border-l bg-sidebar p-2 hover:bg-muted"
        aria-label="Open file browser"
      >
        <PanelRightOpen size={16} />
      </button>
    );
  }

  const rootEntries = cache.get('') ?? [];

  return (
    <>
      <aside className="w-64 shrink-0 border-l bg-sidebar flex flex-col h-full overflow-hidden">
        <div className="flex items-center gap-2 px-3 py-2 border-b shrink-0">
          <span className="text-xs font-medium flex-1">Files</span>
          <button
            type="button"
            onClick={() => setOpen(false)}
            className="p-1 rounded hover:bg-muted text-muted-foreground"
            aria-label="Close file browser"
          >
            <PanelRightClose size={14} />
          </button>
        </div>
        <div className="px-2 py-1.5 shrink-0">
          <Input
            value={filter}
            onChange={(e) => setFilter(e.target.value)}
            placeholder="Filter files..."
            className="h-7 text-xs"
          />
        </div>
        <div className="flex-1 overflow-y-auto px-1 py-1">
          {filterActive ? (
            filterResults.length > 0 ? (
              <ul className="list-none space-y-0.5">
                {filterResults.map((r) => (
                  <li key={r.path}>
                    <button
                      type="button"
                      className="w-full flex items-center gap-1 px-2 py-1 text-xs rounded hover:bg-muted/60 text-left"
                      onClick={() => void openFile(r.path)}
                    >
                      <FileText size={12} className="text-muted-foreground shrink-0" />
                      <span className="font-bold truncate">{r.name}</span>
                      <span className="text-muted-foreground ml-1 truncate">{r.path}</span>
                    </button>
                  </li>
                ))}
              </ul>
            ) : (
              <div className="text-xs text-muted-foreground px-2 py-4 text-center">No matches</div>
            )
          ) : (
            <TreeLevel
              parentPath=""
              entries={rootEntries}
              cache={cache}
              expanded={expandedDirs}
              depth={0}
              onToggleDir={toggleDir}
              onSelectFile={(path) => void openFile(path)}
            />
          )}
        </div>
      </aside>

      {viewerFile && (
        <FileViewerOverlay
          path={viewerFile.path}
          content={viewerFile.content}
          lang={inferLanguage(viewerFile.path)}
          projectId={projectId}
          onClose={() => setViewerFile(null)}
          onNavigate={(path) => void openFile(path)}
        />
      )}
    </>
  );
}

interface TreeLevelProps {
  parentPath: string;
  entries: FileEntry[];
  cache: Map<string, FileEntry[]>;
  expanded: Set<string>;
  depth: number;
  onToggleDir: (dirPath: string) => void;
  onSelectFile: (path: string) => void;
}

function TreeLevel({ parentPath, entries, cache, expanded, depth, onToggleDir, onSelectFile }: TreeLevelProps) {
  const sorted = useMemo(() => {
    const copy = [...entries];
    copy.sort((a, b) => {
      if (a.kind !== b.kind) return a.kind === 'dir' ? -1 : 1;
      return a.name.localeCompare(b.name);
    });
    return copy;
  }, [entries]);

  return (
    <ul className="list-none">
      {sorted.map((entry) => {
        const fullPath = joinPath(parentPath, entry.name);
        const isExpanded = entry.kind === 'dir' && expanded.has(fullPath);
        return (
          <li key={fullPath}>
            <div
              className="flex items-center gap-1 px-1 py-0.5 text-xs cursor-default rounded hover:bg-muted/60"
              style={{ paddingLeft: 4 + depth * 12 }}
              onClick={() => {
                if (entry.kind === 'dir') onToggleDir(fullPath);
                else onSelectFile(fullPath);
              }}
            >
              {entry.kind === 'dir' ? (
                isExpanded ? <ChevronDown size={10} className="shrink-0" /> : <ChevronRight size={10} className="shrink-0" />
              ) : (
                <span className="w-[10px] shrink-0" />
              )}
              {entry.kind === 'dir' ? (
                <Folder size={12} className="text-muted-foreground shrink-0" />
              ) : (
                <FileText size={12} className="text-muted-foreground shrink-0" />
              )}
              <span className="truncate">{entry.name}</span>
            </div>
            {entry.kind === 'dir' && isExpanded && cache.has(fullPath) && (
              <TreeLevel
                parentPath={fullPath}
                entries={cache.get(fullPath) ?? []}
                cache={cache}
                expanded={expanded}
                depth={depth + 1}
                onToggleDir={onToggleDir}
                onSelectFile={onSelectFile}
              />
            )}
          </li>
        );
      })}
    </ul>
  );
}
