"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
import { BaseNode } from "./BaseNode";
import { useWorkflowStore } from "@/store/workflowStore";
import { ImageInputNodeData } from "@/types";
import { useAdaptiveImageSrc } from "@/hooks/useAdaptiveImageSrc";
import { downloadMedia } from "@/utils/downloadMedia";
import { useI18n } from "@/i18n";
import { calculateAspectFitSize } from "@/utils/nodeDimensions";
import { MediaPreviewModal } from "@/components/MediaPreviewModal";
import { AssetPickerModal, PopiAssetItem } from "@/components/AssetPickerModal";
import { defaultNodeDimensions } from "@/constants/nodeDimensions";

type ImageInputNodeType = Node<ImageInputNodeData, "imageInput">;
const IMAGE_INPUT_DIMENSIONS = defaultNodeDimensions.imageInput;

function getImageNodeTitle(id: string, data: ImageInputNodeData, imageLabel: string) {
  if (data.customTitle) return data.customTitle;
  if (data.label) return data.label;
  const match = id.match(/(\d+)$/);
  return match ? `${imageLabel} ${match[1]}` : imageLabel;
}

function ImageTypeIcon() {
  return (
    <span className="flex h-7 w-12 items-center justify-center rounded-full bg-sky-600 text-white shadow-lg shadow-sky-500/20">
      <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.9}>
        <rect x="4" y="5" width="16" height="14" rx="2" />
        <path strokeLinecap="round" strokeLinejoin="round" d="m4 16 4.5-4.5 4 4L15 13l5 5" />
        <circle cx="15.5" cy="9.5" r="1.5" />
      </svg>
    </span>
  );
}

function EmptyImageIcon() {
  return (
    <svg className="w-8 h-8 text-neutral-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5} aria-hidden="true">
      <rect x="3" y="5" width="18" height="14" rx="2" />
      <path strokeLinecap="round" strokeLinejoin="round" d="m3 16 5-5 4.5 4.5L15 13l6 6" />
      <circle cx="15.5" cy="9.5" r="1.5" />
    </svg>
  );
}

type ImageToolbarIconKind =
  | "download"
  | "focus"
  | "delete"
  | "upload"
  | "assets";

function ImageToolbarIcon({ kind }: { kind: ImageToolbarIconKind }) {
  const commonProps = {
    className: "h-6 w-6",
    fill: "none",
    viewBox: "0 0 24 24",
    stroke: "currentColor",
    strokeWidth: 1.8,
  };

  switch (kind) {
    case "download":
      return <svg {...commonProps}><path strokeLinecap="round" strokeLinejoin="round" d="M12 4v11m0 0 4-4m-4 4-4-4M4 17v2h16v-2" /></svg>;
    case "focus":
      return <svg {...commonProps}><path strokeLinecap="round" strokeLinejoin="round" d="M8 4H4v4M16 4h4v4M8 20H4v-4M20 16v4h-4" /></svg>;
    case "delete":
      return <svg {...commonProps}><path strokeLinecap="round" strokeLinejoin="round" d="M6 7h12M9 7V5h6v2m-8 0 1 13h8l1-13" /></svg>;
    case "upload":
      return <svg {...commonProps}><path strokeLinecap="round" strokeLinejoin="round" d="M12 4v11m0-11 4 4m-4-4-4 4M4 17v2h16v-2" /></svg>;
    case "assets":
      return <svg {...commonProps}><path strokeLinecap="round" strokeLinejoin="round" d="M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v11a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 4 17.5z" /><path strokeLinecap="round" strokeLinejoin="round" d="M8 8h8M8 12h8M8 16h5" /></svg>;
  }
}

export function ImageInputNode({ id, data, selected }: NodeProps<ImageInputNodeType>) {
  const { t } = useI18n();
  const nodeData = data;
  const adaptiveImage = useAdaptiveImageSrc(nodeData.image, id);
  const updateNodeData = useWorkflowStore((state) => state.updateNodeData);
  const selectSingleNode = useWorkflowStore((state) => state.selectSingleNode);
  const { setNodes } = useReactFlow();
  const fileInputRef = useRef<HTMLInputElement>(null);
  const prevImageRef = useRef<string | null>(null);
  const [isPreviewOpen, setIsPreviewOpen] = useState(false);
  const [isAssetPickerOpen, setIsAssetPickerOpen] = useState(false);

  useEffect(() => {
    if (!nodeData.image || !nodeData.dimensions || nodeData.image === prevImageRef.current) {
      prevImageRef.current = nodeData.image ?? null;
      return;
    }
    prevImageRef.current = nodeData.image;

    const aspectRatio = nodeData.dimensions.width / nodeData.dimensions.height;
    if (!Number.isFinite(aspectRatio) || aspectRatio <= 0) return;

    requestAnimationFrame(() => {
      setNodes((nodes) =>
        nodes.map((node) => {
          if (node.id !== id) return node;

          const currentWidth = typeof node.width === "number"
            ? node.width
            : typeof node.style?.width === "number"
              ? node.style.width
              : IMAGE_INPUT_DIMENSIONS.width;
          const currentHeight = typeof node.height === "number"
            ? node.height
            : typeof node.style?.height === "number"
              ? node.style.height
              : IMAGE_INPUT_DIMENSIONS.height;
          const newSize = calculateAspectFitSize(aspectRatio, currentWidth, currentHeight, true);

          return {
            ...node,
            width: newSize.width,
            height: newSize.height,
            style: { ...node.style, width: newSize.width, height: newSize.height },
          };
        })
      );
    });
  }, [id, nodeData.dimensions, nodeData.image, setNodes]);

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

      if (!file.type.match(/^image\/(png|jpeg|webp)$/)) {
        alert(t("upload.imageUnsupported"));
        return;
      }

      // if (file.size > 10 * 1024 * 1024) {
      //   alert(t("upload.imageTooLarge"));
      //   return;
      // }

      const reader = new FileReader();
      reader.onload = (event) => {
        const base64 = event.target?.result as string;
        const img = new Image();
        img.onload = () => {
          updateNodeData(id, {
            image: base64,
            imageRef: undefined,
            filename: file.name,
            dimensions: { width: img.width, height: img.height },
          });
        };
        img.src = base64;
      };
      reader.readAsDataURL(file);
    },
    [id, t, updateNodeData]
  );

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      e.stopPropagation();

      const file = e.dataTransfer.files?.[0];
      if (!file) return;

      const dt = new DataTransfer();
      dt.items.add(file);
      if (fileInputRef.current) {
        fileInputRef.current.files = dt.files;
        fileInputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
      }
    },
    []
  );

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);

  const handleOpenFileDialog = useCallback(() => {
    fileInputRef.current?.click();
  }, []);

  const handleSelect = useCallback(() => {
    selectSingleNode(id);
  }, [id, selectSingleNode]);

  const handleRemove = useCallback(() => {
    updateNodeData(id, {
      image: null,
      imageRef: undefined,
      filename: null,
      dimensions: null,
    });
  }, [id, updateNodeData]);

  const handleSelectAsset = useCallback((asset: PopiAssetItem) => {
    const imageUrl = asset.images?.find(Boolean);
    if (!imageUrl) return;

    const applyAsset = (dimensions: { width: number; height: number } | null) => {
      updateNodeData(id, {
        image: imageUrl,
        imageRef: undefined,
        filename: asset.title || `asset-${asset.id}`,
        dimensions,
      });
      setIsAssetPickerOpen(false);
    };

    if (asset.width && asset.height) {
      applyAsset({ width: asset.width, height: asset.height });
      return;
    }

    const img = new Image();
    img.onload = () => applyAsset({ width: img.width, height: img.height });
    img.onerror = () => applyAsset(null);
    img.src = imageUrl;
  }, [id, updateNodeData]);

  const title = getImageNodeTitle(id, nodeData, t("node.image"));
  const hasImage = Boolean(nodeData.image);

  return (
    <BaseNode
      id={id}
      selected={selected}
      minWidth={IMAGE_INPUT_DIMENSIONS.width}
      minHeight={IMAGE_INPUT_DIMENSIONS.height}
      contentClassName="flex-1 min-h-0"
      aspectFitMedia={nodeData.image}
      fullBleed
      className={selected ? "!border-blue-500 !ring-0" : ""}
    >
      <input
        ref={fileInputRef}
        type="file"
        accept="image/png,image/jpeg,image/webp"
        onChange={handleFileChange}
        className="hidden"
      />

      <div className="nodrag pointer-events-none absolute -top-10 left-0 z-[10001] flex items-center gap-2">
        <ImageTypeIcon />
        <span className="text-sm font-medium text-neutral-100 drop-shadow-sm">{title}</span>
      </div>

      {selected && !hasImage && (
        <div className="nodrag nopan absolute left-1/2 -top-[86px] z-[10002] flex -translate-x-1/2 items-center gap-2">
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              selectSingleNode(id);
              handleOpenFileDialog();
            }}
            aria-label={t("imageInput.localUpload")}
            className="flex h-10 items-center gap-2 whitespace-nowrap rounded-lg border border-neutral-600/70 bg-neutral-800 px-3 text-sm text-neutral-200 shadow-xl shadow-black/30 transition-colors hover:border-neutral-500 hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="upload" />
            <span>{t("imageInput.localUpload")}</span>
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              selectSingleNode(id);
              setIsAssetPickerOpen(true);
            }}
            aria-label={t("imageInput.fromAssets")}
            className="flex h-10 items-center gap-2 whitespace-nowrap rounded-lg border border-neutral-600/70 bg-neutral-800 px-3 text-sm text-neutral-200 shadow-xl shadow-black/30 transition-colors hover:border-neutral-500 hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="assets" />
            <span>{t("imageInput.fromAssets")}</span>
          </button>
        </div>
      )}

      {selected && hasImage && (
        <div className="nodrag nopan absolute left-1/2 -top-[132px] z-[10002] flex -translate-x-1/2 items-center gap-2 rounded-xl border border-neutral-600/70 bg-neutral-800 px-4 py-3 text-xl text-neutral-300 shadow-2xl shadow-black/40">
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              handleOpenFileDialog();
            }}
            aria-label={t("imageInput.replace")}
            title={t("imageInput.replace")}
            className="flex h-10 items-center justify-center rounded-md px-2 text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="upload" />
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              setIsAssetPickerOpen(true);
            }}
            aria-label={t("imageInput.fromAssets")}
            title={t("imageInput.fromAssets")}
            className="flex h-10 items-center justify-center rounded-md px-2 text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="assets" />
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              downloadMedia(nodeData.image!, "image");
            }}
            aria-label={t("imageInput.download")}
            title={t("imageInput.download")}
            className="flex h-10 items-center justify-center rounded-md px-2 text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="download" />
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              setIsPreviewOpen(true);
            }}
            aria-label={t("imageInput.focusPreview")}
            title={t("imageInput.focusPreview")}
            className="flex h-10 items-center justify-center rounded-md px-2 text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
          >
            <ImageToolbarIcon kind="focus" />
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              handleRemove();
            }}
            aria-label={t("imageInput.remove")}
            title={t("imageInput.remove")}
            className="flex h-10 items-center justify-center rounded-md px-2 text-neutral-100 transition-colors hover:bg-red-600/80"
          >
            <ImageToolbarIcon kind="delete" />
          </button>
        </div>
      )}

      {nodeData.image ? (
        <div
          role="button"
          tabIndex={0}
          aria-label={`${nodeData.filename || t("imageInput.uploadedAlt")} preview`}
          onClick={handleSelect}
          onDoubleClick={(e) => {
            e.preventDefault();
            e.stopPropagation();
            setIsPreviewOpen(true);
          }}
          onKeyDown={(e) => {
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              setIsPreviewOpen(true);
            }
          }}
          className="relative group h-full w-full cursor-zoom-in overflow-clip rounded-lg bg-neutral-800"
        >
          <img
            src={adaptiveImage ?? undefined}
            alt={nodeData.filename || t("imageInput.uploadedAlt")}
            className="h-full w-full rounded-lg object-contain"
          />
          {nodeData.isOptional && (
            <span className="absolute bottom-2 left-2 text-[9px] font-medium text-neutral-300 bg-black/50 px-1.5 py-0.5 rounded">
              {t("common.optional")}
            </span>
          )}
          {selected && (
            <>
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  handleOpenFileDialog();
                }}
                aria-label={t("imageInput.replace")}
                className="absolute right-3 top-3 flex h-12 items-center gap-2 rounded-xl bg-black/35 px-4 text-lg font-medium text-white shadow-lg backdrop-blur-sm transition-colors hover:bg-black/55"
              >
                <ImageToolbarIcon kind="upload" />
                <span>{t("imageInput.replace")}</span>
              </button>
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  setIsAssetPickerOpen(true);
                }}
                aria-label={t("imageInput.fromAssets")}
                className="absolute right-3 top-[4.25rem] flex h-12 items-center gap-2 rounded-xl bg-black/35 px-4 text-lg font-medium text-white shadow-lg backdrop-blur-sm transition-colors hover:bg-black/55"
              >
                <ImageToolbarIcon kind="assets" />
                <span>{t("imageInput.fromAssets")}</span>
              </button>
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  setIsPreviewOpen(true);
                }}
                aria-label={t("imageInput.focusPreview")}
                className="absolute bottom-3 right-3 flex h-11 w-11 items-center justify-center rounded-xl bg-black/40 text-white shadow-lg backdrop-blur-sm transition-colors hover:bg-black/60"
              >
                <ImageToolbarIcon kind="focus" />
              </button>
            </>
          )}
        </div>
      ) : (
        <div className="flex h-full w-full items-center justify-center bg-neutral-900/20 p-8">
          <div
            role="button"
            tabIndex={0}
            aria-label={t("imageInput.upload")}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              selectSingleNode(id);
              handleOpenFileDialog();
            }}
            onKeyDown={(e) => {
              if (e.key === "Enter" || e.key === " ") {
                e.preventDefault();
                selectSingleNode(id);
                handleOpenFileDialog();
              }
            }}
            onDrop={handleDrop}
            onDragOver={handleDragOver}
            className="flex h-full min-h-28 w-full cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-neutral-700 bg-neutral-900/40 text-center transition-colors hover:border-neutral-500 hover:bg-neutral-900/60"
          >
            <EmptyImageIcon />
            <span className="text-xs text-neutral-500 mt-2">拖入图片或点击</span>
            {nodeData.isOptional && <span className="mt-4 text-xs text-neutral-500">{t("common.optional")}</span>}
          </div>
        </div>
      )}

      {/* Handles rendered after visual content so they paint on top */}
      <Handle
        type="source"
        position={Position.Right}
        id="image"
        data-handletype="image"
        data-tutorial="node-output-handle"
        style={{ opacity: 0, zIndex: 10 }}
      />

      {isPreviewOpen && nodeData.image && (
        <MediaPreviewModal
          type="image"
          src={adaptiveImage ?? nodeData.image}
          downloadSrc={nodeData.image}
          alt={`${nodeData.filename || t("imageInput.uploadedAlt")} preview`}
          onClose={() => setIsPreviewOpen(false)}
        />
      )}
      {isAssetPickerOpen && (
        <AssetPickerModal
          kind="image"
          onClose={() => setIsAssetPickerOpen(false)}
          onSelect={handleSelectAsset}
        />
      )}
    </BaseNode>
  );
}
