import { useEffect, useMemo, useRef, useState } from 'react';
import { LocalFileEntry, getLocalFileRawUrl } from '../../api';
import { isPreviewable, formatFileDate } from '../../lib/utils';

interface FileBrowserProps {
  section: 'workspace' | 'input' | 'output' | 'logs';
  currentPath: string;
  entries: LocalFileEntry[];
  pathSegments: string[];
  taskId?: number;
  onSectionChange: (section: 'workspace' | 'input' | 'output' | 'logs') => void;
  onNavigate: (path: string) => void;
  onPreview: (path: string, name: string) => void;
  onRefresh?: () => void;
  isRefreshing?: boolean;
}

type FileSort = 'name' | 'newest';

const SORT_OPTIONS: Array<{ value: FileSort; label: string }> = [
  { value: 'name', label: '名前順' },
  { value: 'newest', label: '新しい順' },
];

function FileSortMenu({ sort, onChange }: { sort: FileSort; onChange: (s: FileSort) => void }) {
  const [open, setOpen] = useState(false);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const triggerRef = useRef<HTMLButtonElement | null>(null);

  useEffect(() => {
    if (!open) return;
    const handleMouseDown = (e: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        setOpen(false);
        triggerRef.current?.focus();
      }
    };
    document.addEventListener('mousedown', handleMouseDown);
    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.removeEventListener('mousedown', handleMouseDown);
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [open]);

  const current = SORT_OPTIONS.find(o => o.value === sort) ?? SORT_OPTIONS[0];

  const handleSelect = (value: FileSort) => {
    onChange(value);
    setOpen(false);
    triggerRef.current?.focus();
  };

  return (
    <div ref={containerRef} className="relative flex-shrink-0">
      <button
        ref={triggerRef}
        type="button"
        onClick={() => setOpen(v => !v)}
        title={`並び順: ${current.label}`}
        aria-haspopup="true"
        aria-expanded={open}
        aria-label={`並び順: ${current.label}`}
        className={`inline-flex items-center justify-center w-7 h-7 rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
          open ? 'bg-accent-soft text-accent' : 'text-slate-500 hover:text-slate-900 hover:bg-surface-2'
        }`}
      >
        <svg
          width="14"
          height="14"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth={1.75}
          strokeLinecap="round"
          strokeLinejoin="round"
          aria-hidden="true"
        >
          <path d="M3 6h13M3 12h9M3 18h5M17 8V4m0 0l-3 3m3-3l3 3" />
        </svg>
      </button>
      {open && (
        <div className="absolute right-0 top-[calc(100%+6px)] z-10 bg-white border border-hairline rounded-md shadow min-w-[140px] p-1">
          {SORT_OPTIONS.map(o => {
            const selected = sort === o.value;
            return (
              <button
                key={o.value}
                type="button"
                onClick={() => handleSelect(o.value)}
                className={`flex items-center justify-between w-full px-2.5 py-1.5 rounded text-xs text-left transition-colors ${
                  selected
                    ? 'bg-accent-soft text-accent font-semibold'
                    : 'text-slate-700 font-medium hover:bg-surface-2'
                }`}
              >
                {o.label}
                {selected && (
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                    <path d="M5 13l4 4L19 7" />
                  </svg>
                )}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

function sortEntries(entries: LocalFileEntry[], mode: FileSort): LocalFileEntry[] {
  const dirs = entries.filter(e => e.kind === 'directory');
  const files = entries.filter(e => e.kind !== 'directory');
  const sortFn = mode === 'newest'
    ? (a: LocalFileEntry, b: LocalFileEntry) => {
      // Files: by modifiedAt desc. Directories: same when timestamps exist,
      // else fall back to name so the order is stable.
      const at = a.modifiedAt ? new Date(a.modifiedAt).getTime() : 0;
      const bt = b.modifiedAt ? new Date(b.modifiedAt).getTime() : 0;
      if (at !== bt) return bt - at;
      return a.name.localeCompare(b.name);
    }
    : (a: LocalFileEntry, b: LocalFileEntry) => a.name.localeCompare(b.name);
  return [...dirs.sort(sortFn), ...files.sort(sortFn)];
}

export function FileBrowser({
  section,
  currentPath,
  entries,
  pathSegments,
  taskId,
  onSectionChange,
  onNavigate,
  onPreview,
  onRefresh,
  isRefreshing,
}: FileBrowserProps) {
  const SECTIONS = ['workspace', 'input', 'output', 'logs'] as const;
  const [sort, setSort] = useState<FileSort>('name');
  const sortedEntries = useMemo(() => sortEntries(entries, sort), [entries, sort]);

  // Icon-only action buttons. Replaces the wider "Preview" / "DL" / "Open"
  // text buttons that were squeezing long filenames before. Sized 32px for
  // finger-friendly tap targets on iPhone (still compact on desktop).
  const iconBtn = 'w-8 h-8 flex items-center justify-center rounded-md border border-hairline bg-white text-slate-500 hover:text-slate-900 hover:bg-surface transition-colors';

  return (
    <div className="flex flex-col gap-3">
      <div className="flex gap-1 flex-wrap items-center">
        {SECTIONS.map(s => (
          <button
            key={s}
            onClick={() => { onSectionChange(s); onNavigate(''); }}
            className={`px-2 h-7 rounded text-2xs font-medium border transition-colors ${
              section === s
                ? 'border-accent/60 bg-accent-soft text-accent font-semibold'
                : 'border-hairline bg-white text-slate-600 hover:bg-surface'
            }`}
          >
            {s}
          </button>
        ))}
        {onRefresh && (
          <button
            onClick={onRefresh}
            disabled={isRefreshing}
            className={`ml-auto ${iconBtn} disabled:opacity-50`}
            title="ファイル一覧を更新"
            aria-label="ファイル一覧を更新"
          >
            <svg className={`w-3.5 h-3.5 ${isRefreshing ? 'animate-spin' : ''}`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
              <path d="M2 8a6 6 0 0110.5-4M14 8a6 6 0 01-10.5 4" />
              <path d="M12 2v3h-3M4 14v-3h3" />
            </svg>
          </button>
        )}
      </div>

      <div className="flex items-start justify-between gap-2">
        <div className="text-2xs text-slate-500 font-mono break-all min-w-0 flex-1 pt-1">
          /{section}{currentPath ? `/${currentPath}` : ''}
        </div>
        <FileSortMenu sort={sort} onChange={setSort} />
      </div>

      {pathSegments.length > 0 && (
        <button
          onClick={() => onNavigate(pathSegments.slice(0, -1).join('/'))}
          className="self-start inline-flex items-center gap-1 px-2 h-7 rounded border border-hairline bg-white text-2xs text-slate-600 hover:bg-surface transition-colors"
        >
          <svg className="w-3 h-3" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M10 4l-4 4 4 4M6 8h6" />
          </svg>
          Up
        </button>
      )}

      <div className="flex flex-col gap-1">
        {sortedEntries.map(entry => (
          <div
            key={`${entry.kind}:${entry.path}`}
            className="flex items-center gap-2 px-2.5 py-1.5 rounded-md bg-white border border-hairline hover:bg-surface transition-colors"
          >
            <span className="text-slate-400 flex-shrink-0" aria-hidden="true">
              {entry.kind === 'directory' ? (
                <svg className="w-4 h-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M2 4.5A1.5 1.5 0 013.5 3h3l1.5 2h4.5A1.5 1.5 0 0114 6.5v5A1.5 1.5 0 0112.5 13h-9A1.5 1.5 0 012 11.5v-7z" />
                </svg>
              ) : (
                <svg className="w-4 h-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M9 2H4a1.5 1.5 0 00-1.5 1.5v9A1.5 1.5 0 004 14h8a1.5 1.5 0 001.5-1.5V6.5L9 2z" />
                  <path d="M9 2v4.5h4.5" />
                </svg>
              )}
            </span>
            <div className="min-w-0 flex-1">
              <div className="text-[13px] text-slate-800 truncate" title={entry.name}>{entry.name}</div>
              {entry.kind === 'file' && entry.modifiedAt && (
                <div className="text-[10px] text-slate-400 font-mono leading-tight">{formatFileDate(entry.modifiedAt)}</div>
              )}
            </div>
            <div className="flex items-center gap-1 flex-shrink-0">
              {entry.kind === 'directory' ? (
                <button
                  onClick={() => onNavigate(entry.path)}
                  className={iconBtn}
                  title="Open folder"
                  aria-label="Open folder"
                >
                  <svg className="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M6 4l4 4-4 4" />
                  </svg>
                </button>
              ) : (
                <>
                  {isPreviewable(entry.name) && (
                    <button
                      onClick={() => onPreview(entry.path, entry.name)}
                      className={iconBtn}
                      title="Preview"
                      aria-label="Preview"
                    >
                      <svg className="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M1.5 8s2.5-5 6.5-5 6.5 5 6.5 5-2.5 5-6.5 5-6.5-5-6.5-5z" />
                        <circle cx="8" cy="8" r="2" />
                      </svg>
                    </button>
                  )}
                  {taskId != null && (
                    <a
                      href={getLocalFileRawUrl(taskId, section, entry.path)}
                      download={entry.name}
                      className={iconBtn}
                      title="Download"
                      aria-label="Download"
                    >
                      <svg className="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M8 2v8M4.5 6.5L8 10l3.5-3.5M2.5 13h11" />
                      </svg>
                    </a>
                  )}
                </>
              )}
            </div>
          </div>
        ))}
        {entries.length === 0 && (
          <div className="text-xs text-slate-500 px-1 py-2">ファイルなし</div>
        )}
      </div>
    </div>
  );
}
