"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { CloseOutlined, PictureOutlined, PlusOutlined } from "@ant-design/icons";
import { useWorkflowStore } from "@/store/workflowStore";
import { NodeType } from "@/types";
import { useReactFlow } from "@xyflow/react";
import { useI18n } from "@/i18n";
import { buildDefaultGenerationNodeData } from "@/utils/defaultGenerationNodeModel";
import { GenerateNodeMenu } from "./GenerateNodeMenu";
import { ModelSearchDialog } from "./modals/ModelSearchDialog";
import { AssetPickerModal, PopiAssetItem } from "./AssetPickerModal";
import { getPopiImageAssetPreview, resolvePopiImageAsset } from "@/lib/popiAssetApi";
import { useFTUXStore, TutorialStep } from "@/store/ftuxStore";

// Get the center of the React Flow pane in screen coordinates
function getPaneCenter() {
  const pane = document.querySelector(".react-flow");
  if (pane) {
    const rect = pane.getBoundingClientRect();
    return {
      x: rect.left + rect.width / 2,
      y: rect.top + rect.height / 2,
    };
  }
  return { x: window.innerWidth / 2, y: window.innerHeight / 2 };
}

function GenerateComboButton() {
  const { t } = useI18n();
  const [isOpen, setIsOpen] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);
  const addNode = useWorkflowStore((state) => state.addNode);
  const selectSingleNode = useWorkflowStore((state) => state.selectSingleNode);
  const { screenToFlowPosition } = useReactFlow();

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
        setIsOpen(false);
      }
    };

    if (isOpen) {
      document.addEventListener("mousedown", handleClickOutside);
    }

    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [isOpen]);

  const handleAddNode = async (type: NodeType) => {
    const center = getPaneCenter();
    const position = screenToFlowPosition({
      x: center.x + Math.random() * 100 - 50,
      y: center.y + Math.random() * 100 - 50,
    });

    const nodeId = addNode(type, position, await buildDefaultGenerationNodeData(type));
    selectSingleNode(nodeId);
    setIsOpen(false);
  };

  const handleDragStart = (event: React.DragEvent, type: NodeType) => {
    event.dataTransfer.setData("application/node-type", type);
    event.dataTransfer.effectAllowed = "copy";
    setIsOpen(false);
  };

  return (
    <div className="relative" ref={menuRef}>
      <button
        onClick={() => setIsOpen(!isOpen)}
        title={t("toolbar.addNode")}
        aria-label={t("toolbar.addNode")}
        aria-expanded={isOpen}
        className="flex h-8 w-8 items-center justify-center rounded-lg bg-neutral-200 text-neutral-950 shadow-sm transition-colors hover:bg-neutral-100"
      >
        <span className={`text-[18px] leading-none transition-transform duration-200 ${isOpen ? "rotate-45" : "rotate-0"}`}>
          <PlusOutlined />
        </span>
        <span className="sr-only">{t("toolbar.addNode")}</span>
      </button>

      {isOpen && (
        <GenerateNodeMenu
          draggable
          onSelect={handleAddNode}
          onDragStart={handleDragStart}
          className="absolute left-full top-0 ml-2 min-w-[140px] overflow-visible outline-none"
        />
      )}
    </div>
  );
}

function ResourceImageButton() {
  const { t } = useI18n();
  const [isAssetPickerOpen, setIsAssetPickerOpen] = useState(false);
  const addNode = useWorkflowStore((state) => state.addNode);
  const updateNodeData = useWorkflowStore((state) => state.updateNodeData);
  const selectSingleNode = useWorkflowStore((state) => state.selectSingleNode);
  const { screenToFlowPosition } = useReactFlow();

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

    const center = getPaneCenter();
    const position = screenToFlowPosition({
      x: center.x + Math.random() * 100 - 50,
      y: center.y + Math.random() * 100 - 50,
    });

    const nodeId = addNode("imageInput", position, {
      image: preview.imageUrl || preview.previewImageUrl,
      imageRef: undefined,
      assetId: preview.assetId,
      previewImage: preview.previewImageUrl,
      assetDetailLoading: true,
      assetDetailError: null,
      filename: preview.filename,
      dimensions: preview.dimensions,
    });

    selectSingleNode(nodeId);
    setIsAssetPickerOpen(false);

    const applyResolvedAsset = (
      resolved: NonNullable<Awaited<ReturnType<typeof resolvePopiImageAsset>>>,
      dimensions: { width: number; height: number } | null
    ) => {
      updateNodeData(nodeId, {
        image: resolved.imageUrl || resolved.previewImageUrl,
        imageRef: undefined,
        assetId: resolved.assetId,
        previewImage: resolved.previewImageUrl,
        assetDetailLoading: false,
        assetDetailError: null,
        filename: resolved.filename,
        dimensions,
      });
    };

    void resolvePopiImageAsset(asset)
      .then((resolved) => {
        if (!resolved) {
          updateNodeData(nodeId, {
            assetDetailLoading: false,
            assetDetailError: null,
          });
          return;
        }

        if (resolved.dimensions || typeof Image === "undefined") {
          applyResolvedAsset(resolved, resolved.dimensions);
          return;
        }

        const image = new Image();
        image.onload = () => applyResolvedAsset(resolved, { width: image.width, height: image.height });
        image.onerror = () => applyResolvedAsset(resolved, preview.dimensions);
        image.src = resolved.previewImageUrl;
      })
      .catch((error: unknown) => {
        updateNodeData(nodeId, {
          assetDetailLoading: false,
          assetDetailError: error instanceof Error ? error.message : "Failed to load asset detail",
        });
      });
  };

  return (
    <>
      <button
        onClick={() => setIsAssetPickerOpen(true)}
        title={t("imageInput.fromAssets")}
        aria-label={t("imageInput.fromAssets")}
        className="flex h-8 w-8 items-center justify-center rounded-lg text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
      >
        <PictureOutlined className="text-[18px]" />
        <span className="sr-only">{t("imageInput.fromAssets")}</span>
      </button>

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

export function FloatingActionBar() {
  const { t } = useI18n();
  const nodes = useWorkflowStore((state) => state.nodes) ?? [];
  const isRunning = useWorkflowStore((state) => state.isRunning) ?? false;
  const currentNodeIds = useWorkflowStore((state) => state.currentNodeIds) ?? [];
  const executeWorkflow = useWorkflowStore((state) => state.executeWorkflow) ?? (async () => {});
  const regenerateNode = useWorkflowStore((state) => state.regenerateNode) ?? (async () => {});
  const executeSelectedNodes = useWorkflowStore((state) => state.executeSelectedNodes) ?? (async () => {});
  const stopWorkflow = useWorkflowStore((state) => state.stopWorkflow) ?? (() => {});
  const mockTutorialExecution = useWorkflowStore((state) => state.mockTutorialExecution) ?? (async () => {});
  const validateWorkflow = useWorkflowStore((state) => state.validateWorkflow);
  const edgeStyle = useWorkflowStore((state) => state.edgeStyle);
  const setEdgeStyle = useWorkflowStore((state) => state.setEdgeStyle);
  const setModelSearchOpen = useWorkflowStore((state) => state.setModelSearchOpen);
  const modelSearchOpen = useWorkflowStore((state) => state.modelSearchOpen);
  const modelSearchProvider = useWorkflowStore((state) => state.modelSearchProvider);
  const [tutorialActive, setTutorialActive] = useState(false);
  const [currentTutorialStep, setCurrentTutorialStep] = useState(0);
  const [tutorialSteps, setTutorialSteps] = useState<TutorialStep[]>([]);
  const [runMenuOpen, setRunMenuOpen] = useState(false);
  const runMenuRef = useRef<HTMLDivElement>(null);
  const { valid, errors } = validateWorkflow?.() ?? { valid: true, errors: [] };

  useEffect(() => {
    const unsubscribe = useFTUXStore.subscribe((state) => {
      setTutorialActive(state.tutorialActive);
      setCurrentTutorialStep(state.currentTutorialStep);
      setTutorialSteps(state.tutorialSteps);
    });

    const currentState = useFTUXStore.getState();
    setTutorialActive(currentState.tutorialActive);
    setCurrentTutorialStep(currentState.currentTutorialStep);
    setTutorialSteps(currentState.tutorialSteps);

    return unsubscribe;
  }, []);

  const selectedNodes = useMemo(() => nodes.filter((node) => node.selected), [nodes]);
  const selectedNode = useMemo(() => selectedNodes.length === 1 ? selectedNodes[0] : null, [selectedNodes]);

  const isRunOptionsTutorialStep = useMemo(() => {
    if (!tutorialActive || tutorialSteps.length === 0) return false;
    const currentStep = tutorialSteps[currentTutorialStep];
    return currentStep?.id === "explain-run-options";
  }, [currentTutorialStep, tutorialActive, tutorialSteps]);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (runMenuRef.current && !runMenuRef.current.contains(event.target as Node) && !isRunOptionsTutorialStep) {
        setRunMenuOpen(false);
      }
    };

    if (runMenuOpen) {
      document.addEventListener("mousedown", handleClickOutside);
    }

    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [isRunOptionsTutorialStep, runMenuOpen]);

  useEffect(() => {
    if (isRunOptionsTutorialStep) {
      setRunMenuOpen(true);
    }
  }, [isRunOptionsTutorialStep]);

  useEffect(() => {
    if (!tutorialActive || tutorialSteps.length === 0) return;
    const currentStep = tutorialSteps[currentTutorialStep];
    if (
      currentStep?.id === "run-workflow" ||
      currentStep?.id === "demonstrate-downstream" ||
      currentStep?.id === "demonstrate-complete"
    ) {
      setRunMenuOpen(false);
    }
  }, [currentTutorialStep, tutorialActive, tutorialSteps]);

  const runningNodeCount = currentNodeIds.length;
  const getRunningLabel = useCallback(() => {
    if (runningNodeCount === 0) return t("toolbar.running");
    if (runningNodeCount === 1) {
      const node = nodes.find((item) => item.id === currentNodeIds[0]);
      const nodeName = node?.data?.customTitle || node?.type || "node";
      return t("toolbar.runningNode", { node: nodeName });
    }
    return t("toolbar.runningNodes", { count: runningNodeCount });
  }, [currentNodeIds, nodes, runningNodeCount, t]);

  const toggleEdgeStyle = () => {
    setEdgeStyle(edgeStyle === "angular" ? "curved" : "angular");
  };

  const handleRunClick = useCallback(() => {
    const ftuxState = useFTUXStore.getState();
    const currentStep = ftuxState.tutorialSteps[ftuxState.currentTutorialStep];

    if (isRunning) {
      stopWorkflow();
    } else if (ftuxState.tutorialActive && currentStep?.id === "run-workflow") {
      void mockTutorialExecution();
    } else {
      void executeWorkflow();
    }
  }, [executeWorkflow, isRunning, mockTutorialExecution, stopWorkflow]);

  const handleRunFromSelected = useCallback(() => {
    if (!selectedNode) return;
    void executeWorkflow(selectedNode.id);
    setRunMenuOpen(false);
  }, [executeWorkflow, selectedNode]);

  const handleRunSelectedOnly = useCallback(() => {
    if (!selectedNode) return;
    void regenerateNode(selectedNode.id);
    setRunMenuOpen(false);
  }, [regenerateNode, selectedNode]);

  const handleRunSelectedNodes = useCallback(() => {
    if (selectedNodes.length === 0) return;
    void executeSelectedNodes(selectedNodes.map((node) => node.id));
    setRunMenuOpen(false);
  }, [executeSelectedNodes, selectedNodes]);

  return (
    <div className="fixed left-5 top-1/2 z-50 -translate-y-1/2">
      <div className="flex flex-col items-center gap-2 bg-neutral-800/95 rounded-2xl shadow-lg border border-neutral-700/80 px-1.5 py-1.5">
        <GenerateComboButton />
        <ResourceImageButton />

        <button
          onClick={() => setModelSearchOpen(true)}
          title={t("toolbar.browseModels")}
          aria-label={t("toolbar.allModels")}
          className="flex h-8 w-8 items-center justify-center rounded-lg text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
        >
          <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M4 7h16M4 12h16M4 17h16" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M8 4v16M16 4v16" />
          </svg>
          <span className="sr-only">{t("toolbar.allModels")}</span>
        </button>

        <button
          onClick={toggleEdgeStyle}
          title={t("toolbar.switchConnectors", { style: edgeStyle === "angular" ? t("toolbar.curved") : t("toolbar.angular") })}
          className="flex h-8 w-8 items-center justify-center rounded-lg text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
        >
          {edgeStyle === "angular" ? (
            <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
              <path strokeLinecap="round" strokeLinejoin="round" d="M4 12h4l4-8 4 8h4" />
            </svg>
          ) : (
            <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
              <path strokeLinecap="round" strokeLinejoin="round" d="M4 12c0 0 4-8 8-8s8 8 8 8" />
            </svg>
          )}
        </button>

        <div className="relative flex items-center" ref={runMenuRef}>
          <button
            onClick={handleRunClick}
            disabled={!valid && !isRunning}
            title={!valid ? errors.join("\n") : isRunning ? t("toolbar.stop") : t("toolbar.run")}
            data-tutorial="floating-run-button"
            className={`flex h-8 w-8 items-center justify-center rounded-lg transition-colors ${
              isRunning
                ? "bg-neutral-100 text-neutral-950 hover:bg-neutral-200"
                : valid
                  ? "bg-neutral-100 text-neutral-950 hover:bg-neutral-200"
                  : "cursor-not-allowed bg-neutral-700 text-neutral-500"
            }`}
          >
            {isRunning ? (
              <>
                <svg className="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
                  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
                  <path
                    className="opacity-75"
                    fill="currentColor"
                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
                  />
                </svg>
                <span className="sr-only" title={getRunningLabel()}>
                  {runningNodeCount > 1 ? t("toolbar.nodesCount", { count: runningNodeCount }) : t("toolbar.stop")}
                </span>
              </>
            ) : (
              <>
                <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M8 5v14l11-7z" />
                </svg>
                <span className="sr-only">{t("toolbar.run")}</span>
              </>
            )}
          </button>

          {!isRunning && valid && (
            <button
              onClick={() => setRunMenuOpen(!runMenuOpen)}
              data-tutorial="floating-run-dropdown"
              className="absolute -bottom-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full border border-neutral-600 bg-neutral-700 text-neutral-200 transition-colors hover:bg-neutral-600"
              title={t("toolbar.runOptions")}
              aria-label={t("toolbar.runOptions")}
            >
              <svg
                className={`h-2.5 w-2.5 transition-transform ${runMenuOpen ? "rotate-180" : ""}`}
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2.5}
              >
                <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
              </svg>
            </button>
          )}

          {runMenuOpen && !isRunning && (
            <div
              data-tutorial="floating-run-menu"
              className="absolute bottom-0 left-full ml-2 min-w-[180px] overflow-hidden rounded-lg border border-neutral-700 bg-neutral-800 shadow-xl"
            >
              <button
                onClick={() => {
                  void executeWorkflow();
                  setRunMenuOpen(false);
                }}
                className="flex w-full items-center gap-2 px-3 py-2 text-left text-[11px] font-medium text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
              >
                <svg className="h-3.5 w-3.5" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M8 5v14l11-7z" />
                </svg>
                {t("toolbar.runEntire")}
              </button>
              <button
                onClick={handleRunFromSelected}
                disabled={!selectedNode}
                title={!selectedNode ? t("toolbar.selectSingleNode") : undefined}
                className={`flex w-full items-center gap-2 px-3 py-2 text-left text-[11px] font-medium transition-colors ${
                  selectedNode
                    ? "text-neutral-300 hover:bg-neutral-700 hover:text-neutral-100"
                    : "cursor-not-allowed text-neutral-500"
                }`}
              >
                <svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
                </svg>
                {t("toolbar.runFromSelected")}
              </button>
              <button
                onClick={handleRunSelectedOnly}
                disabled={!selectedNode}
                title={!selectedNode ? t("toolbar.selectSingleNode") : undefined}
                className={`flex w-full items-center gap-2 px-3 py-2 text-left text-[11px] font-medium transition-colors ${
                  selectedNode
                    ? "text-neutral-300 hover:bg-neutral-700 hover:text-neutral-100"
                    : "cursor-not-allowed text-neutral-500"
                }`}
              >
                <svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 010 1.972l-11.54 6.347a1.125 1.125 0 01-1.667-.986V5.653z" />
                </svg>
                {t("toolbar.runSelectedOnly")}
              </button>
              <button
                onClick={handleRunSelectedNodes}
                disabled={selectedNodes.length === 0}
                title={selectedNodes.length === 0 ? t("toolbar.selectOneOrMoreNodes") : t("toolbar.runSelectedNodesCount", { count: selectedNodes.length })}
                className={`flex w-full items-center gap-2 px-3 py-2 text-left text-[11px] font-medium transition-colors ${
                  selectedNodes.length > 0
                    ? "text-neutral-300 hover:bg-neutral-700 hover:text-neutral-100"
                    : "cursor-not-allowed text-neutral-500"
                }`}
              >
                <svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 010 1.972l-11.54 6.347a1.125 1.125 0 01-1.667-.986V5.653z" />
                  <path strokeLinecap="round" strokeLinejoin="round" d="M9.75 9.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 010 1.972l-11.54 6.347a1.125 1.125 0 01-1.667-.986V9.653z" />
                </svg>
                {selectedNodes.length > 0
                  ? t("toolbar.runSelectedNodesCount", { count: selectedNodes.length })
                  : t("toolbar.runSelectedNodes")}
              </button>
            </div>
          )}
        </div>
      </div>

      <ModelSearchDialog
        isOpen={modelSearchOpen}
        onClose={() => setModelSearchOpen(false)}
        initialProvider={modelSearchProvider}
      />
    </div>
  );
}
