import { LoadingSpinner } from "@ui/components/LoadingSpinner";
import { toast } from "@xtablo/shared";
import { Button } from "@xtablo/ui/components/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@xtablo/ui/components/dialog";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@xtablo/ui/components/dropdown-menu";
import {
  DownloadIcon,
  EllipsisVerticalIcon,
  FileTextIcon,
  FolderIcon,
  LayersIcon,
  PlusIcon,
  Trash2Icon,
} from "lucide-react";
import { useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useSearchParams } from "react-router-dom";
import {
  useAllTablosFileNames,
  useCreateTabloFile,
  useDeleteTabloFile,
  useDownloadTabloFile,
} from "../hooks/tablo_data";
import {
  extractFolderIdFromFileName,
  getFileNameWithoutFolder,
  getFolderFilePrefix,
  useTabloFolders,
} from "../hooks/tablo_folders";
import { useTablosList } from "../hooks/tablos";

// Derive icon color from file extension
function getFileIconColor(fileName: string): string {
  const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
  if (["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext)) return "bg-purple-500";
  if (ext === "pdf") return "bg-red-500";
  if (["xlsx", "xls", "csv"].includes(ext)) return "bg-green-600";
  if (["doc", "docx"].includes(ext)) return "bg-blue-500";
  return "bg-gray-500";
}

// ─── Upload Modal ────────────────────────────────────────────────────────────

function UploadModal({
  isOpen,
  onClose,
  tablos,
}: {
  isOpen: boolean;
  onClose: () => void;
  tablos: { id: string; name: string; color: string | null }[];
}) {
  const [selectedTabloId, setSelectedTabloId] = useState<string>(tablos[0]?.id ?? "");
  const [selectedFolderId, setSelectedFolderId] = useState<string>("");
  const [isUploading, setIsUploading] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const { data: foldersData } = useTabloFolders(selectedTabloId);
  const folders = foldersData?.folders ?? [];
  const createFile = useCreateTabloFile();

  const handleTabloChange = (tabloId: string) => {
    setSelectedTabloId(tabloId);
    setSelectedFolderId("");
  };

  const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file || !selectedTabloId) return;

    const maxSize = 20 * 1024 * 1024;
    if (file.size > maxSize) {
      toast.add(
        { title: "Erreur", description: "Le fichier ne peut pas dépasser 20MB", type: "error" },
        { timeout: 5000 }
      );
      return;
    }

    setIsUploading(true);
    try {
      const content = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (ev) => resolve(ev.target?.result as string);
        reader.onerror = reject;
        if (file.type.startsWith("text/") || file.type === "application/json") {
          reader.readAsText(file);
        } else {
          reader.readAsDataURL(file);
        }
      });

      const fileName = selectedFolderId
        ? `${getFolderFilePrefix(selectedFolderId)}${file.name}`
        : file.name;

      await createFile.mutateAsync({
        tabloId: selectedTabloId,
        fileName,
        data: { content, contentType: file.type || "application/octet-stream" },
      });

      onClose();
    } catch {
      // error handled by hook
    } finally {
      setIsUploading(false);
      if (fileInputRef.current) fileInputRef.current.value = "";
    }
  };

  return (
    <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>Upload a file</DialogTitle>
        </DialogHeader>
        <div className="space-y-4 pt-2">
          {/* Project selector */}
          <div className="space-y-1.5">
            <label className="text-sm font-medium text-gray-700 dark:text-gray-300">Project</label>
            <div className="grid grid-cols-1 gap-2 max-h-48 overflow-y-auto">
              {tablos.map((tablo) => (
                <button
                  key={tablo.id}
                  type="button"
                  onClick={() => handleTabloChange(tablo.id)}
                  className={`flex items-center gap-3 px-3 py-2 rounded-lg border text-left transition-colors ${
                    selectedTabloId === tablo.id
                      ? "border-primary bg-primary/5 dark:bg-primary/10"
                      : "border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800"
                  }`}
                >
                  <span
                    className={`w-6 h-6 rounded-md shrink-0 flex items-center justify-center text-xs font-bold text-white ${tablo.color ?? "bg-gray-500"}`}
                  >
                    {tablo.name.charAt(0).toUpperCase()}
                  </span>
                  <span className="text-sm text-gray-900 dark:text-gray-100 truncate">
                    {tablo.name}
                  </span>
                </button>
              ))}
            </div>
          </div>

          {/* Folder selector (optional) */}
          {folders.length > 0 && (
            <div className="space-y-1.5">
              <label className="text-sm font-medium text-gray-700 dark:text-gray-300">
                Folder (optional)
              </label>
              <div className="flex flex-wrap gap-2">
                <button
                  type="button"
                  onClick={() => setSelectedFolderId("")}
                  className={`px-3 py-1.5 rounded-lg border text-sm transition-colors ${
                    selectedFolderId === ""
                      ? "border-primary bg-primary/5 dark:bg-primary/10 text-primary"
                      : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800"
                  }`}
                >
                  No folder
                </button>
                {folders.map((folder) => (
                  <button
                    key={folder.id}
                    type="button"
                    onClick={() => setSelectedFolderId(folder.id)}
                    className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg border text-sm transition-colors ${
                      selectedFolderId === folder.id
                        ? "border-primary bg-primary/5 dark:bg-primary/10 text-primary"
                        : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800"
                    }`}
                  >
                    <FolderIcon className="w-3.5 h-3.5" />
                    {folder.name}
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* File picker */}
          <input ref={fileInputRef} type="file" className="hidden" onChange={handleFileSelect} />
          <Button
            className="w-full gap-2"
            onClick={() => fileInputRef.current?.click()}
            disabled={!selectedTabloId || isUploading}
          >
            {isUploading ? (
              <>
                <LoadingSpinner />
                Uploading...
              </>
            ) : (
              <>
                <PlusIcon className="w-4 h-4" />
                Choose file &amp; upload
              </>
            )}
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
}

// ─── Folder cards grid ───────────────────────────────────────────────────────

function FolderGrid({
  folders,
  folderMap,
}: {
  folders: { id: string; name: string; description?: string }[];
  folderMap: Map<string, string[]>;
}) {
  const visibleFolders = folders.filter((f) => (folderMap.get(f.id) ?? []).length > 0);
  if (visibleFolders.length === 0) return null;

  return (
    <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 mb-5">
      {visibleFolders.map((folder) => {
        const count = folderMap.get(folder.id)?.length ?? 0;
        return (
          <div
            key={folder.id}
            className="bg-white dark:bg-gray-800 rounded-xl p-4 border border-[#F2F4F7] dark:border-gray-700 hover:shadow-sm transition-all"
          >
            <div className="flex flex-col items-start gap-3">
              <div className="w-10 h-10 rounded-lg bg-amber-50 dark:bg-amber-900/20 flex items-center justify-center">
                <FolderIcon className="w-5 h-5 text-amber-500 dark:text-amber-400" />
              </div>
              <div className="min-w-0">
                <p className="font-semibold text-gray-900 dark:text-gray-100 text-sm leading-tight truncate">
                  {folder.name}
                </p>
                <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
                  {count} file{count !== 1 ? "s" : ""}
                </p>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── Per-project section ─────────────────────────────────────────────────────

function TabloFilesSection({
  tabloId,
  tabloName,
  tabloColor,
  fileNames,
}: {
  tabloId: string;
  tabloName: string;
  tabloColor: string | null;
  fileNames: string[];
}) {
  const { data: foldersData } = useTabloFolders(tabloId);
  const { mutate: downloadFile } = useDownloadTabloFile();
  const { mutate: deleteFile } = useDeleteTabloFile();

  const allFileNames = fileNames.filter((f) => !f.startsWith("."));
  const folders = foldersData?.folders ?? [];

  const folderMap = new Map<string, string[]>();
  const rootFiles: string[] = [];

  for (const fileName of allFileNames) {
    const folderId = extractFolderIdFromFileName(fileName);
    if (folderId) {
      if (!folderMap.has(folderId)) folderMap.set(folderId, []);
      folderMap.get(folderId)!.push(fileName);
    } else {
      rootFiles.push(fileName);
    }
  }

  if (allFileNames.length === 0) return null;

  return (
    <div className="mb-10">
      {/* Project header */}
      <div className="flex items-center gap-3 mb-4">
        <span
          className={`w-7 h-7 rounded-lg shrink-0 flex items-center justify-center text-sm font-bold text-white ${tabloColor ?? "bg-gray-500"}`}
        >
          {tabloName.charAt(0).toUpperCase()}
        </span>
        <Link
          to={`/tablos/${tabloId}`}
          className="text-lg font-semibold text-gray-900 dark:text-gray-100 hover:underline"
        >
          {tabloName}
        </Link>
        <span className="text-sm text-gray-400 dark:text-gray-500">
          {allFileNames.length} file{allFileNames.length !== 1 ? "s" : ""}
        </span>
      </div>

      {/* Folder cards */}
      <FolderGrid folders={folders} folderMap={folderMap} />

      {/* Files per folder */}
      {folders.map((folder) => {
        const folderFiles = folderMap.get(folder.id) ?? [];
        if (folderFiles.length === 0) return null;
        return (
          <div key={folder.id} className="mb-4">
            <div className="flex items-center gap-2 mb-2 px-1">
              <FolderIcon className="w-4 h-4 text-amber-500 dark:text-amber-400" />
              <span className="text-sm font-medium text-gray-700 dark:text-gray-300">
                {folder.name}
              </span>
              <span className="text-xs text-gray-400 dark:text-gray-500">
                ({folderFiles.length})
              </span>
            </div>
            <FileTable
              fileNames={folderFiles}
              onDownload={(fileName) => downloadFile({ tabloId, fileName })}
              onDelete={(fileName) => deleteFile({ tabloId, fileName })}
            />
          </div>
        );
      })}

      {/* Root files */}
      {rootFiles.length > 0 && (
        <>
          {folders.length > 0 && (
            <div className="flex items-center gap-2 mb-2 px-1">
              <span className="text-sm font-medium text-gray-700 dark:text-gray-300">
                Other files
              </span>
              <span className="text-xs text-gray-400 dark:text-gray-500">({rootFiles.length})</span>
            </div>
          )}
          <FileTable
            fileNames={rootFiles}
            onDownload={(fileName) => downloadFile({ tabloId, fileName })}
            onDelete={(fileName) => deleteFile({ tabloId, fileName })}
          />
        </>
      )}
    </div>
  );
}

// ─── File table ───────────────────────────────────────────────────────────────

function FileTable({
  fileNames,
  onDownload,
  onDelete,
}: {
  fileNames: string[];
  onDownload: (fileName: string) => void;
  onDelete: (fileName: string) => void;
}) {
  return (
    <div className="bg-white dark:bg-gray-800 rounded-2xl border border-gray-100 dark:border-gray-700 overflow-hidden mb-4">
      <div className="overflow-x-auto">
        <table className="w-full">
          <thead className="border-y border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/80">
            <tr>
              <th className="px-6 py-3 text-left text-sm font-normal text-gray-900 dark:text-gray-300">
                File name
              </th>
              <th className="px-6 py-3 w-12" />
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-100 dark:divide-gray-700">
            {fileNames.map((fileName) => {
              const displayName = getFileNameWithoutFolder(fileName);
              const iconColor = getFileIconColor(displayName);
              return (
                <tr
                  key={fileName}
                  className="hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors group"
                >
                  <td className="px-6 py-4">
                    <div className="flex items-center gap-3">
                      <div
                        className={`w-10 h-10 ${iconColor} rounded-lg flex items-center justify-center flex-shrink-0`}
                      >
                        <FileTextIcon className="w-5 h-5 text-white" />
                      </div>
                      <p className="text-sm font-normal text-gray-900 dark:text-gray-100">
                        {displayName}
                      </p>
                    </div>
                  </td>
                  <td className="px-6 py-4">
                    <DropdownMenu>
                      <DropdownMenuTrigger asChild>
                        <button
                          type="button"
                          className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600 rounded transition-colors opacity-0 group-hover:opacity-100"
                          aria-label={`Actions for ${displayName}`}
                        >
                          <EllipsisVerticalIcon className="w-5 h-5" />
                        </button>
                      </DropdownMenuTrigger>
                      <DropdownMenuContent align="end">
                        <DropdownMenuItem onClick={() => onDownload(fileName)}>
                          <DownloadIcon className="w-4 h-4 mr-2" />
                          Download
                        </DropdownMenuItem>
                        <DropdownMenuItem
                          onClick={() => onDelete(fileName)}
                          className="text-red-600 dark:text-red-400 focus:text-red-600 dark:focus:text-red-400"
                        >
                          <Trash2Icon className="w-4 h-4 mr-2" />
                          Delete
                        </DropdownMenuItem>
                      </DropdownMenuContent>
                    </DropdownMenu>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

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

export function FilesPage() {
  const { t } = useTranslation("navigation");
  const [searchParams] = useSearchParams();
  const searchQuery = searchParams.get("q")?.toLowerCase() ?? "";
  const { data: tablos, isLoading: tablosLoading } = useTablosList();
  const { data: allFiles, isLoading: filesLoading } = useAllTablosFileNames();
  const [uploadOpen, setUploadOpen] = useState(false);

  const isLoading = tablosLoading || filesLoading;

  const filesByTabloId = new Map<string, string[]>(
    (allFiles?.tablos ?? []).map(({ tabloId, fileNames }) => [tabloId, fileNames])
  );

  const tablosWithFiles = (tablos ?? []).filter((tablo) => {
    const files = filesByTabloId.get(tablo.id) ?? [];
    const visibleFiles = files.filter((f) => !f.startsWith("."));
    if (searchQuery) {
      return visibleFiles.some(
        (f) =>
          f.toLowerCase().includes(searchQuery) || tablo.name.toLowerCase().includes(searchQuery)
      );
    }
    return visibleFiles.length > 0;
  });

  return (
    <div className="py-6 px-6">
      {/* Header */}
      <div className="flex items-center justify-between pb-6">
        <h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
          {t("files", "Files")}
        </h1>
        <Button
          className="gap-2 bg-[#804EEC] hover:bg-[#6f3fd4] text-white"
          onClick={() => setUploadOpen(true)}
          disabled={!tablos || tablos.length === 0}
        >
          <PlusIcon className="w-5 h-5" />
          Upload
        </Button>
      </div>

      {isLoading ? (
        <div className="flex items-center justify-center py-24">
          <LoadingSpinner />
        </div>
      ) : tablosWithFiles.length === 0 ? (
        <div className="flex flex-col items-center justify-center py-24 text-center">
          <LayersIcon className="w-12 h-12 text-gray-300 dark:text-gray-600 mb-4" />
          <p className="text-gray-500 dark:text-gray-400">No files found.</p>
        </div>
      ) : (
        <div>
          {tablosWithFiles.map((tablo) => (
            <TabloFilesSection
              key={tablo.id}
              tabloId={tablo.id}
              tabloName={tablo.name}
              tabloColor={tablo.color}
              fileNames={filesByTabloId.get(tablo.id) ?? []}
            />
          ))}
        </div>
      )}

      {tablos && tablos.length > 0 && (
        <UploadModal isOpen={uploadOpen} onClose={() => setUploadOpen(false)} tablos={tablos} />
      )}
    </div>
  );
}
