"use client";

import { useCallback, useState, useMemo, useEffect, useRef } from "react";
import { Handle, Position, NodeProps, Node } from "@xyflow/react";
import { BaseNode } from "./BaseNode";
import { useWorkflowStore } from "@/store/workflowStore";
import { OutputNodeData } from "@/types";
import { useVideoBlobUrl } from "@/hooks/useVideoBlobUrl";
import { useVideoAutoplay } from "@/hooks/useVideoAutoplay";
import { useAdaptiveImageSrc } from "@/hooks/useAdaptiveImageSrc";
import { downloadMedia, MediaType } from "@/utils/downloadMedia";
import { useShowHandleLabels } from "@/hooks/useShowHandleLabels";
import { HandleLabel } from "./HandleLabel";
import { useI18n } from "@/i18n";
import { defaultNodeDimensions } from "@/constants/nodeDimensions";

type OutputNodeType = Node<OutputNodeData, "output">;
type OutputKind = "image" | "video" | "audio" | "3d";
const OUTPUT_DIMENSIONS = defaultNodeDimensions.output;

interface OutputItem {
  id: string;
  kind: OutputKind;
  src: string;
  label: string;
}

function isVideoSource(src: string): boolean {
  return src.startsWith("data:video/") || src.includes(".mp4") || src.includes(".webm");
}

function isAudioSource(src: string): boolean {
  return src.startsWith("data:audio/");
}

function is3DSource(src: string): boolean {
  return src.includes(".glb") || src.includes(".gltf");
}

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

function OutputTypeIcon() {
  return (
    <span className="flex h-7 w-12 items-center justify-center rounded-full bg-rose-600 text-white shadow-lg shadow-rose-500/20">
      <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.9}>
        <path strokeLinecap="round" strokeLinejoin="round" d="M5 5h14v14H5z" />
        <path strokeLinecap="round" strokeLinejoin="round" d="m8 15 3-3 2 2 3-4 3 5" />
      </svg>
    </span>
  );
}

function DeleteIcon() {
  return (
    <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M6 7h12M9 7V5h6v2m-8 0 1 13h8l1-13" />
    </svg>
  );
}

export function OutputNode({ id, data, selected }: NodeProps<OutputNodeType>) {
  const nodeData = data;
  const { t } = useI18n();
  const removeNode = useWorkflowStore((state) => state.removeNode);
  const regenerateNode = useWorkflowStore((state) => state.regenerateNode);
  const connectedEdgeCount = useWorkflowStore(
    (state) => state.edges.filter((edge) => edge.target === id).length
  );
  const isRunning = useWorkflowStore((state) => state.isRunning);
  const showLabels = useShowHandleLabels(selected);
  const [showLightbox, setShowLightbox] = useState(false);
  const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
  const previousEdgeCountRef = useRef<number | null>(null);
  const videoAutoplayRef = useVideoAutoplay(id, selected);

  const typeLabels = useMemo<Record<OutputKind, string>>(() => ({
    image: t("toolbar.image"),
    video: t("toolbar.video"),
    audio: t("modelSearch.audio"),
    "3d": "3D",
  }), [t]);

  const outputItems = useMemo<OutputItem[]>(() => {
    const items: OutputItem[] = [];
    const addItem = (kind: OutputKind, src: string | null | undefined) => {
      if (!src) return;
      if (items.some((item) => item.kind === kind && item.src === src)) return;
      items.push({
        id: `${kind}:${src}`,
        kind,
        src,
        label: typeLabels[kind],
      });
    };

    const imageKind: OutputKind | null = nodeData.image
      ? nodeData.contentType === "video" || isVideoSource(nodeData.image)
        ? "video"
        : nodeData.contentType === "audio" || isAudioSource(nodeData.image)
          ? "audio"
          : nodeData.contentType === "3d" || is3DSource(nodeData.image)
            ? "3d"
            : "image"
      : null;

    if (imageKind === "image") addItem("image", nodeData.image);
    addItem("video", nodeData.video ?? (imageKind === "video" ? nodeData.image : null));
    addItem("audio", nodeData.audio ?? (imageKind === "audio" ? nodeData.image : null));
    addItem("3d", nodeData.model3d ?? (imageKind === "3d" ? nodeData.image : null));

    return items;
  }, [nodeData.audio, nodeData.contentType, nodeData.image, nodeData.model3d, nodeData.video, typeLabels]);

  const preferredItemId = useMemo(() => {
    if (outputItems.length === 0) return null;

    const explicitItem = nodeData.contentType
      ? outputItems.find((item) => item.kind === nodeData.contentType)
      : null;
    if (explicitItem) return explicitItem.id;

    const legacyPriority: OutputKind[] = ["audio", "video", "3d", "image"];
    return legacyPriority
      .map((kind) => outputItems.find((item) => item.kind === kind))
      .find(Boolean)?.id ?? outputItems[0].id;
  }, [nodeData.contentType, outputItems]);

  const activeItem = useMemo(() => {
    if (outputItems.length === 0) return null;
    return outputItems.find((item) => item.id === selectedItemId)
      ?? outputItems.find((item) => item.id === preferredItemId)
      ?? outputItems[0];
  }, [outputItems, preferredItemId, selectedItemId]);

  const contentSrc = activeItem?.src ?? null;
  const isAudio = activeItem?.kind === "audio";
  const isVideo = activeItem?.kind === "video";
  const is3D = activeItem?.kind === "3d";

  const imageSrc = activeItem?.kind === "image" ? contentSrc : null;
  const adaptiveImage = useAdaptiveImageSrc(imageSrc, id);
  const videoBlobUrl = useVideoBlobUrl(isVideo ? contentSrc ?? null : null);

  // Auto-trigger execution when a new connection is made
  useEffect(() => {
    if (previousEdgeCountRef.current === null) {
      // First run — just record the baseline, don't trigger
      previousEdgeCountRef.current = connectedEdgeCount;
      return;
    }
    if (connectedEdgeCount > previousEdgeCountRef.current) {
      regenerateNode(id);
    }
    previousEdgeCountRef.current = connectedEdgeCount;
  }, [connectedEdgeCount, id, regenerateNode]);

  const handleDelete = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    removeNode(id);
  }, [id, removeNode]);

  const handleDownload = useCallback(async () => {
    if (!contentSrc || !activeItem) return;
    const type: MediaType = activeItem.kind;
    try {
      await downloadMedia(contentSrc, type, nodeData.outputFilename ?? undefined);
    } catch (err) {
      console.error("Download failed:", err);
    }
  }, [activeItem, contentSrc, nodeData.outputFilename]);

  const handleDownloadAll = useCallback(async () => {
    for (const item of outputItems) {
      const filename = nodeData.outputFilename
        ? `${nodeData.outputFilename}-${item.kind}`
        : undefined;
      try {
        await downloadMedia(item.src, item.kind, filename);
      } catch (err) {
        console.error("Download failed:", err);
      }
    }
  }, [nodeData.outputFilename, outputItems]);

  const downloadCurrentTitle = outputItems.length > 1 && activeItem
    ? t("output.downloadCurrent", { type: activeItem.label })
    : t("common.download");
  const downloadAllTitle = t("output.downloadAll");
  const title = getOutputNodeTitle(id, nodeData, t("node.output"));

  return (
    <>
      <BaseNode
        id={id}
        selected={selected}
        isExecuting={isRunning}
        minWidth={OUTPUT_DIMENSIONS.width}
        minHeight={OUTPUT_DIMENSIONS.height}
        className={selected ? "!border-blue-500 !ring-0" : ""}
      >
        <div className="nodrag pointer-events-none absolute -top-10 left-0 z-[10001] flex items-center gap-2">
          <OutputTypeIcon />
          <span className="text-sm font-medium text-neutral-100 drop-shadow-sm">{title}</span>
        </div>

        {selected && (
          <div className="nodrag nopan absolute left-1/2 -top-[86px] z-[10002] -translate-x-1/2">
            <button
              type="button"
              onClick={handleDelete}
              aria-label={t("prompt.deleteNode")}
              className="flex h-10 min-w-[120px] items-center justify-center gap-2 rounded-lg border border-neutral-600/70 bg-neutral-800 px-3 text-sm text-neutral-300 shadow-2xl shadow-black/40 transition-colors hover:border-neutral-500 hover:bg-neutral-700 hover:text-neutral-100"
            >
              <DeleteIcon />
              <span className="whitespace-nowrap">{t("prompt.deleteNode")}</span>
            </button>
          </div>
        )}

        <Handle
          type="target"
          position={Position.Left}
          id="image"
          data-handletype="image"
          style={{ top: "32%", zIndex: 10 }}
        />
        <HandleLabel label={typeLabels.image} side="target" color="var(--handle-color-image)" top="calc(32% - 18px)" visible={showLabels} />
        <Handle
          type="target"
          position={Position.Left}
          id="video"
          data-handletype="video"
          style={{ top: "50%", zIndex: 10 }}
        />
        <HandleLabel label={typeLabels.video} side="target" color="var(--handle-color-video)" top="calc(50% - 18px)" visible={showLabels} />
        <Handle
          type="target"
          position={Position.Left}
          id="audio"
          data-handletype="audio"
          style={{ top: "68%", background: "rgb(167, 139, 250)", zIndex: 10 }}
        />
        <HandleLabel label={typeLabels.audio} side="target" color="var(--handle-color-audio)" top="calc(68% - 18px)" visible={showLabels} />

        <div className="relative w-full h-full overflow-hidden rounded-lg">
          {outputItems.length > 1 && (
            <div className="absolute left-2 top-2 z-20 flex max-w-[calc(100%-5.5rem)] gap-1 overflow-x-auto rounded bg-black/50 p-1 backdrop-blur-sm">
              {outputItems.map((item) => {
                const isActive = activeItem?.id === item.id;
                return (
                  <button
                    key={item.id}
                    type="button"
                    data-testid={`output-tab-${item.kind}`}
                    onClick={() => setSelectedItemId(item.id)}
                    aria-pressed={isActive}
                    className={`shrink-0 rounded px-2 py-0.5 text-[10px] font-medium transition-colors ${
                      isActive
                        ? "bg-white text-neutral-900"
                        : "text-neutral-300 hover:bg-white/10 hover:text-white"
                    }`}
                    title={item.label}
                  >
                    {item.label} 1
                  </button>
                );
              })}
            </div>
          )}
          {contentSrc ? (
            <>
              {isAudio ? (
                <div className="w-full h-full flex items-center justify-center p-4">
                  <audio
                    src={contentSrc}
                    controls
                    className="w-full rounded"
                  />
                </div>
              ) : is3D ? (
                <div className="w-full h-full flex flex-col items-center justify-center gap-2 p-4 text-center bg-neutral-900/60">
                  <svg className="w-9 h-9 text-sky-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9L12 21.75m0-9L3 7.5m9 5.25v9M3 7.5v9l9 5.25" />
                  </svg>
                  <span className="text-xs font-medium text-neutral-200">3D model</span>
                  <span className="max-w-full truncate text-[10px] text-neutral-500">
                    {contentSrc}
                  </span>
                </div>
              ) : (
                <div
                  className="relative cursor-pointer group w-full h-full"
                  onClick={() => setShowLightbox(true)}
                >
                  {isVideo ? (
                    <video
                      ref={videoAutoplayRef}
                      src={videoBlobUrl ?? undefined}
                      controls
                      loop
                      muted
                      playsInline
                      className="w-full h-full object-contain"
                      onClick={(e) => e.stopPropagation()}
                    />
                  ) : (
                    <img
                      src={adaptiveImage ?? contentSrc}
                      alt="Output"
                      className="w-full h-full object-contain"
                    />
                  )}
                  <div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors flex items-center justify-center pointer-events-none">
                    <span className="text-[10px] font-medium text-white opacity-0 group-hover:opacity-100 transition-opacity bg-black/50 px-2 py-1 rounded">
                      View full size
                    </span>
                  </div>
                </div>
              )}
              {outputItems.length > 1 && (
                <button
                  onClick={handleDownloadAll}
                  className="absolute top-2 right-10 p-1.5 bg-black/60 hover:bg-black/80 text-white text-xs rounded transition-colors flex items-center gap-1"
                  title={downloadAllTitle}
                >
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M7 11l5 5 5-5M12 16V4M5 20h14" />
                  </svg>
                </button>
              )}
              <button
                onClick={handleDownload}
                className="absolute top-2 right-2 p-1.5 bg-black/60 hover:bg-black/80 text-white text-xs rounded transition-colors flex items-center gap-1"
                title={downloadCurrentTitle}
              >
                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
                </svg>
              </button>
            </>
          ) : (
            <div className="w-full h-full bg-neutral-900/40 flex flex-col items-center justify-center">
              <svg className="w-8 h-8 text-neutral-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
              </svg>
              <span className="text-xs text-neutral-500 mt-2">Connect input</span>
            </div>
          )}
        </div>
      </BaseNode>

      {/* Lightbox Modal (skip for audio and 3D files) */}
      {showLightbox && contentSrc && !isAudio && !is3D && (
        <div
          className="fixed inset-0 bg-black/90 z-[100] flex items-center justify-center p-8"
          onClick={() => setShowLightbox(false)}
        >
          <div className="relative max-w-full max-h-full">
            {isVideo ? (
              <video
                src={videoBlobUrl ?? undefined}
                controls
                loop
                autoPlay
                playsInline
                className="max-w-full max-h-[90vh] object-contain rounded"
                onClick={(e) => e.stopPropagation()}
              />
            ) : (
              <img
                src={contentSrc}
                alt="Output full size"
                className="max-w-full max-h-[90vh] object-contain rounded"
              />
            )}
            <button
              onClick={() => setShowLightbox(false)}
              className="absolute top-4 right-4 w-8 h-8 bg-white/10 hover:bg-white/20 rounded text-white text-sm transition-colors flex items-center justify-center"
            >
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </div>
        </div>
      )}
    </>
  );
}
