import { ArrowDown, ArrowUp, CornersOut, Cursor, DownloadSimple, DropHalf, Eraser, Eye, EyeSlash, FolderOpen, FolderPlus, Hand, Lock, LockOpen, MagicWand, Minus, PaintBrush, Plus, Sparkle, Stack, Trash } from "@phosphor-icons/react";
import type { ReactNode } from "react";
import { commandIds } from "@commands/ids";
import type { Artboard } from "@core/artboard";
import type { ImageDocument } from "@core/document";
import type { GenerationCompareMode, GenerationState, SelectionState, ViewportState } from "@editor/state";
import type { AppStore } from "@editor/store";
import { availableToolIds, type GenerateMode, type ToolId, type ToolState } from "@editor/tools";
import type { DocumentReadIndex, IndexedLayerInfo } from "@editor/document-indexes";
import { downloadArtboardPng } from "@operations/export/downloadArtboard";
import { addArtboard, addEmptyLayer, addGroupLayer, deleteSelection, groupLayers, moveLayer } from "@operations/document/layerActions";
import { labelForTool } from "./toolLabels";

export type PaletteItem = { id: string; title: string; section: string; subtitle?: string; keywords?: string[]; disabled?: boolean; icon: ReactNode; run: () => void };

export function createPaletteItems(options: {
  document: ImageDocument;
  documentIndex: DocumentReadIndex;
  activeArtboard?: Artboard;
  activeArtboardId?: string;
  selectedLayer?: IndexedLayerInfo;
  canGroup: boolean;
  canUngroup: boolean;
  selection: SelectionState;
  viewport: ViewportState;
  tools: ToolState;
  generation: GenerationState;
  layersOpen: boolean;
  dispatch: AppStore["dispatch"];
  openFilePicker: () => void;
  openGenerate: () => void;
  openLayers: () => void;
  closeLayers: () => void;
}): PaletteItem[] {
  const {
    document,
    documentIndex,
    activeArtboard,
    activeArtboardId,
    selectedLayer,
    canGroup,
    canUngroup,
    selection,
    viewport,
    tools,
    generation,
    layersOpen,
    dispatch,
    openFilePicker,
    openGenerate,
    openLayers,
    closeLayers,
  } = options;
  const hasCandidates = generation.candidates.length > 0;
  const items: PaletteItem[] = [];

  items.push(
    ...availableToolIds.map((tool) => ({
      id: `tool-${tool}`,
      section: "Tools",
      title: `Switch to ${labelForTool(tool)}`,
      subtitle: tool === tools.activeTool ? "Current tool" : undefined,
      keywords: [tool],
      icon: toolIcon(tool),
      run: () => dispatch(commandIds.toolSetActive, { tool }),
    })),
  );

  items.push(
    {
      id: "import-image",
      section: "File",
      title: "Import image",
      subtitle: "Add an image as a layer",
      keywords: ["open", "file", "layer"],
      icon: <FolderOpen size={20} />,
      run: openFilePicker,
    },
    {
      id: "export-artboard",
      section: "File",
      title: "Export artboard as PNG",
      subtitle: activeArtboard ? activeArtboard.name : "No artboard selected",
      keywords: ["download", "png"],
      disabled: !activeArtboard,
      icon: <DownloadSimple size={20} />,
      run: () => {
        if (activeArtboard) void downloadArtboardPng(activeArtboard, document.assets);
      },
    },
  );

  items.push(
    {
      id: layersOpen ? "close-layers" : "open-layers",
      section: "Layers",
      title: layersOpen ? "Close layers panel" : "Open layers panel",
      keywords: ["panel", "stack"],
      icon: <Stack size={20} weight={layersOpen ? "fill" : "regular"} />,
      run: layersOpen ? closeLayers : openLayers,
    },
    {
      id: "add-artboard",
      section: "Layers",
      title: "Add artboard",
      icon: <Plus size={20} />,
      run: () => addArtboard(document, dispatch),
    },
    {
      id: "add-empty-layer",
      section: "Layers",
      title: "Add empty layer",
      subtitle: activeArtboardId ? undefined : "No artboard available",
      keywords: ["new", "raster"],
      disabled: !activeArtboardId,
      icon: <Plus size={20} />,
      run: () => {
        if (activeArtboardId) addEmptyLayer(document, activeArtboardId, selectedLayer, dispatch);
      },
    },
    {
      id: "add-group",
      section: "Layers",
      title: "Add group",
      subtitle: activeArtboardId ? undefined : "No artboard available",
      disabled: !activeArtboardId,
      icon: <FolderPlus size={20} />,
      run: () => {
        if (activeArtboardId) addGroupLayer(activeArtboardId, dispatch);
      },
    },
    {
      id: "group-selection",
      section: "Layers",
      title: "Group selected layers",
      subtitle: canGroup ? `${selection.layerIds.length} selected` : "Select one or more layers",
      disabled: !canGroup,
      icon: <Stack size={20} />,
      run: () => {
        if (selection.artboardId) groupLayers(selection.artboardId, selection.layerIds, dispatch);
      },
    },
    {
      id: "ungroup-selection",
      section: "Layers",
      title: "Ungroup selected group",
      subtitle: selectedLayer?.layer.name,
      disabled: !canUngroup || !selectedLayer,
      icon: <Stack size={20} weight="fill" />,
      run: () => {
        if (selectedLayer?.layer.type === "group") dispatch(commandIds.documentUngroupLayer, { groupId: selectedLayer.layer.id });
      },
    },
    {
      id: "move-layer-up",
      section: "Layers",
      title: "Move selected layer up",
      subtitle: selectedLayer?.layer.name,
      disabled: !selectedLayer,
      icon: <ArrowUp size={20} />,
      run: () => {
        if (selectedLayer) moveLayer(documentIndex, selectedLayer, -1, dispatch);
      },
    },
    {
      id: "move-layer-down",
      section: "Layers",
      title: "Move selected layer down",
      subtitle: selectedLayer?.layer.name,
      disabled: !selectedLayer,
      icon: <ArrowDown size={20} />,
      run: () => {
        if (selectedLayer) moveLayer(documentIndex, selectedLayer, 1, dispatch);
      },
    },
    {
      id: "toggle-layer-visible",
      section: "Layers",
      title: selectedLayer?.layer.visible === false ? "Show selected layer" : "Hide selected layer",
      subtitle: selectedLayer?.layer.name,
      disabled: !selectedLayer,
      icon: selectedLayer?.layer.visible === false ? <Eye size={20} /> : <EyeSlash size={20} />,
      run: () => {
        if (selectedLayer) dispatch(commandIds.documentSetLayerVisible, { layerId: selectedLayer.layer.id, visible: !selectedLayer.layer.visible });
      },
    },
    {
      id: "toggle-layer-lock",
      section: "Layers",
      title: selectedLayer?.layer.locked ? "Unlock selected layer" : "Lock selected layer",
      subtitle: selectedLayer?.layer.name,
      disabled: !selectedLayer,
      icon: selectedLayer?.layer.locked ? <LockOpen size={20} /> : <Lock size={20} />,
      run: () => {
        if (selectedLayer) dispatch(commandIds.documentSetLayerLocked, { layerId: selectedLayer.layer.id, locked: !selectedLayer.layer.locked });
      },
    },
    {
      id: "delete-selection",
      section: "Layers",
      title: selectedLayer ? "Delete selected layer" : "Delete selected artboard",
      subtitle: selectedLayer?.layer.name ?? activeArtboard?.name,
      disabled: !selectedLayer && !selection.artboardId,
      icon: <Trash size={20} />,
      run: () => deleteSelection(selection, selectedLayer, dispatch),
    },
  );

  items.push(
    {
      id: "open-generate",
      section: "Generate",
      title: "Open generate panel",
      keywords: ["ai"],
      icon: <Sparkle size={20} />,
      run: openGenerate,
    },
    {
      id: "open-chroma-key",
      section: "Masks",
      title: "Open chroma key operation",
      keywords: ["key", "mask", "remove background"],
      icon: <DropHalf size={20} />,
      run: () => dispatch(commandIds.workspaceSetPanel, { panel: "chromaKey" }),
    },
    ...generateModeItems.map((modeItem) => ({
      id: `generate-mode-${modeItem.mode}`,
      section: "Generate",
      title: modeItem.title,
      subtitle: tools.generate.mode === modeItem.mode ? "Current mode" : undefined,
      keywords: ["mode", modeItem.mode],
      icon: <Sparkle size={20} />,
      run: () => {
        openGenerate();
        dispatch(commandIds.toolSetGenerateSettings, { mode: modeItem.mode });
      },
    })),
    {
      id: "generate-random-seed",
      section: "Generate",
      title: "Use random seed",
      subtitle: tools.generate.seed === -1 ? "Current seed" : `Seed ${tools.generate.seed}`,
      keywords: ["seed"],
      icon: <Sparkle size={20} />,
      run: () => dispatch(commandIds.toolSetGenerateSettings, { seed: -1 }),
    },
    {
      id: "clear-generation-candidates",
      section: "Generate",
      title: "Clear candidates",
      subtitle: hasCandidates ? `${generation.candidates.length} candidate${generation.candidates.length === 1 ? "" : "s"}` : "No candidates",
      disabled: !hasCandidates,
      icon: <Trash size={20} />,
      run: () => dispatch(commandIds.generationClearCandidates, undefined),
    },
    ...generationCompareItems.map((compareItem) => ({
      id: `generation-compare-${compareItem.mode}`,
      section: "Generate",
      title: compareItem.title,
      subtitle: generation.compareMode === compareItem.mode ? "Current compare mode" : undefined,
      disabled: !hasCandidates,
      icon: <Sparkle size={20} />,
      run: () => dispatch(commandIds.generationSetCompareMode, { mode: compareItem.mode }),
    })),
  );

  items.push(
    {
      id: "zoom-in",
      section: "Zoom",
      title: "Zoom in",
      subtitle: `${Math.round(viewport.zoom * 100)}%`,
      icon: <Plus size={20} />,
      run: () => dispatch(commandIds.viewportSetZoom, { zoom: viewport.zoom * 1.2 }),
    },
    {
      id: "zoom-out",
      section: "Zoom",
      title: "Zoom out",
      subtitle: `${Math.round(viewport.zoom * 100)}%`,
      icon: <Minus size={20} />,
      run: () => dispatch(commandIds.viewportSetZoom, { zoom: viewport.zoom / 1.2 }),
    },
    {
      id: "zoom-100",
      section: "Zoom",
      title: "Zoom to 100%",
      icon: <CornersOut size={20} />,
      run: () => dispatch(commandIds.viewportSetZoom, { zoom: 1 }),
    },
    {
      id: "fit-artboard",
      section: "Zoom",
      title: "Fit artboard",
      subtitle: activeArtboard?.name,
      disabled: !activeArtboard,
      icon: <CornersOut size={20} />,
      run: () => dispatch(commandIds.viewportFitArtboard, undefined),
    },
  );

  items.push(
    {
      id: "debug-reset-viewport",
      section: "Debug",
      title: "Reset viewport",
      icon: <CornersOut size={20} />,
      run: () => dispatch(commandIds.viewportReset, undefined),
    },
    {
      id: "debug-clear-selection",
      section: "Debug",
      title: "Clear selection",
      subtitle: selection.layerIds.length > 0 || selection.artboardId ? undefined : "Nothing selected",
      disabled: selection.layerIds.length === 0 && !selection.artboardId,
      icon: <Cursor size={20} />,
      run: () => dispatch(commandIds.selectionClear, undefined),
    },
    {
      id: "debug-clear-candidates",
      section: "Debug",
      title: "Clear generation state",
      disabled: !hasCandidates,
      icon: <Trash size={20} />,
      run: () => dispatch(commandIds.generationClearCandidates, undefined),
    },
  );

  return items;
}

const generateModeItems: Array<{ mode: GenerateMode; title: string }> = [
  { mode: "text-to-image", title: "Text-to-image mode" },
  { mode: "image-to-image", title: "Image-to-image mode" },
  { mode: "inpaint", title: "Inpaint mode" },
  { mode: "outpaint", title: "Outpaint mode" },
];

const generationCompareItems: Array<{ mode: GenerationCompareMode; title: string }> = [
  { mode: "result", title: "Show result" },
  { mode: "before", title: "Show before" },
  { mode: "split", title: "Split compare" },
];

function toolIcon(tool: ToolId) {
  switch (tool) {
    case "select":
      return <Cursor size={20} />;
    case "brush":
      return <PaintBrush size={20} />;
    case "eraser":
      return <Eraser size={20} />;
    case "magicWand":
      return <MagicWand size={20} />;
    case "pan":
      return <Hand size={20} />;
  }
}
