<!-- #region FileList [C:3] [TYPE Component] [SEMANTICS storage,file,table,navigation,explorer,pagination,sort,search,selection] -->
<!-- @ingroup Components -->
<!-- @BRIEF File explorer table with breadcrumbs, search, sort, pagination, multi-select, and bulk actions. -->
<!-- @LAYER UI -->
<!-- @RELATION DEPENDS_ON -> [storageService] -->
<!-- @RELATION DEPENDS_ON -> [$lib/ui/Button] -->
<!-- @RELATION DEPENDS_ON -> [$lib/ui/Input] -->
<!-- @RELATION DEPENDS_ON -> [$lib/utils/dateFormat] -->
<!-- @UX_STATE Loading -> Skeleton rows -->
<!-- @UX_STATE Empty -> "No files" message -->
<!-- @UX_STATE Table -> Data rows with sort, search, pagination -->
<!-- @UX_STATE Selected -> Checkboxes + bulk action bar -->
<!-- @UX_FEEDBACK Sort indicator on column headers -->
<!-- @UX_FEEDBACK Pagination "Showing X-Y of Z" -->
<!-- @UX_FEEDBACK Search input filters in real-time -->
<!-- @UX_RECOVERY Clear search to restore full list -->
<script lang="ts">
  import { downloadFile } from '../../../services/storageService.js';
  import { t } from '$lib/i18n/index.svelte.js';
  import { addToast } from '$lib/toasts.svelte.js';
  import { formatDateTime, formatFileSize } from "$lib/utils/dateFormat";
  import { Button, Input, EmptyState } from "$lib/ui";

  interface FileEntry {
    name: string;
    path: string;
    category: string;
    size: number;
    created_at: string;
    mime_type: string;
  }

  let {
    files = [] as FileEntry[],
    currentPath = "",
    loading = false,
    pageSize = 20,
    ondelete = (_payload: { category: string; path: string; name: string }) => {},
    onnavigate = (_path: string) => {},
    onnavigateup = () => {},
    onbulkdelete = (_files: FileEntry[]) => {},
  } = $props();

  // ── Helpers ─────────────────────────────────────────────────────
  function isDirectory(file: FileEntry): boolean {
    return file.mime_type === 'directory';
  }

  function isFile(file: FileEntry): boolean {
    return !isDirectory(file);
  }

  // ── Search ──────────────────────────────────────────────────────
  let searchText = $state("");

  // ── Sort ────────────────────────────────────────────────────────
  type SortColumn = 'name' | 'category' | 'size' | 'created_at';
  type SortDir = 'asc' | 'desc';
  let sortColumn = $state<SortColumn>('name');
  let sortDirection = $state<SortDir>('asc');

  function handleSort(col: SortColumn) {
    if (sortColumn === col) {
      sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
    } else {
      sortColumn = col;
      sortDirection = 'asc';
    }
  }

  function getSortIndicator(col: SortColumn): string {
    if (sortColumn !== col) return '⇅';
    return sortDirection === 'asc' ? '▲' : '▼';
  }

  // ── Derived: filtered + sorted data ─────────────────────────────
  let filteredFiles = $derived.by(() => {
    let result = files;
    if (searchText) {
      const q = searchText.toLowerCase();
      result = result.filter(f => f.name.toLowerCase().includes(q) || f.category.toLowerCase().includes(q));
    }
    // Directories always first, then files
    const dirs = result.filter(f => isDirectory(f));
    const fils = result.filter(f => isFile(f));
    const sortFn = (a: FileEntry, b: FileEntry) => {
      let aVal: any, bVal: any;
      if (sortColumn === 'size') {
        aVal = a.size ?? 0;
        bVal = b.size ?? 0;
      } else if (sortColumn === 'created_at') {
        aVal = a.created_at ?? '';
        bVal = b.created_at ?? '';
      } else {
        aVal = (a[sortColumn] ?? '').toString().toLowerCase();
        bVal = (b[sortColumn] ?? '').toString().toLowerCase();
      }
      if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;
      if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;
      return 0;
    };
    dirs.sort(sortFn);
    fils.sort(sortFn);
    return [...dirs, ...fils];
  });

  // ── Pagination ──────────────────────────────────────────────────
  let page = $state(0);
  let totalPages = $derived(Math.max(1, Math.ceil(filteredFiles.length / pageSize)));

  // Reset page on search change
  $effect(() => { searchText; page = 0; });

  let displayFiles = $derived(
    filteredFiles.slice(page * pageSize, (page + 1) * pageSize)
  );

  function goToPage(p: number) {
    if (p >= 0 && p < totalPages) page = p;
  }

  // ── Selection ───────────────────────────────────────────────────
  let selectedNames = $state<string[]>([]);

  function toggleSelect(name: string) {
    if (selectedNames.includes(name)) {
      selectedNames = selectedNames.filter(n => n !== name);
    } else {
      selectedNames = [...selectedNames, name];
    }
  }

  function selectAllVisible() {
    const visibleNames = displayFiles.map(f => f.name);
    const allSelected = visibleNames.every(n => selectedNames.includes(n));
    if (allSelected) {
      selectedNames = selectedNames.filter(n => !visibleNames.includes(n));
    } else {
      const newSet = new Set([...selectedNames, ...visibleNames]);
      selectedNames = Array.from(newSet);
    }
  }

  let isAllSelected = $derived(
    displayFiles.length > 0 && displayFiles.every(f => selectedNames.includes(f.name))
  );

  let selectedFiles = $derived(
    files.filter(f => selectedNames.includes(f.name))
  );

  function clearSelection() {
    selectedNames = [];
  }

  // ── Bulk actions ────────────────────────────────────────────────
  function handleBulkDelete() {
    if (selectedFiles.length === 0) return;
    onbulkdelete(selectedFiles);
    clearSelection();
  }

  async function handleBulkDownload() {
    const fileEntries = selectedFiles.filter(f => isFile(f));
    for (const f of fileEntries) {
      try { await downloadFile(f.category, f.path, f.name); }
      catch (e: any) { addToast(e?.message || 'Download failed', 'error'); }
    }
  }

  // ── Single file download ────────────────────────────────────────
  async function handleDownload(file: FileEntry) {
    try { await downloadFile(file.category, file.path, file.name); }
    catch (e: any) { addToast(e?.message || 'Download failed', 'error'); }
  }

  // ── Breadcrumb segments ─────────────────────────────────────────
  let breadcrumbSegments = $derived(
    currentPath ? currentPath.split('/').filter(Boolean) : []
  );

  function navigateToBreadcrumb(index: number) {
    const segments = currentPath.split('/').filter(Boolean);
    const newPath = segments.slice(0, index + 1).join('/');
    onnavigate(newPath);
  }
</script>

<div class="file-list space-y-3">
  <!-- ── Breadcrumbs ── -->
  <div class="flex items-center text-sm text-text-muted flex-wrap gap-1">
    <button onclick={() => onnavigate('')} class="hover:text-primary transition-colors" aria-label={$t.storage?.root || "Root"}>
      <svg class="h-4 w-4 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
      </svg>
    </button>
    {#each breadcrumbSegments as seg, i}
      <span class="text-text-subtle">/</span>
      {#if i === breadcrumbSegments.length - 1}
        <span class="text-text font-medium">{seg}</span>
      {:else}
        <button onclick={() => navigateToBreadcrumb(i)} class="hover:text-primary transition-colors capitalize">{seg}</button>
      {/if}
    {/each}
    {#if currentPath}
      <button onclick={onnavigateup} class="ml-2 text-xs text-primary hover:text-primary-hover transition-colors" title={$t.common?.back}>
        <svg class="h-4 w-4 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
        </svg>
      </button>
    {/if}
  </div>

  <!-- ── Toolbar: search + bulk actions ── -->
  <div class="flex items-center gap-3 flex-wrap">
    <div class="flex-1 min-w-[200px] max-w-sm">
      <Input bind:value={searchText} placeholder={$t.storage?.search || "Search files..."} />
    </div>
    {#if selectedFiles.length > 0}
      <span class="text-sm text-text-muted">{selectedFiles.length} selected</span>
      <Button size="sm" variant="secondary" onclick={handleBulkDownload}
        disabled={!selectedFiles.some(f => isFile(f))}>
        {$t.storage.table.download || "Download"}
      </Button>
      <Button size="sm" variant="destructive" onclick={handleBulkDelete}>
        {$t.storage.table.delete || "Delete"} ({selectedFiles.length})
      </Button>
      <Button size="sm" variant="ghost" onclick={clearSelection}>Clear</Button>
    {/if}
  </div>

  <!-- ── Table ── -->
  <div class="overflow-x-auto rounded-lg border border-border">
    <table class="min-w-full divide-y divide-border">
      <thead class="bg-surface-muted">
        <tr>
          <th class="px-4 py-3 w-10" scope="col">
            <input type="checkbox"
              checked={isAllSelected}
              onchange={selectAllVisible}
              class="h-4 w-4 text-primary border-border-strong rounded"
              aria-label="Select all" />
          </th>
          <th scope="col" class="px-6 py-3 text-left text-sm font-semibold text-text-muted cursor-pointer hover:text-text transition-colors select-none"
            onclick={() => handleSort('name')}>
            {$t.storage.table.name || "Name"} <span class="ml-0.5 text-xs">{getSortIndicator('name')}</span>
          </th>
          <th scope="col" class="px-6 py-3 text-left text-sm font-semibold text-text-muted cursor-pointer hover:text-text transition-colors select-none"
            onclick={() => handleSort('category')}>
            {$t.storage.table.category || "Category"} <span class="ml-0.5 text-xs">{getSortIndicator('category')}</span>
          </th>
          <th scope="col" class="px-6 py-3 text-left text-sm font-semibold text-text-muted cursor-pointer hover:text-text transition-colors select-none"
            onclick={() => handleSort('size')}>
            {$t.storage.table.size || "Size"} <span class="ml-0.5 text-xs">{getSortIndicator('size')}</span>
          </th>
          <th scope="col" class="px-6 py-3 text-left text-sm font-semibold text-text-muted cursor-pointer hover:text-text transition-colors select-none"
            onclick={() => handleSort('created_at')}>
            {$t.storage.table.created_at || "Created"} <span class="ml-0.5 text-xs">{getSortIndicator('created_at')}</span>
          </th>
          <th scope="col" class="px-6 py-3 text-right text-sm font-semibold text-text-muted">
            {$t.storage.table.actions || "Actions"}
          </th>
        </tr>
      </thead>
      <tbody class="bg-surface-card divide-y divide-border">
        {#if loading}
          {#each Array(Math.min(pageSize, 5)) as _}
            <tr class="animate-pulse">
              <td class="px-4 py-4"><div class="h-4 w-4 bg-surface-muted rounded"></div></td>
              <td class="px-6 py-4"><div class="h-4 bg-surface-muted rounded w-3/4"></div></td>
              <td class="px-6 py-4"><div class="h-4 bg-surface-muted rounded w-1/2"></div></td>
              <td class="px-6 py-4"><div class="h-4 bg-surface-muted rounded w-16"></div></td>
              <td class="px-6 py-4"><div class="h-4 bg-surface-muted rounded w-24"></div></td>
              <td class="px-6 py-4"><div class="h-4 bg-surface-muted rounded w-20 ml-auto"></div></td>
            </tr>
          {/each}
        {:else if !loading && displayFiles.length === 0}
          <tr>
            <td colspan="6" class="px-6 py-10">
              <EmptyState title={$t.storage.no_files || "No files found"} />
            </td>
          </tr>
        {:else}
          {#each displayFiles as file (file.path + '/' + file.name)}
            <tr class="hover:bg-surface-muted transition-colors">
              <!-- Checkbox -->
              <td class="px-4 py-3">
                <input type="checkbox"
                  checked={selectedNames.includes(file.name)}
                  onchange={() => toggleSelect(file.name)}
                  class="h-4 w-4 text-primary border-border-strong rounded" />
              </td>
              <!-- Name -->
              <td class="px-6 py-3 whitespace-nowrap text-sm">
                {#if isDirectory(file)}
                  <button onclick={() => onnavigate(file.path)}
                    class="flex items-center text-primary hover:text-primary-hover transition-colors font-medium">
                    <svg class="h-5 w-5 mr-2 text-warning shrink-0" fill="currentColor" viewBox="0 0 20 20">
                      <path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"/>
                    </svg>
                    {file.name}
                  </button>
                {:else}
                  <div class="flex items-center text-text">
                    <svg class="h-5 w-5 mr-2 text-text-subtle shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>
                    </svg>
                    {file.name}
                  </div>
                {/if}
              </td>
              <!-- Category -->
              <td class="px-6 py-3 whitespace-nowrap text-sm text-text-muted capitalize">{file.category}</td>
              <!-- Size -->
              <td class="px-6 py-3 whitespace-nowrap text-sm text-text-muted">
                {isDirectory(file) ? '--' : formatFileSize(file.size)}
              </td>
              <!-- Created -->
              <td class="px-6 py-3 whitespace-nowrap text-sm text-text-muted">{formatDateTime(file.created_at)}</td>
              <!-- Actions -->
              <td class="px-6 py-3 whitespace-nowrap text-right text-sm">
                <div class="flex items-center justify-end gap-2">
                  {#if isFile(file)}
                    <Button size="sm" variant="ghost" onclick={() => handleDownload(file)}>
                      {$t.storage.table.download || "Download"}
                    </Button>
                  {/if}
                  <Button size="sm" variant="ghost"
                    onclick={() => ondelete({ category: file.category, path: file.path, name: file.name })}>
                    <span class="text-destructive">{$t.storage.table.delete || "Delete"}</span>
                  </Button>
                </div>
              </td>
            </tr>
          {/each}
        {/if}
      </tbody>
    </table>
  </div>

  <!-- ── Pagination ── -->
  {#if !loading && filteredFiles.length > pageSize}
    <div class="flex items-center justify-between">
      <div class="text-sm text-text-muted">
        {$t.storage.showing?.replace('{start}', String(page * pageSize + 1))
          ?.replace('{end}', String(Math.min((page + 1) * pageSize, filteredFiles.length)))
          ?.replace('{total}', String(filteredFiles.length))
          || `Showing ${page * pageSize + 1}-${Math.min((page + 1) * pageSize, filteredFiles.length)} of ${filteredFiles.length}`}
      </div>
      <div class="flex gap-2">
        <Button variant="secondary" size="sm" disabled={page === 0} onclick={() => goToPage(page - 1)}>
          {$t.dashboard?.previous || "Prev"}
        </Button>
        <Button variant="secondary" size="sm" disabled={page >= totalPages - 1} onclick={() => goToPage(page + 1)}>
          {$t.dashboard?.next || "Next"}
        </Button>
      </div>
    </div>
  {/if}
</div>
<!-- #endregion FileList -->
