"use client";

import { useState, useEffect, useCallback, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import {
  CheckCircle2,
  AlertCircle,
  Loader2,
  RefreshCw,
  Upload,
  Search,
  ChevronDown,
} from "lucide-react";

// ── Types ──────────────────────────────────────────────────────────────────────

interface FileEntry {
  key: string;
  filename: string;
  folder: string;
  sizeBytes: number;
  hetznerExists: boolean;
}

type MigrateStatus = "idle" | "uploading" | "done" | "error";

function formatBytes(bytes: number) {
  if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(1)} GB`;
  if (bytes >= 1024 ** 2) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
  return `${(bytes / 1024).toFixed(1)} KB`;
}

// ── Page ───────────────────────────────────────────────────────────────────────

export default function MigrationPage() {
  const [files, setFiles] = useState<FileEntry[]>([]);
  const [loading, setLoading] = useState(true);
  const [hetznerConfigured, setHetznerConfigured] = useState(true);
  const [fetchError, setFetchError] = useState<string | null>(null);

  // Selection: Set of keys
  const [selected, setSelected] = useState<Set<string>>(new Set());

  // Per-file migration status
  const [statuses, setStatuses] = useState<Map<string, MigrateStatus>>(new Map());
  const [errors, setErrors] = useState<Map<string, string>>(new Map());

  // Filters
  const [search, setSearch] = useState("");
  const [folderFilter, setFolderFilter] = useState("all");
  const [folderMenuOpen, setFolderMenuOpen] = useState(false);

  const [migrating, setMigrating] = useState(false);

  // ── Load file list ──────────────────────────────────────────────────────────

  const loadFiles = useCallback(async () => {
    setLoading(true);
    setFetchError(null);
    try {
      const res = await fetch("/api/admin/migration");
      const data = await res.json();
      if (!res.ok) throw new Error(data.error ?? "Failed to load");
      setFiles(data.files);
      setHetznerConfigured(data.hetznerConfigured);
      // Pre-select all files NOT yet on Hetzner
      const preSelected = new Set<string>(
        (data.files as FileEntry[])
          .filter((f) => !f.hetznerExists)
          .map((f) => f.key)
      );
      setSelected(preSelected);
      setStatuses(new Map());
      setErrors(new Map());
    } catch (e: unknown) {
      setFetchError(e instanceof Error ? e.message : "Failed to load file list");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { loadFiles(); }, [loadFiles]);

  // ── Derived data ────────────────────────────────────────────────────────────

  const folders = useMemo(
    () => ["all", ...Array.from(new Set(files.map((f) => f.folder))).sort()],
    [files]
  );

  const filtered = useMemo(() => {
    return files.filter((f) => {
      const matchFolder = folderFilter === "all" || f.folder === folderFilter;
      const matchSearch =
        !search || f.filename.toLowerCase().includes(search.toLowerCase());
      return matchFolder && matchSearch;
    });
  }, [files, folderFilter, search]);

  const stats = useMemo(() => {
    const synced = files.filter((f) => f.hetznerExists).length;
    const total = files.length;
    const totalBytes = files.reduce((a, f) => a + f.sizeBytes, 0);
    const selectedBytes = files
      .filter((f) => selected.has(f.key))
      .reduce((a, f) => a + f.sizeBytes, 0);
    return { synced, total, totalBytes, selectedBytes };
  }, [files, selected]);

  // ── Selection helpers ───────────────────────────────────────────────────────

  const toggleOne = (key: string) => {
    setSelected((prev) => {
      const next = new Set(prev);
      next.has(key) ? next.delete(key) : next.add(key);
      return next;
    });
  };

  const selectAll = () =>
    setSelected(new Set(filtered.map((f) => f.key)));

  const deselectAll = () =>
    setSelected((prev) => {
      const next = new Set(prev);
      filtered.forEach((f) => next.delete(f.key));
      return next;
    });

  const selectUnsynced = () =>
    setSelected(new Set(filtered.filter((f) => !f.hetznerExists).map((f) => f.key)));

  // ── Migration ───────────────────────────────────────────────────────────────

  const handleMigrate = async () => {
    const toMigrate = files.filter((f) => selected.has(f.key));
    if (!toMigrate.length) return;
    setMigrating(true);

    for (const file of toMigrate) {
      setStatuses((prev) => new Map(prev).set(file.key, "uploading"));
      try {
        const res = await fetch("/api/admin/migration", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ key: file.key }),
        });
        const data = await res.json();
        if (!res.ok) throw new Error(data.error ?? "Upload failed");
        setStatuses((prev) => new Map(prev).set(file.key, "done"));
        // Mark as synced in the file list
        setFiles((prev) =>
          prev.map((f) => (f.key === file.key ? { ...f, hetznerExists: true } : f))
        );
      } catch (e: unknown) {
        setStatuses((prev) => new Map(prev).set(file.key, "error"));
        setErrors((prev) =>
          new Map(prev).set(file.key, e instanceof Error ? e.message : "Failed")
        );
      }
    }

    setMigrating(false);
  };

  // ── Render ──────────────────────────────────────────────────────────────────

  if (loading) {
    return (
      <div className="flex items-center justify-center h-64">
        <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
        <span className="ml-2 text-sm text-muted-foreground">Scanning files…</span>
      </div>
    );
  }

  if (fetchError) {
    return (
      <div className="p-6 max-w-2xl">
        <div className="flex items-center gap-2 text-sm text-red-400 rounded-md border border-red-500/20 bg-red-500/10 px-3 py-2">
          <AlertCircle className="h-4 w-4 shrink-0" />
          {fetchError}
        </div>
        <Button variant="outline" size="sm" className="mt-3" onClick={loadFiles}>
          Retry
        </Button>
      </div>
    );
  }

  if (!hetznerConfigured) {
    return (
      <div className="p-6 max-w-2xl">
        <div className="flex items-center gap-2 text-sm text-amber-400 rounded-md border border-amber-500/20 bg-amber-500/10 px-3 py-2">
          <AlertCircle className="h-4 w-4 shrink-0" />
          Hetzner object storage is not configured. Set{" "}
          <code className="font-mono text-xs">HETZNER_ENDPOINT</code>,{" "}
          <code className="font-mono text-xs">HETZNER_ACCESS_KEY</code>,{" "}
          <code className="font-mono text-xs">HETZNER_SECRET_KEY</code>, and{" "}
          <code className="font-mono text-xs">HETZNER_BUCKET_NAME</code> in your environment,
          or configure them in Settings.
        </div>
      </div>
    );
  }

  const allFilteredSelected = filtered.length > 0 && filtered.every((f) => selected.has(f.key));

  return (
    <div className="p-6 space-y-4 max-w-5xl">
      {/* Header */}
      <div className="flex items-start justify-between gap-4">
        <div>
          <h1 className="text-xl font-semibold">Storage Migration</h1>
          <p className="text-sm text-muted-foreground mt-0.5">
            {stats.total} local files · {formatBytes(stats.totalBytes)} total ·{" "}
            <span className="text-emerald-400">{stats.synced} already on Hetzner</span>
          </p>
        </div>
        <Button variant="outline" size="sm" onClick={loadFiles} disabled={migrating} className="gap-1.5 shrink-0">
          <RefreshCw className="h-3.5 w-3.5" />
          Refresh
        </Button>
      </div>

      {/* Toolbar */}
      <div className="flex flex-wrap items-center gap-2">
        {/* Search */}
        <div className="relative flex-1 min-w-[180px]">
          <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
          <Input
            placeholder="Search filenames…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            className="pl-8 h-8 text-sm"
          />
        </div>

        {/* Folder filter */}
        <div className="relative">
          <button
            onClick={() => setFolderMenuOpen((o) => !o)}
            className="flex items-center gap-1.5 h-8 px-3 text-sm rounded-md border border-input bg-background hover:bg-muted transition-colors"
          >
            {folderFilter === "all" ? "All folders" : folderFilter}
            <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
          </button>
          {folderMenuOpen && (
            <div className="absolute top-full left-0 mt-1 z-10 rounded-md border border-border bg-popover shadow-md py-1 min-w-[140px]">
              {folders.map((f) => (
                <button
                  key={f}
                  onClick={() => { setFolderFilter(f); setFolderMenuOpen(false); }}
                  className={cn(
                    "w-full text-left px-3 py-1.5 text-sm hover:bg-muted transition-colors",
                    folderFilter === f && "text-primary font-medium"
                  )}
                >
                  {f === "all" ? "All folders" : f}
                </button>
              ))}
            </div>
          )}
        </div>

        {/* Bulk actions */}
        <Button variant="outline" size="sm" className="h-8" onClick={allFilteredSelected ? deselectAll : selectAll}>
          {allFilteredSelected ? "Deselect all" : "Select all"}
        </Button>
        <Button variant="outline" size="sm" className="h-8" onClick={selectUnsynced}>
          Select unsynced
        </Button>
      </div>

      {/* Table */}
      <div className="rounded-md border border-border overflow-hidden">
        <table className="w-full text-sm">
          <thead className="bg-muted/50">
            <tr>
              <th className="w-10 px-3 py-2">
                <input
                  type="checkbox"
                  checked={allFilteredSelected}
                  onChange={allFilteredSelected ? deselectAll : selectAll}
                  className="rounded accent-primary"
                />
              </th>
              <th className="text-left px-3 py-2 font-medium text-muted-foreground">Filename</th>
              <th className="text-left px-3 py-2 font-medium text-muted-foreground w-24">Folder</th>
              <th className="text-right px-3 py-2 font-medium text-muted-foreground w-24">Size</th>
              <th className="text-right px-3 py-2 font-medium text-muted-foreground w-28">Status</th>
            </tr>
          </thead>
          <tbody className="divide-y divide-border">
            {filtered.length === 0 && (
              <tr>
                <td colSpan={5} className="text-center py-10 text-muted-foreground text-sm">
                  No files match your filters.
                </td>
              </tr>
            )}
            {filtered.map((file) => {
              const status = statuses.get(file.key);
              const err = errors.get(file.key);
              const isSelected = selected.has(file.key);

              return (
                <tr
                  key={file.key}
                  onClick={() => !migrating && toggleOne(file.key)}
                  className={cn(
                    "transition-colors cursor-pointer",
                    isSelected ? "bg-primary/5 hover:bg-primary/10" : "hover:bg-muted/40"
                  )}
                >
                  {/* Checkbox */}
                  <td className="px-3 py-2" onClick={(e) => e.stopPropagation()}>
                    <input
                      type="checkbox"
                      checked={isSelected}
                      onChange={() => !migrating && toggleOne(file.key)}
                      disabled={migrating}
                      className="rounded accent-primary"
                    />
                  </td>

                  {/* Filename */}
                  <td className="px-3 py-2 font-mono text-xs text-foreground max-w-xs truncate">
                    {file.filename}
                    {err && (
                      <span className="ml-2 text-red-400 font-sans not-italic">{err}</span>
                    )}
                  </td>

                  {/* Folder */}
                  <td className="px-3 py-2 text-muted-foreground">{file.folder}</td>

                  {/* Size */}
                  <td className="px-3 py-2 text-right text-muted-foreground tabular-nums">
                    {formatBytes(file.sizeBytes)}
                  </td>

                  {/* Status */}
                  <td className="px-3 py-2 text-right">
                    {status === "uploading" ? (
                      <span className="inline-flex items-center gap-1 text-amber-400 text-xs">
                        <Loader2 className="h-3 w-3 animate-spin" />
                        Uploading…
                      </span>
                    ) : status === "done" || (status === undefined && file.hetznerExists) ? (
                      <span className="inline-flex items-center gap-1 text-emerald-400 text-xs">
                        <CheckCircle2 className="h-3 w-3" />
                        Synced
                      </span>
                    ) : status === "error" ? (
                      <span className="inline-flex items-center gap-1 text-red-400 text-xs">
                        <AlertCircle className="h-3 w-3" />
                        Failed
                      </span>
                    ) : (
                      <Badge variant="outline" className="text-xs text-muted-foreground">
                        Local only
                      </Badge>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      {/* Footer action bar */}
      {selected.size > 0 && (
        <div className="sticky bottom-4 flex items-center justify-between gap-4 rounded-lg border border-border bg-background/95 backdrop-blur px-4 py-3 shadow-lg">
          <span className="text-sm text-muted-foreground">
            <span className="font-semibold text-foreground">{selected.size} files</span> selected ·{" "}
            {formatBytes(stats.selectedBytes)}
          </span>
          <Button
            onClick={handleMigrate}
            disabled={migrating}
            className="gap-2"
          >
            {migrating ? (
              <>
                <Loader2 className="h-4 w-4 animate-spin" />
                Migrating…
              </>
            ) : (
              <>
                <Upload className="h-4 w-4" />
                Migrate {selected.size} {selected.size === 1 ? "file" : "files"} to Hetzner
              </>
            )}
          </Button>
        </div>
      )}
    </div>
  );
}
