/**
 * ファイルブラウザのツールバー部品群（スペース / タスク共有）。
 * いずれも状態を持たない純プレゼンテーショナル。drag 状態だけ FileDropzone が内部に持つ。
 *
 * - FileActions       … 「追加」ボタン + 隠し input + 再読み込み
 * - FileSelectionBar  … すべて選択 + 選択件数 + 削除（複数選択）
 * - FileDropzone      … 子をラップし、ドラッグ&ドロップでアップロードを受ける
 * - FileViewToggle    … アイコン表示 / 詳細表示 の切替（セグメント）
 */
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { FileViewMode } from '../../lib/fileView';

/**
 * アイコン表示時の並べ替えメニュー（名前順 / 新しい順）。タスク窓・ワークスペース窓で共有。
 * 共有ソート状態（FileSortState）を持つ呼出側が、name/modified に縮約してこの 2 値で操作する。
 */
export type FileSort = 'name' | 'newest';

const SORT_OPTIONS: Array<{ value: FileSort; labelKey: string }> = [
  { value: 'name', labelKey: 'sort.name' },
  { value: 'newest', labelKey: 'sort.newest' },
];

export function FileSortMenu({ sort, onChange }: { sort: FileSort; onChange: (s: FileSort) => void }) {
  const { t } = useTranslation('files');
  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={t('sort.tooltip', { mode: t(current.labelKey) })}
        aria-haspopup="true"
        aria-expanded={open}
        aria-label={t('sort.tooltip', { mode: t(current.labelKey) })}
        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-canvas 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'
                }`}
              >
                {t(o.labelKey)}
                {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>
  );
}

const ICON_BTN =
  'w-8 h-8 flex items-center justify-center rounded-md border border-hairline bg-canvas text-slate-500 hover:text-slate-900 hover:bg-surface transition-colors';

export function FileActions({
  idPrefix,
  canUpload,
  onUploadFiles,
  onRefresh,
  isRefreshing,
  isUploading,
}: {
  idPrefix: string;
  canUpload: boolean;
  onUploadFiles: (files: File[]) => void;
  onRefresh: () => void;
  isRefreshing?: boolean;
  isUploading?: boolean;
}) {
  const { t } = useTranslation('files');
  const inputRef = useRef<HTMLInputElement>(null);
  return (
    <div className="flex shrink-0 items-center gap-1.5">
      {canUpload && (
        <>
          <input
            ref={inputRef}
            type="file"
            multiple
            data-testid={`${idPrefix}-files-upload-input`}
            className="hidden"
            onChange={e => {
              const files = Array.from(e.target.files ?? []);
              e.target.value = '';
              onUploadFiles(files);
            }}
          />
          <button
            type="button"
            data-testid={`${idPrefix}-files-upload-btn`}
            onClick={() => inputRef.current?.click()}
            disabled={isUploading}
            className="inline-flex h-8 items-center gap-1 rounded-md border border-hairline bg-canvas px-2.5 text-2xs font-medium text-slate-600 transition-colors hover:bg-surface hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 disabled:opacity-50"
          >
            <svg className="h-3.5 w-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
              <path d="M8 3.5v9M3.5 8h9" />
            </svg>
            {t('toolbar.add')}
          </button>
        </>
      )}
      <button
        type="button"
        onClick={onRefresh}
        disabled={isRefreshing}
        className={`${ICON_BTN} disabled:opacity-50`}
        title={t('toolbar.refresh')}
        aria-label={t('toolbar.refresh')}
      >
        <svg className={`h-3.5 w-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>
  );
}

export function FileSelectionBar({
  idPrefix,
  allSelected,
  onToggleSelectAll,
  selectedCount,
  onDeleteSelected,
  onDownloadSelected,
  onMoveSelected,
  isDeleting,
  isDownloading,
  isMoving,
}: {
  idPrefix: string;
  allSelected: boolean;
  onToggleSelectAll: () => void;
  selectedCount: number;
  onDeleteSelected: () => void;
  onDownloadSelected: () => void;
  /** Phase 2: 指定すると「移動」ボタンを出す（移動先ダイアログを開く）。 */
  onMoveSelected?: () => void;
  isDeleting?: boolean;
  isDownloading?: boolean;
  isMoving?: boolean;
}) {
  const { t } = useTranslation('files');
  return (
    <div className="flex flex-wrap items-center gap-2 text-2xs text-slate-500">
      <label className="inline-flex cursor-pointer items-center gap-1.5 select-none">
        <input
          type="checkbox"
          data-testid={`${idPrefix}-files-select-all`}
          checked={allSelected}
          onChange={onToggleSelectAll}
          className="h-3.5 w-3.5 rounded border-hairline accent-[var(--brand-primary)]"
        />
        {t('toolbar.selectAll')}
      </label>
      <span data-testid={`${idPrefix}-files-selected-count`} className="text-slate-400">
        {t('toolbar.selectedCount', { count: selectedCount })}
      </span>
      {selectedCount > 0 && (
        <div className="ml-auto flex items-center gap-1.5">
          {onMoveSelected && (
            <button
              type="button"
              data-testid={`${idPrefix}-files-move-selected`}
              onClick={onMoveSelected}
              disabled={isMoving}
              className="inline-flex h-7 items-center gap-1 rounded-md border border-hairline bg-canvas px-2.5 text-2xs font-medium text-slate-600 transition-colors hover:bg-surface hover:text-slate-900 disabled:opacity-50"
            >
              <svg className="h-3.5 w-3.5" 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 3h2.6l1.2 1.6h5.2A1.5 1.5 0 0116 6.1V12a1.5 1.5 0 01-1.5 1.5h-11A1.5 1.5 0 012 12V4.5z" />
                <path d="M9 8.5h3M10.5 7l1.5 1.5L10.5 10" />
              </svg>
              {t('toolbar.move')}
            </button>
          )}
          <button
            type="button"
            data-testid={`${idPrefix}-files-download-selected`}
            onClick={onDownloadSelected}
            disabled={isDownloading}
            className="inline-flex h-7 items-center gap-1 rounded-md border border-hairline bg-canvas px-2.5 text-2xs font-medium text-slate-600 transition-colors hover:bg-surface hover:text-slate-900 disabled:opacity-50"
          >
            <svg className="h-3.5 w-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>
            {t('toolbar.download')}
          </button>
          <button
            type="button"
            data-testid={`${idPrefix}-files-delete-selected`}
            onClick={onDeleteSelected}
            disabled={isDeleting}
            className="inline-flex h-7 items-center gap-1 rounded-md border border-red-200 bg-red-50 px-2.5 text-2xs font-bold text-red-700 transition-colors hover:bg-red-100 disabled:opacity-50 dark:border-red-500/30 dark:bg-red-500/15 dark:text-red-300"
          >
            <svg className="h-3.5 w-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M3 4h10M6.5 4V2.5h3V4M5 4l.5 9h5l.5-9" />
            </svg>
            {t('toolbar.delete')}
          </button>
        </div>
      )}
    </div>
  );
}

export function FileDropzone({
  idPrefix,
  enabled,
  isUploading,
  onDropFiles,
  onRejectFolder,
  children,
}: {
  idPrefix: string;
  /** false のときドロップを受け付けない（オーバーレイも出さない）。 */
  enabled: boolean;
  isUploading?: boolean;
  onDropFiles: (files: File[]) => void;
  /** フォルダがドロップされたときのコールバック（未対応メッセージ表示用）。 */
  onRejectFolder?: () => void;
  children: React.ReactNode;
}) {
  const { t } = useTranslation('files');
  const [dragDepth, setDragDepth] = useState(0);

  // ウィンドウ外への drag 離脱や drop 取りこぼしで dragDepth が戻らずオーバーレイが
  // 残るのを防ぐ。window レベルの dragend/drop で必ず 0 に戻す。
  useEffect(() => {
    const reset = () => setDragDepth(0);
    window.addEventListener('dragend', reset);
    window.addEventListener('drop', reset);
    return () => {
      window.removeEventListener('dragend', reset);
      window.removeEventListener('drop', reset);
    };
  }, []);

  // testid と要素は常に出し、enabled=false（read-only）のときは DnD ハンドラと
  // オーバーレイだけ無効化する（既存 e2e の dropzone testid を温存する）。
  return (
    <div
      data-testid={`${idPrefix}-files-dropzone`}
      className="relative min-h-[6rem] rounded-lg"
      onDragEnter={enabled ? (e => { e.preventDefault(); setDragDepth(d => d + 1); }) : undefined}
      onDragOver={enabled ? (e => { e.preventDefault(); }) : undefined}
      onDragLeave={enabled ? (e => { e.preventDefault(); if (!e.relatedTarget) { setDragDepth(0); } else { setDragDepth(d => Math.max(0, d - 1)); } }) : undefined}
      onDrop={enabled ? (e => {
        e.preventDefault();
        setDragDepth(0);
        const hasDirectory = Array.from(e.dataTransfer.items ?? []).some(
          it => it.webkitGetAsEntry?.()?.isDirectory,
        );
        const files = Array.from(e.dataTransfer.files);
        if (hasDirectory) { onRejectFolder?.(); return; }
        if (files.length === 0) return;
        onDropFiles(files);
      }) : undefined}
    >
      {enabled && dragDepth > 0 && (
        <div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-lg border-2 border-dashed border-slate-400 bg-canvas/85 text-sm font-medium text-slate-600">
          {t('toolbar.dropToAdd')}
        </div>
      )}
      {enabled && isUploading && (
        <div className="pointer-events-none absolute right-1 top-1 z-10 rounded bg-slate-800/80 px-2 py-0.5 text-2xs text-white">
          {t('toolbar.uploading')}
        </div>
      )}
      {children}
    </div>
  );
}

/**
 * アイコン表示 / 詳細表示 の切替セグメント。状態は呼出側（useFileView）が持つ。
 */
export function FileViewToggle({
  idPrefix,
  mode,
  onChange,
}: {
  idPrefix: string;
  mode: FileViewMode;
  onChange: (mode: FileViewMode) => void;
}) {
  const { t } = useTranslation('files');
  const btn = (m: FileViewMode, label: string, path: JSX.Element) => (
    <button
      type="button"
      data-testid={`${idPrefix}-files-view-${m}`}
      onClick={() => onChange(m)}
      aria-pressed={mode === m}
      title={label}
      aria-label={label}
      className={`flex h-8 w-8 items-center justify-center transition-colors ${
        mode === m ? 'bg-surface text-slate-900' : 'bg-canvas text-slate-500 hover:bg-surface hover:text-slate-900'
      }`}
    >
      <svg className="h-4 w-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
        {path}
      </svg>
    </button>
  );
  return (
    <div className="flex shrink-0 overflow-hidden rounded-md border border-hairline" role="group" aria-label={t('toolbar.viewToggle')}>
      {btn('icon', t('toolbar.iconView'), <><rect x="2" y="2" width="5" height="5" rx="1" /><rect x="9" y="2" width="5" height="5" rx="1" /><rect x="2" y="9" width="5" height="5" rx="1" /><rect x="9" y="9" width="5" height="5" rx="1" /></>)}
      {btn('detail', t('toolbar.detailView'), <><path d="M5.5 4h8M5.5 8h8M5.5 12h8" /><path d="M2.5 4h.01M2.5 8h.01M2.5 12h.01" /></>)}
    </div>
  );
}
