"use client";

import { ChevronDown, ChevronUp, Download, Eye, EyeOff, Frame, Image as ImageIcon, Layers, Lock, Type, Unlock, X } from "lucide-react";
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
import type { AgentMessage, CanvasNode, CanvasViewport } from "@/domain/design";
import { useI18n } from "@/i18n/i18n";
import { thumbnailImageUrl } from "@/ui/lib/imageDelivery";
import { ShapeNodePreview } from "@/ui/pages/CanvasWorkspace/canvas/ShapeNodePreview";
import { isBooleanShapeGroupNode, isBooleanShapeOperandNode, isShapeNode } from "@/ui/pages/CanvasWorkspace/canvas/shapeNode";

type LayerPanelItem =
  | {
      type: "node";
      node: CanvasNode;
    }
  | {
      type: "booleanGroup";
      node: CanvasNode;
      layers: CanvasNode[];
    }
  | {
      type: "group";
      id: string;
      nodes: CanvasNode[];
      layers: CanvasNode[];
    };

type Translate = (key: string, values?: Record<string, string | number>) => string;

export function CanvasGrid({ viewport }: { viewport: CanvasViewport }) {
  const gridSize = 48 * viewport.k;
  return (
    <div
      className="canvas-grid"
      style={{
        backgroundSize: `${gridSize}px ${gridSize}px`,
        backgroundPosition: `${viewport.x % gridSize}px ${viewport.y % gridSize}px`
      }}
    />
  );
}

export function CanvasGeneratedFilesPanel({
  nodes,
  messages,
  selectedIds,
  onSelect,
  onDownload,
  onClose
}: {
  nodes: CanvasNode[];
  messages: AgentMessage[];
  selectedIds: Set<string>;
  onSelect: (nodeId: string) => void;
  onDownload: (node: CanvasNode) => void;
  onClose: () => void;
}) {
  const { t } = useI18n();
  const generatedFileURLs = useMemo(() => generatedFileURLSet(messages), [messages]);
  const files = useMemo(() => nodes.filter((node) => isGeneratedFileNode(node, generatedFileURLs)).reverse(), [generatedFileURLs, nodes]);

  return (
    <aside className="canvas-generated-files-panel" onPointerDown={(event) => event.stopPropagation()}>
      <header>
        <strong>{t("generatedFiles")}</strong>
        <button type="button" aria-label={t("close")} onClick={onClose}>
          <X size={19} />
        </button>
      </header>
      <div className="generated-files-list">
        {files.length === 0 ? (
          <div className="generated-files-empty">
            <ImageIcon size={54} />
            <span>{t("generatedFilesEmpty")}</span>
          </div>
        ) : (
          <>
            {files.map((node) => (
              <div
                key={node.id}
                className={`generated-file-row ${selectedIds.has(node.id) ? "selected" : ""}`}
                onClick={() => onSelect(node.id)}
                role="button"
                tabIndex={0}
                onKeyDown={(event) => {
                  if (event.key !== "Enter" && event.key !== " ") return;
                  event.preventDefault();
                  onSelect(node.id);
                }}
              >
                <span className="generated-file-thumb">
                  <img src={thumbnailImageUrl(node.content)} alt={node.title} draggable={false} />
                </span>
                <span className="generated-file-name">{layerTitle(node, t)}</span>
                <button
                  type="button"
                  className="generated-file-download"
                  aria-label={t("download")}
                  onClick={(event) => {
                    event.stopPropagation();
                    onDownload(node);
                  }}
                >
                  <Download size={19} />
                </button>
              </div>
            ))}
            <div className="generated-files-end">{t("generatedFilesEnd")}</div>
          </>
        )}
      </div>
    </aside>
  );
}

export function CanvasLayerPanel({
  nodes,
  selectedIds,
  hiddenNodeIds,
  lockedNodeIds,
  onSelect,
  onSelectMany,
  onRenameNode,
  onToggleHidden,
  onToggleLocked,
  onToggleHiddenMany,
  onToggleLockedMany,
  onClose
}: {
  nodes: CanvasNode[];
  selectedIds: Set<string>;
  hiddenNodeIds: Set<string>;
  lockedNodeIds: Set<string>;
  onSelect: (nodeId: string) => void;
  onSelectMany: (nodeIds: string[]) => void;
  onRenameNode: (nodeId: string, title: string) => void;
  onToggleHidden: (nodeId: string) => void;
  onToggleLocked: (nodeId: string) => void;
  onToggleHiddenMany: (nodeIds: string[]) => void;
  onToggleLockedMany: (nodeIds: string[]) => void;
  onClose: () => void;
}) {
  const { t } = useI18n();
  const layers = useMemo(() => buildLayerPanelItems(nodes), [nodes]);

  return (
    <aside className="canvas-layer-panel" onPointerDown={(event) => event.stopPropagation()}>
      <header>
        <strong>{t("layers")}</strong>
        <button type="button" aria-label={t("close")} onClick={onClose}>
          <X size={17} />
        </button>
      </header>
      <section className="layer-history-section">
        <button type="button">
          <span>{t("layerHistory")}</span>
          <ChevronUp size={14} />
        </button>
        <div className="layer-history-empty">
          <ImageIcon size={58} />
          <span>{t("layerNoHistory")}</span>
        </div>
      </section>
      <div className="layer-list">
        {layers.length === 0 ? (
          <div className="layer-list-empty">{t("layerNoLayers")}</div>
        ) : (
          layers.map((item) => {
            if (item.type === "booleanGroup") {
              const selected = selectedIds.has(item.node.id);
              const hidden = hiddenNodeIds.has(item.node.id);
              const locked = lockedNodeIds.has(item.node.id);
              return (
                <Fragment key={item.node.id}>
                  <div className={`layer-row layer-group-row boolean-layer-group-row ${selected ? "selected" : ""} ${hidden ? "is-hidden" : ""}`} onClick={() => onSelect(item.node.id)}>
                    <ChevronDown size={14} />
                    <span className="layer-thumb group-thumb boolean-group-thumb">
                      {layerThumb(item.node)}
                    </span>
                    <button
                      type="button"
                      className="layer-name"
                      onClick={(event) => {
                        event.stopPropagation();
                        onSelect(item.node.id);
                      }}
                    >
                      <strong>{layerTitle(item.node, t)}</strong>
                      <small>{t("booleanOperation")}</small>
                    </button>
                    <button
                      type="button"
                      className={`layer-control ${locked ? "active" : ""}`}
                      aria-label={locked ? t("contextUnlock") : t("contextLock")}
                      onClick={(event) => {
                        event.stopPropagation();
                        onToggleLocked(item.node.id);
                      }}
                    >
                      {locked ? <Lock size={15} /> : <Unlock size={15} />}
                    </button>
                    <button
                      type="button"
                      className={`layer-control ${hidden ? "active" : ""}`}
                      aria-label={hidden ? t("contextShow") : t("contextHide")}
                      onClick={(event) => {
                        event.stopPropagation();
                        onToggleHidden(item.node.id);
                      }}
                    >
                      {hidden ? <EyeOff size={15} /> : <Eye size={15} />}
                    </button>
                  </div>
                  {item.layers.map((node) => (
                    <LayerNodeRow
                      key={node.id}
                      node={node}
                      nested
                      selected={selected}
                      hidden={hiddenNodeIds.has(node.id)}
                      locked={lockedNodeIds.has(node.id)}
                      controlsVisible={false}
                      onSelect={() => onSelect(item.node.id)}
                      onRenameNode={onRenameNode}
                      onToggleHidden={onToggleHidden}
                      onToggleLocked={onToggleLocked}
                    />
                  ))}
                </Fragment>
              );
            }

            if (item.type === "group") {
              const nodeIds = item.nodes.map((node) => node.id);
              const selected = nodeIds.some((nodeId) => selectedIds.has(nodeId));
              const hidden = nodeIds.every((nodeId) => hiddenNodeIds.has(nodeId));
              const locked = nodeIds.every((nodeId) => lockedNodeIds.has(nodeId));
              return (
                <Fragment key={item.id}>
                  <div className={`layer-row layer-group-row ${selected ? "selected" : ""} ${hidden ? "is-hidden" : ""}`} onClick={() => onSelectMany(nodeIds)}>
                    <span className="layer-thumb group-thumb">
                      <Layers size={17} />
                    </span>
                    <button
                      type="button"
                      className="layer-name"
                      onClick={(event) => {
                        event.stopPropagation();
                        onSelectMany(nodeIds);
                      }}
                    >
                      <strong>{t("layerGroup")}</strong>
                      <small>{t("layerItemsCount", { count: item.nodes.length })}</small>
                    </button>
                    <button
                      type="button"
                      className={`layer-control ${locked ? "active" : ""}`}
                      aria-label={locked ? t("contextUnlock") : t("contextLock")}
                      onClick={(event) => {
                        event.stopPropagation();
                        onToggleLockedMany(nodeIds);
                      }}
                    >
                      {locked ? <Lock size={15} /> : <Unlock size={15} />}
                    </button>
                    <button
                      type="button"
                      className={`layer-control ${hidden ? "active" : ""}`}
                      aria-label={hidden ? t("contextShow") : t("contextHide")}
                      onClick={(event) => {
                        event.stopPropagation();
                        onToggleHiddenMany(nodeIds);
                      }}
                    >
                      {hidden ? <EyeOff size={15} /> : <Eye size={15} />}
                    </button>
                  </div>
                  {item.layers.map((node) => (
                    <LayerNodeRow
                      key={node.id}
                      node={node}
                      nested
                      selected={selectedIds.has(node.id)}
                      hidden={hiddenNodeIds.has(node.id)}
                      locked={lockedNodeIds.has(node.id)}
                      onSelect={onSelect}
                      onRenameNode={onRenameNode}
                      onToggleHidden={onToggleHidden}
                      onToggleLocked={onToggleLocked}
                    />
                  ))}
                </Fragment>
              );
            }

            const node = item.node;
            return (
              <LayerNodeRow
                key={node.id}
                node={node}
                selected={selectedIds.has(node.id)}
                hidden={hiddenNodeIds.has(node.id)}
                locked={lockedNodeIds.has(node.id)}
                onSelect={onSelect}
                onRenameNode={onRenameNode}
                onToggleHidden={onToggleHidden}
                onToggleLocked={onToggleLocked}
              />
            );
          })
        )}
      </div>
    </aside>
  );
}

function LayerNodeRow({
  node,
  selected,
  hidden,
  locked,
  nested = false,
  onSelect,
  onRenameNode,
  onToggleHidden,
  onToggleLocked,
  controlsVisible = true
}: {
  node: CanvasNode;
  selected: boolean;
  hidden: boolean;
  locked: boolean;
  nested?: boolean;
  onSelect: (nodeId: string) => void;
  onRenameNode: (nodeId: string, title: string) => void;
  onToggleHidden: (nodeId: string) => void;
  onToggleLocked: (nodeId: string) => void;
  controlsVisible?: boolean;
}) {
  const { t } = useI18n();
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [editingName, setEditingName] = useState(false);
  const [draftName, setDraftName] = useState(node.title);
  const title = layerTitle(node, t);
  const canRename = node.type === "image" && !locked;

  useEffect(() => {
    if (!editingName) setDraftName(node.title);
  }, [editingName, node.title]);

  useEffect(() => {
    if (!editingName) return;
    const input = inputRef.current;
    if (!input) return;
    input.focus();
    input.select();
  }, [editingName]);

  const startEditingName = () => {
    if (!canRename) return;
    setDraftName(node.title.trim() || title);
    setEditingName(true);
  };

  const cancelEditingName = () => {
    setDraftName(node.title);
    setEditingName(false);
  };

  const commitEditingName = () => {
    const nextName = draftName.trim();
    if (!nextName || nextName === node.title.trim()) {
      cancelEditingName();
      return;
    }
    onRenameNode(node.id, nextName);
    setEditingName(false);
  };

  return (
    <div className={`layer-row ${nested ? "nested" : ""} ${selected ? "selected" : ""} ${hidden ? "is-hidden" : ""}`} onClick={() => onSelect(node.id)}>
      <span className="layer-thumb">{layerThumb(node)}</span>
      {editingName ? (
        <span className="layer-name layer-name-editing" onPointerDown={(event) => event.stopPropagation()} onClick={(event) => event.stopPropagation()}>
          <input
            ref={inputRef}
            value={draftName}
            placeholder={t("imageTitlePlaceholder")}
            aria-label={t("renameImageNode")}
            onChange={(event) => setDraftName(event.target.value)}
            onBlur={commitEditingName}
            onKeyDown={(event) => {
              if (event.key === "Enter") {
                event.preventDefault();
                commitEditingName();
              }
              if (event.key === "Escape") {
                event.preventDefault();
                cancelEditingName();
              }
            }}
          />
        </span>
      ) : (
        <button
          type="button"
          className="layer-name"
          aria-label={canRename ? t("renameImageNode") : undefined}
          onClick={(event) => {
            event.stopPropagation();
            onSelect(node.id);
          }}
          onDoubleClick={(event) => {
            event.stopPropagation();
            startEditingName();
          }}
        >
          <strong>{title}</strong>
          <small>{layerKindLabel(node, t)}</small>
        </button>
      )}
      {controlsVisible ? (
        <>
          <button
            type="button"
            className={`layer-control ${locked ? "active" : ""}`}
            aria-label={locked ? t("contextUnlock") : t("contextLock")}
            onClick={(event) => {
              event.stopPropagation();
              onToggleLocked(node.id);
            }}
          >
            {locked ? <Lock size={15} /> : <Unlock size={15} />}
          </button>
          <button
            type="button"
            className={`layer-control ${hidden ? "active" : ""}`}
            aria-label={hidden ? t("contextShow") : t("contextHide")}
            onClick={(event) => {
              event.stopPropagation();
              onToggleHidden(node.id);
            }}
          >
            {hidden ? <EyeOff size={15} /> : <Eye size={15} />}
          </button>
        </>
      ) : (
        <span className="layer-controls-placeholder" />
      )}
    </div>
  );
}

function buildLayerPanelItems(nodes: CanvasNode[]): LayerPanelItem[] {
  const groups = new Map<string, CanvasNode[]>();
  nodes.forEach((node) => {
    if (!node.parentId) return;
    groups.set(node.parentId, [...(groups.get(node.parentId) ?? []), node]);
  });

  const emittedGroups = new Set<string>();
  const items: LayerPanelItem[] = [];
  [...nodes].reverse().forEach((node) => {
    if (isBooleanShapeOperandNode(node, nodes)) return;
    if (isBooleanShapeGroupNode(node)) {
      items.push({
        type: "booleanGroup",
        node,
        layers: [...(groups.get(node.id) ?? [])].reverse()
      });
      return;
    }
    if (!node.parentId) {
      items.push({ type: "node", node });
      return;
    }
    if (emittedGroups.has(node.parentId)) return;
    const groupNodes = groups.get(node.parentId) ?? [node];
    emittedGroups.add(node.parentId);
    items.push({
      type: "group",
      id: node.parentId,
      nodes: groupNodes,
      layers: [...groupNodes].reverse()
    });
  });
  return items;
}

function layerTitle(node: CanvasNode, t: Translate) {
  const title = node.title.trim();
  if (title) return title;
  if (node.type === "text") return node.content.trim().slice(0, 24) || t("textNode");
  if (node.type === "frame" && node.layerRole === "shape") return t("shapeTool");
  if (node.type === "frame") return t("frameNode");
  if (node.type === "image") return t("imageNode");
  return t("canvasUntitled");
}

function layerKindLabel(node: CanvasNode, t: Translate) {
  if (node.type === "text") return t("textNode");
  if (node.type === "frame" && node.layerRole === "shape") return t("shapeTool");
  if (node.type === "frame") return t("frameNode");
  if (node.type === "image") return t("imageNode");
  return node.type;
}

function layerThumb(node: CanvasNode) {
  if (isBooleanShapeGroupNode(node) || isShapeNode(node)) {
    return <ShapeNodePreview node={node} />;
  }
  if (node.type === "image" && isImageUrl(node.content)) {
    return <img src={thumbnailImageUrl(node.content)} alt="" draggable={false} />;
  }
  if (node.type === "text") return <Type size={17} />;
  if (node.type === "frame") return <Frame size={17} />;
  return <ImageIcon size={17} />;
}

function isGeneratedFileNode(node: CanvasNode, generatedFileURLs: Set<string>) {
  return (
    node.type === "image" &&
    isImageUrl(node.content) &&
    node.status !== "generating" &&
    node.status !== "uploading" &&
    node.status !== "error" &&
    !nonModelGeneratedLayerRoles.has(node.layerRole ?? "") &&
    assetURLSetHas(generatedFileURLs, node.content)
  );
}

function isImageUrl(value: string) {
  return /^(https?:\/\/|data:image\/|blob:|\/)/.test(value.trim());
}

const nonModelGeneratedLayerRoles = new Set(["pen-stroke", "background-removed", "clean-image", "mockup-print", "merge-layer", "manual-frame", "image-generator"]);

function generatedFileURLSet(messages: AgentMessage[]) {
  const urls = new Set<string>();
  messages.forEach((message) => {
    if (message.name !== "generate_media" && message.title !== "generate_media" && message.type !== "tool_use") return;
    try {
      const events = JSON.parse(message.content) as Array<{
        event_data?: {
          artifact?: Array<{
            image_url?: string;
          }>;
        };
      }>;
      events.forEach((event) => {
        event.event_data?.artifact?.forEach((artifact) => {
          if (artifact.image_url) addAssetURLKeys(urls, artifact.image_url);
        });
      });
    } catch {
      // Tool messages that are not artifact payloads are ignored here.
    }
  });
  return urls;
}

function assetURLSetHas(items: Set<string>, value: string) {
  return assetURLKeys(value).some((key) => items.has(key));
}

function addAssetURLKeys(items: Set<string>, value: string) {
  assetURLKeys(value).forEach((key) => items.add(key));
}

function assetURLKeys(value: string) {
  const trimmed = value.trim();
  if (!trimmed || trimmed.startsWith("data:image/") || trimmed.startsWith("blob:")) return [];
  const keys = new Set<string>([trimmed]);
  const queryIndex = trimmed.indexOf("?");
  if (queryIndex > 0) keys.add(trimmed.slice(0, queryIndex));
  try {
    const url = new URL(trimmed, window.location.origin);
    keys.add(url.pathname);
    if (url.search) keys.add(`${url.pathname}${url.search}`);
  } catch {
    // Keep exact and queryless keys above.
  }
  return Array.from(keys);
}
