"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Node, NodeProps, useUpdateNodeInternals } from "@xyflow/react";
import { Spin } from "antd";
import { AssetPickerModal, PopiAssetItem } from "@/components/AssetPickerModal";
import { useI18n } from "@/i18n";
import { useWorkflowStore } from "@/store/workflowStore";
import { SmartVideoNodeData } from "@/types";
import { uploadFileToGatewayMedia } from "@/utils/gatewayMediaUpload";
import { isSmartGenerateUploadEnabled } from "@/utils/smartGenerateUpload";
import { deriveSmartVideoMode } from "@/utils/smartVideoMode";
import { getPopiVideoAssetPreview, resolvePopiVideoAsset } from "@/lib/popiAssetApi";
import { GenerateVideoNodeView } from "./GenerateVideoNode";
import { VideoInputNodeView } from "./VideoInputNode";

type SmartVideoNodeType = Node<SmartVideoNodeData, "smartVideo">;

const MAX_FILE_SIZE = 200 * 1024 * 1024;
const ACCEPTED_FORMATS = "video/mp4,video/webm,video/quicktime";
const ACCEPTED_MIME_TYPES = ACCEPTED_FORMATS.split(",");

function SmartVideoToolbarIcon({ kind }: { kind: "upload" | "assets" }) {
  if (kind === "upload") {
    return (
      <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8}>
        <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v11m0-11 4 4m-4-4-4 4M4 17v2h16v-2" />
      </svg>
    );
  }

  return (
    <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8}>
      <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>
  );
}

function SmartVideoUploadToolbar({
  id,
  selected,
  isUploading,
  addToGenerationHistory = false,
}: {
  id: string;
  selected?: boolean;
  isUploading: boolean;
  addToGenerationHistory?: boolean;
}) {
  const { t } = useI18n();
  const updateNodeData = useWorkflowStore((state) => state.updateNodeData);
  const selectSingleNode = useWorkflowStore((state) => state.selectSingleNode);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const [isAssetPickerOpen, setIsAssetPickerOpen] = useState(false);

  const buildUploadedVideoPatch = useCallback(
    (videoUrl: string): Partial<SmartVideoNodeData> => {
      if (!addToGenerationHistory) return {};

      const currentNode = useWorkflowStore.getState().getNodeById(id);
      const currentData = (currentNode?.data ?? {}) as SmartVideoNodeData;
      const timestamp = Date.now();
      return {
        outputVideo: videoUrl,
        videoHistory: [
          {
            id: `${timestamp}`,
            video: videoUrl,
            timestamp,
            prompt: currentData.inputPrompt || "",
            model: currentData.selectedModel?.modelId || "",
            modelDisplayName: currentData.selectedModel?.displayName,
            modelProvider: currentData.selectedModel?.provider,
          },
          ...(currentData.videoHistory || []),
        ].slice(0, 50),
        selectedVideoHistoryIndex: 0,
      };
    },
    [addToGenerationHistory, id]
  );

  const applyVideo = useCallback(
    (videoSrc: string, filename: string, format: string, duration: number | null, dimensions: { width: number; height: number } | null) => {
      updateNodeData(id, {
        video: videoSrc,
        videoRef: undefined,
        assetId: undefined,
        previewVideoPoster: undefined,
        assetDetailLoading: false,
        assetDetailError: null,
        filename,
        format,
        duration,
        dimensions,
        ...buildUploadedVideoPatch(videoSrc),
      });
    },
    [buildUploadedVideoPatch, id, updateNodeData]
  );

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

      if (!ACCEPTED_MIME_TYPES.includes(file.type)) {
        alert(t("upload.videoUnsupported"));
        return;
      }

      if (file.size > MAX_FILE_SIZE) {
        alert(t("upload.videoTooLarge"));
        return;
      }

      updateNodeData(id, { uploadStatus: "loading", uploadError: null });

      const metadataUrl = URL.createObjectURL(file);
      const video = document.createElement("video");
      video.preload = "metadata";

      const applyUpload = (metadata: {
        duration: number | null;
        dimensions: { width: number; height: number } | null;
      }) => {
        URL.revokeObjectURL(metadataUrl);
        uploadFileToGatewayMedia(file, "video")
          .then((uploaded) => {
            updateNodeData(id, { uploadStatus: "idle", uploadError: null });
            applyVideo(
              uploaded.url,
              uploaded.filename,
              uploaded.contentType,
              metadata.duration,
              metadata.dimensions
            );
          })
          .catch((error) => {
            const message = error instanceof Error ? error.message : "Media upload failed";
            updateNodeData(id, { uploadStatus: "error", uploadError: message });
            alert(message);
          });
      };
      video.onloadedmetadata = () => {
        applyUpload({
          duration: Number.isFinite(video.duration) ? video.duration : null,
          dimensions: video.videoWidth && video.videoHeight
            ? { width: video.videoWidth, height: video.videoHeight }
            : null,
        });
      };
      video.onerror = () => {
        applyUpload({ duration: null, dimensions: null });
      };
      video.src = metadataUrl;
    },
    [applyVideo, id, t, updateNodeData]
  );

  const handleSelectAsset = useCallback((asset: PopiAssetItem) => {
    const preview = getPopiVideoAssetPreview(asset);
    if (!preview) return;

    updateNodeData(id, {
      video: preview.videoUrl,
      videoRef: undefined,
      assetId: preview.assetId,
      previewVideoPoster: preview.previewPosterUrl || undefined,
      assetDetailLoading: true,
      assetDetailError: null,
      filename: preview.filename,
      format: "video/asset",
      duration: preview.duration,
      dimensions: preview.dimensions,
    });
    setIsAssetPickerOpen(false);

    const applyResolvedAsset = (
      resolved: NonNullable<Awaited<ReturnType<typeof resolvePopiVideoAsset>>>,
      metadata: { duration: number | null; dimensions: { width: number; height: number } | null }
    ) => {
      const currentNode = useWorkflowStore.getState().getNodeById(id);
      const currentData = currentNode?.data as SmartVideoNodeData | undefined;
      if (currentData?.assetId !== preview.assetId) return;
      const videoUrl = resolved.videoUrl;
      if (!videoUrl) return;

      updateNodeData(id, {
        video: videoUrl,
        videoRef: undefined,
        assetId: resolved.assetId,
        previewVideoPoster: resolved.previewPosterUrl || undefined,
        assetDetailLoading: false,
        assetDetailError: null,
        filename: resolved.filename,
        format: "video/asset",
        duration: resolved.duration || metadata.duration,
        dimensions: resolved.dimensions || metadata.dimensions,
        ...buildUploadedVideoPatch(videoUrl),
      });
    };

    void resolvePopiVideoAsset(asset)
      .then((resolved) => {
        if (!resolved?.videoUrl) return;
        if (resolved.dimensions && resolved.duration) {
          applyResolvedAsset(resolved, { duration: resolved.duration, dimensions: resolved.dimensions });
          return;
        }

        const video = document.createElement("video");
        video.preload = "metadata";
        video.onloadedmetadata = () => {
          applyResolvedAsset(resolved, {
            duration: Number.isFinite(video.duration) ? video.duration : null,
            dimensions: video.videoWidth && video.videoHeight
              ? { width: video.videoWidth, height: video.videoHeight }
              : null,
          });
        };
        video.onerror = () => applyResolvedAsset(resolved, {
          duration: preview.duration,
          dimensions: preview.dimensions,
        });
        video.src = resolved.videoUrl;
      })
      .catch((error) => {
        const currentNode = useWorkflowStore.getState().getNodeById(id);
        const currentData = currentNode?.data as SmartVideoNodeData | undefined;
        if (currentData?.assetId !== preview.assetId) return;

        updateNodeData(id, {
          assetDetailLoading: false,
          assetDetailError: error instanceof Error ? error.message : "Failed to load asset detail",
        });
      });
  }, [buildUploadedVideoPatch, id, updateNodeData]);

  const handleOpenFileDialog = useCallback(() => {
    if (isUploading) return;
    selectSingleNode(id);
    fileInputRef.current?.click();
  }, [id, isUploading, selectSingleNode]);

  return (
    <>
      <input
        ref={fileInputRef}
        type="file"
        accept={ACCEPTED_FORMATS}
        onChange={handleFileChange}
        className="hidden"
      />

      {selected && (
        <div className="nodrag nopan absolute left-1/2 -top-[50px] z-[10002] flex -translate-x-1/2 items-center gap-2">
          <button
            type="button"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              handleOpenFileDialog();
            }}
            aria-label={t("imageInput.localUpload")}
            disabled={isUploading}
            className="flex h-10 items-center gap-2 whitespace-nowrap rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-2)] px-3 text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)] transition-colors hover:border-[var(--border-strong)] hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)] disabled:cursor-wait disabled:opacity-60"
          >
            <SmartVideoToolbarIcon kind="upload" />
            <span>{t("imageInput.localUpload")}</span>
          </button>
          <button
            type="button"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              if (isUploading) return;
              selectSingleNode(id);
              setIsAssetPickerOpen(true);
            }}
            aria-label={t("imageInput.fromAssets")}
            disabled={isUploading}
            className="flex h-10 items-center gap-2 whitespace-nowrap rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-2)] px-3 text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)] transition-colors hover:border-[var(--border-strong)] hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)] disabled:cursor-wait disabled:opacity-60"
          >
            <SmartVideoToolbarIcon kind="assets" />
            <span>{t("imageInput.fromAssets")}</span>
          </button>
        </div>
      )}

      {isAssetPickerOpen && (
        <AssetPickerModal
          kind="video"
          onClose={() => setIsAssetPickerOpen(false)}
          onSelect={handleSelectAsset}
        />
      )}
    </>
  );
}

function SmartVideoNeutralView({
  id,
  data,
  selected,
  addToGenerationHistory = false,
}: {
  id: string;
  data: SmartVideoNodeData;
  selected?: boolean;
  addToGenerationHistory?: boolean;
}) {
  const isUploading = data.uploadStatus === "loading";

  return (
    <div className="relative h-full w-full">
      <GenerateVideoNodeView id={id} data={data} selected={selected} />
      <SmartVideoUploadToolbar
        id={id}
        selected={selected}
        isUploading={isUploading}
        addToGenerationHistory={addToGenerationHistory}
      />
      {isUploading && (
        <div className="nodrag nopan absolute inset-0 z-[10003] flex items-center justify-center rounded-lg bg-black/45 backdrop-blur-[1px]">
          <Spin />
        </div>
      )}
    </div>
  );
}

export function SmartVideoNode({ id, data, selected }: NodeProps<SmartVideoNodeType>) {
  const edges = useWorkflowStore((state) => state.edges);
  const providerEnvStatus = useWorkflowStore((state) => state.providerEnvStatus);
  const updateNodeInternals = useUpdateNodeInternals();
  const mode = deriveSmartVideoMode(id, data, edges);

  useEffect(() => {
    updateNodeInternals(id);
  }, [id, mode, updateNodeInternals]);

  if (mode === "generate") {
    if (isSmartGenerateUploadEnabled(providerEnvStatus)) {
      return <SmartVideoNeutralView id={id} data={data} selected={selected} addToGenerationHistory />;
    }
    return <GenerateVideoNodeView id={id} data={data} selected={selected} />;
  }

  if (mode === "neutral") {
    return <SmartVideoNeutralView id={id} data={data} selected={selected} />;
  }

  return <VideoInputNodeView id={id} data={data} selected={selected} />;
}
