"use client";

import { useEffect, useState, useCallback, useRef } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from "@/components/ui/context-menu";
import {
  FolderOpen,
  File,
  FileText,
  ChevronRight,
  Upload,
  Download,
  Trash2,
  RefreshCw,
  ArrowLeft,
  Home,
} from "lucide-react";
import { toast } from "sonner";
import { useDropzone } from "react-dropzone";
import { formatDistanceToNow } from "date-fns";
import dynamic from "next/dynamic";

const MonacoEditor = dynamic(() => import("@monaco-editor/react"), { ssr: false });

interface FileEntry {
  name: string;
  path: string;
  isDirectory: boolean;
  size: number;
  modifiedAt: number;
}

function formatBytes(bytes: number): string {
  if (bytes === 0) return "—";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
}

const TEXT_EXTENSIONS = new Set([".txt", ".yml", ".yaml", ".json", ".properties", ".toml", ".cfg", ".conf", ".log", ".md", ".sh", ".ini"]);

function getEditorLanguage(name: string): string {
  const ext = name.split(".").pop() ?? "";
  const map: Record<string, string> = {
    json: "json", yml: "yaml", yaml: "yaml",
    properties: "ini", toml: "ini", cfg: "ini",
    sh: "shell", md: "markdown", log: "plaintext",
  };
  return map[ext] ?? "plaintext";
}

function isEditable(name: string): boolean {
  const ext = "." + name.split(".").pop()?.toLowerCase();
  return TEXT_EXTENSIONS.has(ext);
}

export default function FilesPage() {
  const [currentPath, setCurrentPath] = useState("/");
  const [entries, setEntries] = useState<FileEntry[]>([]);
  const [loading, setLoading] = useState(true);
  const [deleteTarget, setDeleteTarget] = useState<FileEntry | null>(null);
  const [editFile, setEditFile] = useState<{ path: string; content: string } | null>(null);
  const [saving, setSaving] = useState(false);

  const fetchEntries = useCallback(async (path: string) => {
    setLoading(true);
    try {
      const res = await fetch(`/api/files/list?path=${encodeURIComponent(path)}`);
      if (!res.ok) throw new Error((await res.json()).error);
      const data = await res.json();
      setEntries(data.entries);
      setCurrentPath(data.path);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to load directory");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { fetchEntries("/"); }, [fetchEntries]);

  const handleOpen = (entry: FileEntry) => {
    if (entry.isDirectory) {
      fetchEntries(entry.path);
    } else if (isEditable(entry.name)) {
      openEditor(entry);
    } else {
      window.open(`/api/files/download?path=${encodeURIComponent(entry.path)}`, "_blank");
    }
  };

  const openEditor = async (entry: FileEntry) => {
    try {
      const res = await fetch(`/api/files/download?path=${encodeURIComponent(entry.path)}`);
      const text = await res.text();
      setEditFile({ path: entry.path, content: text });
    } catch {
      toast.error("Failed to open file");
    }
  };

  const saveFile = async () => {
    if (!editFile) return;
    setSaving(true);
    try {
      const blob = new Blob([editFile.content], { type: "text/plain" });
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const FileClass = (globalThis as any).File ?? Blob;
      const file = new FileClass([blob], editFile.path.split("/").pop() ?? "file") as File;
      const dir = editFile.path.split("/").slice(0, -1).join("/") || "/";
      const form = new FormData();
      form.append("file", file);
      const res = await fetch(`/api/files/upload?path=${encodeURIComponent(dir)}`, {
        method: "POST",
        body: form,
      });
      if (!res.ok) throw new Error((await res.json()).error);
      toast.success("File saved");
      setEditFile(null);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Save failed");
    } finally {
      setSaving(false);
    }
  };

  const handleDelete = async (entry: FileEntry) => {
    try {
      const res = await fetch("/api/files/delete", {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ filePath: entry.path }),
      });
      if (!res.ok) throw new Error((await res.json()).error);
      toast.success(`${entry.name} deleted`);
      fetchEntries(currentPath);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Delete failed");
    }
    setDeleteTarget(null);
  };

  const { getRootProps, getInputProps, isDragActive, open: openUpload } = useDropzone({
    noClick: true,
    noKeyboard: true,
    onDrop: async (acceptedFiles) => {
      for (const file of acceptedFiles) {
        const form = new FormData();
        form.append("file", file);
        try {
          const res = await fetch(`/api/files/upload?path=${encodeURIComponent(currentPath)}`, {
            method: "POST",
            body: form,
          });
          if (!res.ok) throw new Error((await res.json()).error);
          toast.success(`${file.name} uploaded`);
        } catch (err) {
          toast.error(`Failed to upload ${file.name}`);
        }
      }
      fetchEntries(currentPath);
    },
  });

  // Breadcrumbs
  const parts = currentPath === "/" ? [] : currentPath.split("/").filter(Boolean);
  const navigateUp = () => {
    if (parts.length === 0) return;
    const parent = parts.length === 1 ? "/" : "/" + parts.slice(0, -1).join("/");
    fetchEntries(parent);
  };

  if (editFile) {
    const fileName = editFile.path.split("/").pop() ?? "file";
    return (
      <div className="p-6 h-full flex flex-col gap-4">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setEditFile(null)}
              className="border-zinc-700 text-zinc-400 hover:text-white"
            >
              <ArrowLeft className="w-4 h-4 mr-1.5" />
              Back
            </Button>
            <div>
              <h1 className="text-lg font-bold text-white">{fileName}</h1>
              <p className="text-xs text-zinc-500 font-mono">{editFile.path}</p>
            </div>
          </div>
          <Button
            onClick={saveFile}
            disabled={saving}
            className="bg-emerald-600 hover:bg-emerald-500 text-white"
            size="sm"
          >
            {saving ? "Saving..." : "Save File"}
          </Button>
        </div>
        <div className="flex-1 rounded-xl overflow-hidden border border-zinc-800" style={{ minHeight: "500px" }}>
          <MonacoEditor
            height="100%"
            language={getEditorLanguage(fileName)}
            theme="vs-dark"
            value={editFile.content}
            onChange={(v) => setEditFile((prev) => prev ? { ...prev, content: v ?? "" } : null)}
            options={{
              fontSize: 13,
              minimap: { enabled: false },
              scrollBeyondLastLine: false,
              wordWrap: "on",
              padding: { top: 16, bottom: 16 },
            }}
          />
        </div>
      </div>
    );
  }

  return (
    <div className="p-6 space-y-4" {...getRootProps()}>
      <input {...getInputProps()} />

      {isDragActive && (
        <div className="fixed inset-0 z-50 bg-emerald-500/10 border-2 border-dashed border-emerald-500/50 flex items-center justify-center pointer-events-none">
          <div className="text-center text-emerald-400">
            <Upload className="w-12 h-12 mx-auto mb-2" />
            <p className="text-lg font-semibold">Drop files to upload</p>
          </div>
        </div>
      )}

      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-bold text-white">File Explorer</h1>
          <p className="text-zinc-400 text-sm mt-1">
            Browse and manage Minecraft server files
          </p>
        </div>
        <div className="flex gap-2">
          <Button
            variant="outline"
            size="sm"
            onClick={() => fetchEntries(currentPath)}
            className="border-zinc-700 text-zinc-400 hover:text-white"
          >
            <RefreshCw className="w-4 h-4" />
          </Button>
          <Button
            size="sm"
            onClick={openUpload}
            className="bg-emerald-600 hover:bg-emerald-500 text-white"
          >
            <Upload className="w-4 h-4 mr-1.5" />
            Upload
          </Button>
        </div>
      </div>

      {/* Breadcrumbs */}
      <div className="flex items-center gap-1 text-sm text-zinc-500">
        <Button
          variant="ghost"
          size="sm"
          onClick={() => fetchEntries("/")}
          className="h-7 px-2 text-zinc-400 hover:text-white"
        >
          <Home className="w-3.5 h-3.5" />
        </Button>
        {parts.map((part, i) => (
          <span key={i} className="flex items-center gap-1">
            <ChevronRight className="w-3.5 h-3.5" />
            <Button
              variant="ghost"
              size="sm"
              onClick={() => fetchEntries("/" + parts.slice(0, i + 1).join("/"))}
              className="h-7 px-2 text-zinc-400 hover:text-white"
            >
              {part}
            </Button>
          </span>
        ))}
      </div>

      <Card className="bg-zinc-900 border-zinc-800">
        <CardContent className="p-0">
          {loading ? (
            <div className="p-4 space-y-2">
              {Array.from({ length: 8 }).map((_, i) => (
                <Skeleton key={i} className="h-10 w-full bg-zinc-800" />
              ))}
            </div>
          ) : entries.length === 0 ? (
            <div className="flex flex-col items-center justify-center py-16 text-zinc-600">
              <FolderOpen className="w-10 h-10 mb-3 opacity-50" />
              <p>Empty directory</p>
            </div>
          ) : (
            <div className="divide-y divide-zinc-800">
              {parts.length > 0 && (
                <button
                  onClick={navigateUp}
                  className="flex items-center gap-3 px-4 py-2.5 w-full hover:bg-zinc-800/50 transition-colors text-left"
                >
                  <ArrowLeft className="w-4 h-4 text-zinc-600" />
                  <span className="text-sm text-zinc-500">..</span>
                </button>
              )}
              {entries.map((entry) => (
                <ContextMenu key={entry.path}>
                  <ContextMenuTrigger>
                    <button
                      onDoubleClick={() => handleOpen(entry)}
                      className="flex items-center gap-3 px-4 py-2.5 w-full hover:bg-zinc-800/50 transition-colors text-left"
                    >
                      {entry.isDirectory ? (
                        <FolderOpen className="w-4 h-4 text-amber-500 shrink-0" />
                      ) : isEditable(entry.name) ? (
                        <FileText className="w-4 h-4 text-blue-400 shrink-0" />
                      ) : (
                        <File className="w-4 h-4 text-zinc-500 shrink-0" />
                      )}
                      <span className="flex-1 text-sm text-zinc-300 truncate">
                        {entry.name}
                      </span>
                      {!entry.isDirectory && (
                        <span className="text-xs text-zinc-600 shrink-0">
                          {formatBytes(entry.size)}
                        </span>
                      )}
                      <span className="text-xs text-zinc-600 shrink-0 hidden sm:block">
                        {entry.modifiedAt
                          ? formatDistanceToNow(new Date(entry.modifiedAt), {
                              addSuffix: true,
                            })
                          : ""}
                      </span>
                    </button>
                  </ContextMenuTrigger>
                  <ContextMenuContent className="bg-zinc-900 border-zinc-700">
                    {entry.isDirectory ? (
                      <ContextMenuItem
                        onClick={() => fetchEntries(entry.path)}
                        className="text-zinc-300 focus:text-white focus:bg-zinc-800"
                      >
                        <FolderOpen className="w-4 h-4 mr-2" />
                        Open
                      </ContextMenuItem>
                    ) : (
                      <>
                        {isEditable(entry.name) && (
                          <ContextMenuItem
                            onClick={() => openEditor(entry)}
                            className="text-zinc-300 focus:text-white focus:bg-zinc-800"
                          >
                            <FileText className="w-4 h-4 mr-2" />
                            Edit
                          </ContextMenuItem>
                        )}
                        <ContextMenuItem
                          onClick={() =>
                            window.open(
                              `/api/files/download?path=${encodeURIComponent(entry.path)}`,
                              "_blank",
                            )
                          }
                          className="text-zinc-300 focus:text-white focus:bg-zinc-800"
                        >
                          <Download className="w-4 h-4 mr-2" />
                          Download
                        </ContextMenuItem>
                      </>
                    )}
                    <ContextMenuSeparator className="bg-zinc-700" />
                    <ContextMenuItem
                      onClick={() => setDeleteTarget(entry)}
                      className="text-red-400 focus:text-red-300 focus:bg-zinc-800"
                    >
                      <Trash2 className="w-4 h-4 mr-2" />
                      Delete
                    </ContextMenuItem>
                  </ContextMenuContent>
                </ContextMenu>
              ))}
            </div>
          )}
        </CardContent>
      </Card>

      <p className="text-xs text-zinc-600 text-center">
        Double-click to open files/folders • Right-click for options • Drag and drop to upload
      </p>

      <AlertDialog open={!!deleteTarget} onOpenChange={() => setDeleteTarget(null)}>
        <AlertDialogContent className="bg-zinc-900 border-zinc-700">
          <AlertDialogHeader>
            <AlertDialogTitle className="text-white">
              Delete {deleteTarget?.isDirectory ? "folder" : "file"}?
            </AlertDialogTitle>
            <AlertDialogDescription className="text-zinc-400">
              <span className="font-mono text-white">{deleteTarget?.name}</span>{" "}
              will be permanently deleted.
              {deleteTarget?.isDirectory && " All contents will be removed."}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel className="border-zinc-700 text-zinc-400">Cancel</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => deleteTarget && handleDelete(deleteTarget)}
              className="bg-red-600 hover:bg-red-500 text-white"
            >
              Delete
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  );
}
