/**
 * Dataset Workspace — the case-centric nnU-Net view of ONE real ImageHub dataset.
 *
 * Reskin of docs/workflows/DatasetWorkspace.tsx onto the UMP design system, wired to REAL data:
 * files come from listDatasetFiles, cases + readiness from the nnU-Net core domain (via the
 * workspaceModel adapter), versions from listDatasetVersions. The nnU-Net descriptor
 * (channel names / label class-map) is inferred from filenames until persistence (Phase 2), so the
 * config + readiness are flagged "suy ra" (inferred).
 */
import { Fragment, lazy, Suspense, useMemo, useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
  AlertTriangle,
  Boxes,
  Braces,
  CheckCircle2,
  ChevronDown,
  ChevronRight,
  Eye,
  FileText,
  FolderTree,
  Info,
  Layers,
  Loader2,
  ScanLine,
  Search,
  Wand2,
  XCircle,
} from 'lucide-react';

import {
  Badge,
  Button,
  Card,
  CardContent,
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  Input,
  Skeleton,
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
  cn,
  fetchAsFile,
  fileDownloadUrl,
  getDataset,
  updateDataset,
  listDatasetFiles,
  listDatasetVersions,
  normalizeChannels,
} from '@ump/shared';

import type { DatasetCase } from '../../data-import/domain/nnunet/core';
import {
  buildWorkspaceModel,
  caseFiles,
  caseStatus,
  channelPresence,
  datasetJsonPreview,
  formatBytes,
  type CaseStatus,
  type WorkspaceModel,
} from '../domain/workspaceModel';
import { FolderTreeView } from './FolderTreeView';
import { FolderUploadButtons } from './FolderUploadButtons';
import { toast } from 'sonner';
import type { AvailableMask } from '../../../components/DatasetFileViewerDialog';
// VTK-free image-sequence quad-viewer (same light subpath, no lazy boundary needed).
import { ImageSequenceViewer } from '@ump/shared/video-viewer';

const DatasetFileViewerDialog = lazy(() => import('../../../components/DatasetFileViewerDialog'));

/** A case whose image channel is a flat 2D raster opens in the image-sequence viewer, not VTK. */
const is2dImage = (filename: string): boolean => /\.(png|jpe?g|bmp|webp)$/i.test(filename);

const STATUS_META: Record<CaseStatus, { label: string; cls: string; Icon: typeof CheckCircle2 }> = {
  valid: { label: 'Hợp lệ', cls: 'bg-emerald-50 text-emerald-700 border-emerald-200', Icon: CheckCircle2 },
  warn: { label: 'Cảnh báo', cls: 'bg-amber-50 text-amber-700 border-amber-200', Icon: AlertTriangle },
  error: { label: 'Lỗi', cls: 'bg-rose-50 text-rose-700 border-rose-200', Icon: XCircle },
};

const PAGE_SIZE = 8;

function StatusPill({ status }: { status: CaseStatus }) {
  const m = STATUS_META[status];
  return (
    <Badge variant="outline" className={cn('gap-1 font-medium', m.cls)}>
      <m.Icon className="h-3.5 w-3.5" /> {m.label}
    </Badge>
  );
}

/**
 * Normalize image filenames to the single-channel `{case}_0000.{ext}` training-layout
 * convention (labels stay bare). Logical rename only — no file move.
 */
function NormalizeFilesButton({ datasetId }: { datasetId: string }) {
  const qc = useQueryClient();
  const [open, setOpen] = useState(false);
  const [prefix, setPrefix] = useState('');
  const [running, setRunning] = useState(false);
  // The sanitized prefix that the server will actually use (uppercase alphanumeric).
  const clean = prefix.trim().toUpperCase().replace(/[^A-Z0-9]/g, '');

  const run = async () => {
    if (!clean) return;
    setRunning(true);
    try {
      const r = await normalizeChannels(datasetId, clean);
      qc.invalidateQueries({ queryKey: ['imagehub'] });
      toast.success(
        r.renamed > 0
          ? `Đã chuẩn hoá ${r.renamed} tệp${r.skipped ? ` (bỏ qua ${r.skipped})` : ''}.`
          : 'Tất cả tệp đã đúng định dạng.',
      );
      setOpen(false);
    } catch {
      toast.error('Không chuẩn hoá được tên tệp.');
    } finally {
      setRunning(false);
    }
  };

  const sample = clean || 'POLYP25';
  return (
    <>
      <Button
        variant="outline"
        onClick={() => setOpen(true)}
        title="Chuẩn hoá tên tệp theo định dạng huấn luyện"
      >
        <Wand2 className="mr-1.5 h-4 w-4" /> Chuẩn hoá tên tệp
      </Button>
      <Dialog open={open} onOpenChange={(o) => !running && setOpen(o)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Chuẩn hoá tên tệp</DialogTitle>
          </DialogHeader>
          <div className="space-y-3">
            <div className="space-y-1.5">
              <span className="text-sm font-medium">Mã tổn thương (tiền tố)</span>
              <Input
                value={prefix}
                onChange={(e) => setPrefix(e.target.value)}
                onKeyDown={(e) => e.key === 'Enter' && run()}
                placeholder="VD: POLYP25"
                autoFocus
              />
            </div>
            <p className="text-xs text-muted-foreground">
              Ảnh đổi thành <span className="font-mono">{sample}_00001_0000.png</span>, nhãn thành{' '}
              <span className="font-mono">{sample}_00001.png</span> (số ca được điền 5 chữ số).
            </p>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setOpen(false)} disabled={running}>
              Huỷ
            </Button>
            <Button onClick={run} disabled={running || !clean}>
              {running ? <Loader2 className="mr-1.5 h-4 w-4 animate-spin" /> : null}
              Chuẩn hoá
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}

/* ------------------------------- Cases tab ------------------------------- */
function CasesTab({ model, datasetId }: { model: WorkspaceModel; datasetId: string }) {
  const [query, setQuery] = useState('');
  const [statusFilter, setStatusFilter] = useState<'all' | CaseStatus>('all');
  const [expanded, setExpanded] = useState<Set<string>>(new Set());
  const [page, setPage] = useState(1);
  const [detailId, setDetailId] = useState<string | null>(null);
  const [viewer, setViewer] = useState<{ file: File; name: string; masks: AvailableMask[] } | null>(null);
  const [imageViewer, setImageViewer] = useState<
    { caseId: string; original: string[]; depth: string[]; segmentation: string[] } | null
  >(null);
  const [openingCase, setOpeningCase] = useState<string | null>(null);
  const qc = useQueryClient();
  // The dataset's per-value label map (migration 027) — shared cache key with the main view.
  const labelMap = useQuery({
    queryKey: ['imagehub', 'dataset', datasetId],
    queryFn: () => getDataset(datasetId),
    enabled: !!datasetId,
  }).data?.labelMap;

  // Persist a renamed multi-label organ to the dataset's label_map, then refresh the cache.
  const renameLabel = (value: number, name: string) => {
    const next = { ...(labelMap || {}), [String(value)]: name };
    updateDataset(datasetId, { labelMap: next })
      .then((ds) => {
        qc.setQueryData(['imagehub', 'dataset', datasetId], ds);
        toast.success('Đã đổi tên nhãn.');
      })
      .catch(() => toast.error('Không đổi được tên nhãn.'));
  };

  // Open one case in the 3D viewer: fetch its (first) image channel as a File, and pass its
  // labelsTr mask — if any — as a single toggleable overlay. Image-only cases open without overlay.
  const openCaseViewer = async (c: DatasetCase) => {
    const channels = model.meta.channels;
    const imgSource = channels.map((ch) => c.channelFiles[ch.index]).find(Boolean);
    if (!imgSource) {
      toast.error('Ca này chưa có ảnh gốc để xem.');
      return;
    }
    setOpeningCase(c.caseId);
    try {
      if (is2dImage(imgSource.filename)) {
        // 2D PNG case → the image-sequence quad-viewer. A channel whose FILE is named "…depth…"
        // feeds Q2 (channel names here are inferred "Kênh N", so match the filename, not the name);
        // the first non-depth channel feeds Q1 (original), and labelFile feeds Q3 (segmentation).
        // <img> loads the presigned URLs directly, so no fetch-to-File is needed.
        const depthCh = channels.find((ch) => {
          const f = c.channelFiles[ch.index];
          return f != null && /depth/i.test(f.filename);
        });
        const origCh = channels.find((ch) => c.channelFiles[ch.index] && ch !== depthCh) ?? channels[0];
        const origFile = c.channelFiles[origCh.index];
        const depthFile = depthCh ? c.channelFiles[depthCh.index] : undefined;
        const [orig, depth, seg] = await Promise.all([
          origFile ? fileDownloadUrl(datasetId, origFile.id) : Promise.resolve(null),
          depthFile ? fileDownloadUrl(datasetId, depthFile.id) : Promise.resolve(null),
          c.labelFile ? fileDownloadUrl(datasetId, c.labelFile.id) : Promise.resolve(null),
        ]);
        setViewer(null); // ensure only one viewer dialog is mounted at a time
        setImageViewer({
          caseId: c.caseId,
          original: orig ? [orig.url] : [],
          depth: depth ? [depth.url] : [],
          segmentation: seg ? [seg.url] : [],
        });
        return;
      }
      // 3D volume (NIfTI/DICOM) → the VTK viewer with the labelsTr mask as a toggleable overlay.
      const { url } = await fileDownloadUrl(datasetId, imgSource.id);
      const file = await fetchAsFile(url, imgSource.filename);
      const masks: AvailableMask[] = c.labelFile
        ? [{ id: c.labelFile.id, organLabel: 'Mặt nạ ROI', logicalPath: c.labelFile.filename, multiLabel: true }]
        : [];
      setImageViewer(null); // ensure only one viewer dialog is mounted at a time
      setViewer({ file, name: imgSource.filename, masks });
    } catch {
      toast.error('Không tải được ảnh để xem.');
    } finally {
      setOpeningCase(null);
    }
  };

  const filtered = useMemo(() => {
    return model.cases.filter((c) => {
      const mq = !query.trim() || c.caseId.toLowerCase().includes(query.trim().toLowerCase());
      const ms = statusFilter === 'all' || caseStatus(c, model.meta) === statusFilter;
      return mq && ms;
    });
  }, [model, query, statusFilter]);

  const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
  const safePage = Math.min(page, totalPages);
  const pageRows = filtered.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE);
  const detail = detailId ? model.cases.find((c) => c.caseId === detailId) ?? null : null;

  const toggle = (id: string) => {
    setExpanded((s) => {
      const n = new Set(s);
      n.has(id) ? n.delete(id) : n.add(id);
      return n;
    });
  };

  return (
    <>
    <div className="flex gap-4">
      <div className="min-w-0 flex-1 space-y-3">
        <div className="flex flex-wrap items-center gap-2">
          <div className="relative min-w-0 flex-1">
            <Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
            <Input
              value={query}
              onChange={(e) => {
                setQuery(e.target.value);
                setPage(1);
              }}
              placeholder="Tìm theo mã ca…"
              className="pl-9"
            />
          </div>
          <div className="flex items-center gap-1">
            {(['all', 'valid', 'warn', 'error'] as const).map((s) => (
              <Button
                key={s}
                size="sm"
                variant={statusFilter === s ? 'default' : 'outline'}
                onClick={() => {
                  setStatusFilter(s);
                  setPage(1);
                }}
              >
                {s === 'all' ? 'Tất cả' : STATUS_META[s].label}
              </Button>
            ))}
          </div>
        </div>

        <Card>
          <CardContent className="p-0">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead className="w-8" />
                  <TableHead>Ca dữ liệu</TableHead>
                  <TableHead>Nhãn</TableHead>
                  <TableHead>Tình trạng</TableHead>
                  <TableHead className="w-12 text-right">Xem</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {pageRows.map((c) => {
                  const status = caseStatus(c, model.meta);
                  const isOpen = expanded.has(c.caseId);
                  const files = caseFiles(c, model.meta);
                  return (
                    <Fragment key={c.caseId}>
                      <TableRow
                        className="cursor-pointer"
                        onClick={() => setDetailId(c.caseId)}
                      >
                        <TableCell onClick={(e) => { e.stopPropagation(); toggle(c.caseId); }}>
                          <button className="rounded p-0.5 text-muted-foreground hover:bg-muted">
                            {isOpen ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
                          </button>
                        </TableCell>
                        <TableCell>
                          <span className="inline-flex items-center gap-2">
                            <Boxes className="h-4 w-4 text-muted-foreground" />
                            <span className="font-mono font-medium text-foreground">{c.caseId}</span>
                          </span>
                        </TableCell>
                        <TableCell>
                          {c.labelFile ? (
                            <span className="inline-flex items-center gap-1 text-xs text-emerald-600">
                              <CheckCircle2 className="h-3.5 w-3.5" /> có
                            </span>
                          ) : (
                            <span className="text-xs text-muted-foreground">chưa có</span>
                          )}
                        </TableCell>
                        <TableCell><StatusPill status={status} /></TableCell>
                        <TableCell className="text-right">
                          <button
                            type="button"
                            onClick={(e) => {
                              e.stopPropagation();
                              void openCaseViewer(c);
                            }}
                            disabled={openingCase === c.caseId}
                            title="Xem"
                            className="rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50"
                          >
                            {openingCase === c.caseId ? (
                              <Loader2 className="h-4 w-4 animate-spin" />
                            ) : (
                              <Eye className="h-4 w-4" />
                            )}
                          </button>
                        </TableCell>
                      </TableRow>
                      {isOpen &&
                        files.map((f) => (
                          <TableRow key={f.id} className="bg-muted/40">
                            <TableCell />
                            <TableCell>
                              <span className="font-mono text-[11px] text-muted-foreground">{f.filename}</span>
                            </TableCell>
                            <TableCell>
                              <span className="text-[11px] text-muted-foreground">
                                {f.role === 'label' ? 'nhãn' : `kênh ${f.channelIndex ?? 0}`}
                              </span>
                            </TableCell>
                            <TableCell colSpan={2} className="text-right font-mono text-[11px] text-muted-foreground">
                              {formatBytes(f.sizeBytes)}
                            </TableCell>
                          </TableRow>
                        ))}
                    </Fragment>
                  );
                })}
                {pageRows.length === 0 && (
                  <TableRow>
                    <TableCell colSpan={5} className="py-12 text-center text-sm text-muted-foreground">
                      Không có ca nào phù hợp.
                    </TableCell>
                  </TableRow>
                )}
              </TableBody>
            </Table>
          </CardContent>
        </Card>

        {filtered.length > PAGE_SIZE && (
          <div className="flex items-center justify-between text-xs text-muted-foreground">
            <span>
              {(safePage - 1) * PAGE_SIZE + 1}–{Math.min(safePage * PAGE_SIZE, filtered.length)} / {filtered.length} ca
            </span>
            <div className="flex items-center gap-1">
              <Button size="sm" variant="outline" disabled={safePage === 1} onClick={() => setPage((p) => p - 1)}>
                Trước
              </Button>
              <span className="px-2 font-mono">{safePage}/{totalPages}</span>
              <Button size="sm" variant="outline" disabled={safePage === totalPages} onClick={() => setPage((p) => p + 1)}>
                Sau
              </Button>
            </div>
          </div>
        )}
      </div>

      {detail && <DetailAside c={detail} model={model} onClose={() => setDetailId(null)} />}
    </div>
    {viewer && (
      <Suspense fallback={null}>
        <DatasetFileViewerDialog
          open
          onOpenChange={(o) => !o && setViewer(null)}
          file={viewer.file}
          fileName={viewer.name}
          datasetId={datasetId}
          masks={viewer.masks}
          labelMap={labelMap}
          onRenameLabel={renameLabel}
        />
      </Suspense>
    )}
    {imageViewer && (
      <Dialog open onOpenChange={(o) => !o && setImageViewer(null)}>
        <DialogContent className="w-[96vw] max-w-[1500px] p-0">
          <DialogHeader className="border-b border-border px-4 py-3">
            <DialogTitle>Xem ca: {imageViewer.caseId}</DialogTitle>
          </DialogHeader>
          <div className="h-[85vh] w-full bg-black">
            <ImageSequenceViewer
              original={
                imageViewer.original.length ? { urls: imageViewer.original, label: 'Ảnh gốc' } : undefined
              }
              depth={imageViewer.depth.length ? { urls: imageViewer.depth, label: 'Bản đồ độ sâu' } : undefined}
              segmentation={
                imageViewer.segmentation.length
                  ? { urls: imageViewer.segmentation, label: 'Mặt nạ phân vùng', opacity: 0.6 }
                  : undefined
              }
            />
          </div>
        </DialogContent>
      </Dialog>
    )}
    </>
  );
}

function DetailAside({ c, model, onClose }: { c: DatasetCase; model: WorkspaceModel; onClose: () => void }) {
  const issues = model.report.issues.filter((i) => i.caseId === c.caseId);
  return (
    <Card className="w-80 shrink-0">
      <CardContent className="space-y-4 p-4">
        <div className="flex items-start justify-between">
          <span className="inline-flex items-center gap-2">
            <Boxes className="h-4 w-4 text-muted-foreground" />
            <span className="font-mono font-semibold text-foreground">{c.caseId}</span>
          </span>
          <button onClick={onClose} className="text-muted-foreground hover:text-foreground">
            <XCircle className="h-4 w-4" />
          </button>
        </div>

        <div>
          <div className="mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
            <ScanLine className="h-3.5 w-3.5" /> Kênh
          </div>
          <div className="space-y-1">
            {channelPresence(c, model.meta).map(({ channel, present }) => (
              <div key={channel.index} className="flex items-center justify-between rounded-md border border-border px-2.5 py-1.5 text-xs">
                <span className="font-mono">{String(channel.index).padStart(4, '0')} · {channel.name}</span>
                {present ? (
                  <CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
                ) : (
                  <span className="text-[11px] font-medium text-rose-600">thiếu</span>
                )}
              </div>
            ))}
          </div>
        </div>

        <div>
          <div className="mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
            <FileText className="h-3.5 w-3.5" /> Tệp
          </div>
          <div className="space-y-1">
            {caseFiles(c, model.meta).map((f) => (
              <div key={f.id} className="flex items-center justify-between gap-2 text-xs">
                <span className="truncate font-mono text-[11px] text-muted-foreground">{f.filename}</span>
                <span className="shrink-0 font-mono text-[11px] text-muted-foreground">{formatBytes(f.sizeBytes)}</span>
              </div>
            ))}
          </div>
        </div>

        {issues.length > 0 && (
          <div>
            <div className="mb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
              Vấn đề
            </div>
            <ul className="space-y-1">
              {issues.map((i, k) => (
                <li key={k} className={cn('text-xs', i.level === 'error' ? 'text-rose-700' : 'text-amber-700')}>
                  · {i.message}
                </li>
              ))}
            </ul>
          </div>
        )}
      </CardContent>
    </Card>
  );
}

/* ----------------------------- Config tab ------------------------------- */
function ConfigTab({ model }: { model: WorkspaceModel }) {
  const json = useMemo(() => datasetJsonPreview(model), [model]);
  return (
    <div className="space-y-4">
      <InferredBanner />
      <Card>
        <CardContent className="p-0">
          <pre className="overflow-auto rounded-xl bg-foreground p-4 font-mono text-[11px] leading-relaxed text-background">
            {JSON.stringify(json, null, 2)}
          </pre>
        </CardContent>
      </Card>
    </div>
  );
}

/* --------------------------- Validation tab ----------------------------- */
function ValidationTab({ model }: { model: WorkspaceModel }) {
  const { issues } = model.report;
  if (issues.length === 0) {
    return (
      <Card>
        <CardContent className="flex flex-col items-center gap-2 py-16 text-center">
          <CheckCircle2 className="h-6 w-6 text-emerald-600" />
          <p className="text-sm font-medium text-foreground">Mọi ca đều hợp lệ.</p>
        </CardContent>
      </Card>
    );
  }
  return (
    <div className="space-y-3">
      <InferredBanner />
      <Card>
        <CardContent className="p-0">
          {issues.map((i, k) => (
            <div key={k} className={cn('flex items-start gap-3 px-4 py-3', k ? 'border-t border-border' : '')}>
              {i.level === 'error' ? (
                <XCircle className="mt-0.5 h-4 w-4 shrink-0 text-rose-600" />
              ) : (
                <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-amber-600" />
              )}
              <div className="min-w-0">
                <div className="text-sm text-foreground">{i.message}</div>
                <div className="font-mono text-[11px] text-muted-foreground">
                  {i.code}
                  {i.caseId ? ` · ${i.caseId}` : ''}
                </div>
              </div>
            </div>
          ))}
        </CardContent>
      </Card>
    </div>
  );
}

/* ---------------------------- Versions tab ------------------------------ */
function VersionsTab({ datasetId }: { datasetId: string }) {
  const q = useQuery({
    queryKey: ['imagehub', 'dataset', datasetId, 'versions'],
    queryFn: () => listDatasetVersions(datasetId),
    enabled: !!datasetId,
  });
  if (q.isLoading) return <Skeleton className="h-32 w-full" />;
  const versions = q.data ?? [];
  if (versions.length === 0) {
    return (
      <Card>
        <CardContent className="py-12 text-center text-sm text-muted-foreground">
          Chưa có phiên bản nào được lưu.
        </CardContent>
      </Card>
    );
  }
  return (
    <Card>
      <CardContent className="p-0">
        {versions.map((v, k) => (
          <div key={v.id} className={cn('flex items-start gap-3 px-4 py-3', k ? 'border-t border-border' : '')}>
            <Badge variant="outline" className="font-mono">v{v.seq}</Badge>
            <div className="min-w-0 flex-1">
              <div className="text-sm text-foreground">{v.message || '(không có mô tả)'}</div>
              <div className="font-mono text-[11px] text-muted-foreground">
                {v.fileCount} tệp{v.createdAt ? ` · ${new Date(v.createdAt).toLocaleString('vi-VN')}` : ''}
              </div>
            </div>
          </div>
        ))}
      </CardContent>
    </Card>
  );
}

function InferredBanner() {
  return (
    <div className="flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800">
      <Info className="mt-0.5 h-3.5 w-3.5 shrink-0" />
      <span>
        Mô tả dữ liệu (kênh / nhãn) được <strong>suy ra từ tên tệp</strong>, chưa được lưu cùng bộ dữ liệu. Dùng
        trình tổ chức dữ liệu để khai báo chính thức.
      </span>
    </div>
  );
}

/* ------------------------------- main view ------------------------------ */
export function DatasetWorkspaceView({ datasetId }: { datasetId: string }) {
  const dsQ = useQuery({
    queryKey: ['imagehub', 'dataset', datasetId],
    queryFn: () => getDataset(datasetId),
    enabled: !!datasetId,
  });
  const filesQ = useQuery({
    queryKey: ['imagehub', 'dataset', datasetId, 'files'],
    queryFn: () => listDatasetFiles(datasetId),
    enabled: !!datasetId,
  });

  const model = useMemo(
    () => buildWorkspaceModel(dsQ.data?.name ?? '', filesQ.data ?? []),
    [dsQ.data?.name, filesQ.data],
  );

  if (filesQ.isLoading || dsQ.isLoading) {
    return <Skeleton className="h-64 w-full" />;
  }

  const fileCount = filesQ.data?.length ?? 0;

  if (fileCount === 0) {
    return (
      <Card>
        <CardContent className="flex flex-col items-center gap-3 py-16 text-center">
          <Layers className="h-7 w-7 text-muted-foreground" />
          <p className="text-sm font-medium text-foreground">Bộ dữ liệu này chưa có tệp nào.</p>
          <p className="text-xs text-muted-foreground">Tải ảnh gốc và nhãn để bắt đầu.</p>
          <FolderUploadButtons datasetId={datasetId} />
        </CardContent>
      </Card>
    );
  }

  return (
    <div className="space-y-4">
      <Tabs defaultValue="files">
        <TabsList>
          <TabsTrigger value="files">
            <FolderTree className="mr-1.5 h-4 w-4" /> Thư mục
          </TabsTrigger>
          <TabsTrigger value="cases">
            <Boxes className="mr-1.5 h-4 w-4" /> Ca dữ liệu
          </TabsTrigger>
          <TabsTrigger value="config">
            <Braces className="mr-1.5 h-4 w-4" /> dataset.json
          </TabsTrigger>
          <TabsTrigger value="validation">
            <AlertTriangle className="mr-1.5 h-4 w-4" /> Kiểm tra
            {model.report.issues.length > 0 && (
              <span className="ml-1.5 rounded-full bg-muted px-1.5 text-[11px]">{model.report.issues.length}</span>
            )}
          </TabsTrigger>
          <TabsTrigger value="versions">
            <Layers className="mr-1.5 h-4 w-4" /> Phiên bản
          </TabsTrigger>
        </TabsList>

        <TabsContent value="files" className="mt-4 space-y-3">
          <div className="flex flex-wrap items-center gap-2">
            <FolderUploadButtons datasetId={datasetId} />
            <NormalizeFilesButton datasetId={datasetId} />
          </div>
          <FolderTreeView files={filesQ.data ?? []} />
        </TabsContent>
        <TabsContent value="cases" className="mt-4">
          <CasesTab model={model} datasetId={datasetId} />
        </TabsContent>
        <TabsContent value="config" className="mt-4">
          <ConfigTab model={model} />
        </TabsContent>
        <TabsContent value="validation" className="mt-4">
          <ValidationTab model={model} />
        </TabsContent>
        <TabsContent value="versions" className="mt-4">
          <VersionsTab datasetId={datasetId} />
        </TabsContent>
      </Tabs>
    </div>
  );
}
