"use client";

/**
 * 노드 기반 플로우 에디터 메인 컴포넌트
 * - 100% 캔버스 + Command Palette (/ 키) + Slide-over 속성 패널
 */

import { useCallback, useRef, useEffect, useState, useMemo } from "react";
import ReactFlow, {
  Background,
  Controls,
  MiniMap,
  Panel,
  ReactFlowProvider,
  useReactFlow,
} from "reactflow";
import "reactflow/dist/style.css";

import { useFlowEditorStore } from "@/lib/stores/flowEditorStore";
import { apiClient } from "@/lib/api/client";
import { CommandPalette } from "./CommandPalette";
import { SlideOverSheet } from "./SlideOverSheet";
import { FlowBreadcrumb } from "./FlowBreadcrumb";
import { NodeContextMenu } from "./NodeContextMenu";
import { ValidationNotification } from "./ValidationNotification";
import { FlowToolbar } from "./FlowToolbar";
import { getNodePaletteItem } from "./sidebar/nodePaletteConfig";
import { Pencil, Copy, Trash2 } from "lucide-react";
import { toast } from "sonner";

import { TableSourceNode } from "./nodes/TableSourceNode";
import { ExternalDBSourceNode } from "./nodes/ExternalDBSourceNode";
import { ConditionNode } from "./nodes/ConditionNode";
import { InsertActionNode } from "./nodes/InsertActionNode";
import { UpdateActionNode } from "./nodes/UpdateActionNode";
import { DeleteActionNode } from "./nodes/DeleteActionNode";
import { UpsertActionNode } from "./nodes/UpsertActionNode";
import { DataTransformNode } from "./nodes/DataTransformNode";
import { AggregateNode } from "./nodes/AggregateNode";
import { FormulaTransformNode } from "./nodes/FormulaTransformNode";
import { RestAPISourceNode } from "./nodes/RestAPISourceNode";
import { CommentNode } from "./nodes/CommentNode";
import { LogNode } from "./nodes/LogNode";
import { EmailActionNode } from "./nodes/EmailActionNode";
import { ScriptActionNode } from "./nodes/ScriptActionNode";
import { HttpRequestActionNode } from "./nodes/HttpRequestActionNode";
import { ProcedureCallActionNode } from "./nodes/ProcedureCallActionNode";
import { validateFlow } from "@/lib/utils/flowValidation";
import type { FlowValidation } from "@/lib/utils/flowValidation";

const nodeTypes = {
  tableSource: TableSourceNode,
  externalDBSource: ExternalDBSourceNode,
  restAPISource: RestAPISourceNode,
  condition: ConditionNode,
  dataTransform: DataTransformNode,
  aggregate: AggregateNode,
  formulaTransform: FormulaTransformNode,
  insertAction: InsertActionNode,
  updateAction: UpdateActionNode,
  deleteAction: DeleteActionNode,
  upsertAction: UpsertActionNode,
  emailAction: EmailActionNode,
  scriptAction: ScriptActionNode,
  httpRequestAction: HttpRequestActionNode,
  procedureCallAction: ProcedureCallActionNode,
  comment: CommentNode,
  log: LogNode,
};

interface FlowEditorInnerProps {
  initialFlowId?: number | null;
  onSaveComplete?: (flowId: number, flowName: string) => void;
  embedded?: boolean;
}

function getDefaultNodeData(type: string): Record<string, any> {
  const paletteItem = getNodePaletteItem(type);
  const base: Record<string, any> = {
    displayName: paletteItem?.label || `새 ${type} 노드`,
  };

  if (type === "restAPISource") {
    Object.assign(base, {
      method: "GET",
      url: "",
      headers: {},
      timeout: 30000,
      responseFields: [],
      responseMapping: "",
    });
  }

  if (["insertAction", "updateAction", "deleteAction", "upsertAction"].includes(type)) {
    Object.assign(base, {
      targetType: "internal",
      fieldMappings: [],
      options: {},
    });
    if (type === "updateAction" || type === "deleteAction") {
      base.whereConditions = [];
    }
    if (type === "upsertAction") {
      base.conflictKeys = [];
    }
  }

  if (type === "emailAction") {
    Object.assign(base, {
      displayName: "메일 발송",
      smtpConfig: { host: "", port: 587, secure: false },
      from: "",
      to: "",
      subject: "",
      body: "",
      bodyType: "text",
    });
  }

  if (type === "scriptAction") {
    Object.assign(base, {
      displayName: "스크립트 실행",
      scriptType: "python",
      executionMode: "inline",
      inlineScript: "",
      inputMethod: "stdin",
      inputFormat: "json",
      outputHandling: { captureStdout: true, captureStderr: true, parseOutput: "text" },
    });
  }

  if (type === "httpRequestAction") {
    Object.assign(base, {
      displayName: "HTTP 요청",
      url: "",
      method: "GET",
      bodyType: "none",
      authentication: { type: "none" },
      options: { timeout: 30000, followRedirects: true },
    });
  }

  return base;
}

function FlowEditorInner({
  initialFlowId,
  onSaveComplete,
  embedded = false,
}: FlowEditorInnerProps) {
  const reactFlowWrapper = useRef<HTMLDivElement>(null);
  const { screenToFlowPosition, setCenter, getViewport } = useReactFlow();

  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
  const [slideOverOpen, setSlideOverOpen] = useState(false);
  const [contextMenu, setContextMenu] = useState<{
    x: number;
    y: number;
    nodeId: string;
  } | null>(null);

  const {
    nodes,
    edges,
    onNodesChange,
    onEdgesChange,
    onConnect,
    onNodeDragStart,
    addNode,
    selectNodes,
    selectedNodes,
    removeNodes,
    undo,
    redo,
    loadFlow,
  } = useFlowEditorStore();

  const validations = useMemo<FlowValidation[]>(
    () => validateFlow(nodes, edges),
    [nodes, edges],
  );

  const handleValidationNodeClick = useCallback(
    (nodeId: string) => {
      const node = nodes.find((n) => n.id === nodeId);
      if (node) {
        selectNodes([nodeId]);
        setCenter(node.position.x + 125, node.position.y + 50, {
          zoom: 1.5,
          duration: 500,
        });
      }
    },
    [nodes, selectNodes, setCenter],
  );

  // 노드 선택 시 속성 패널 열기
  useEffect(() => {
    if (selectedNodes.length > 0) {
      setSlideOverOpen(true);
    }
  }, [selectedNodes]);

  // 플로우 로드
  useEffect(() => {
    const fetchAndLoadFlow = async () => {
      if (initialFlowId) {
        try {
          const response = await apiClient.get(
            `/dataflow/node-flows/${initialFlowId}`,
          );
          if (response.data.success && response.data.data) {
            const flow = response.data.data;
            const flowData =
              typeof flow.flowData === "string"
                ? JSON.parse(flow.flowData)
                : flow.flowData;

            loadFlow(
              flow.flowId,
              flow.flowName,
              flow.flowDescription || "",
              flowData.nodes || [],
              flowData.edges || [],
            );
          }
        } catch (error) {
          console.error("플로우 로드 실패:", error);
        }
      }
    };
    fetchAndLoadFlow();
  }, [initialFlowId, loadFlow]);

  const onSelectionChange = useCallback(
    ({ nodes: selected }: { nodes: any[] }) => {
      const selectedIds = selected.map((n) => n.id);
      selectNodes(selectedIds);
    },
    [selectNodes],
  );

  // 더블클릭으로 속성 패널 열기
  const onNodeDoubleClick = useCallback(
    (_event: React.MouseEvent, node: any) => {
      selectNodes([node.id]);
      setSlideOverOpen(true);
    },
    [selectNodes],
  );

  // 우클릭 컨텍스트 메뉴
  const onNodeContextMenu = useCallback(
    (event: React.MouseEvent, node: any) => {
      event.preventDefault();
      selectNodes([node.id]);
      setContextMenu({
        x: event.clientX,
        y: event.clientY,
        nodeId: node.id,
      });
    },
    [selectNodes],
  );

  // 캔버스 우클릭 → 커맨드 팔레트
  const onPaneContextMenu = useCallback(
    (event: React.MouseEvent | MouseEvent) => {
      event.preventDefault();
      setCommandPaletteOpen(true);
    },
    [],
  );

  // 컨텍스트 메뉴 아이템 생성
  const getContextMenuItems = useCallback(
    (nodeId: string) => {
      const node = nodes.find((n) => n.id === nodeId);
      const nodeName = (node?.data as any)?.displayName || "노드";
      return [
        {
          label: "속성 편집",
          icon: <Pencil className="h-3.5 w-3.5" />,
          onClick: () => {
            selectNodes([nodeId]);
            setSlideOverOpen(true);
          },
        },
        {
          label: "복제",
          icon: <Copy className="h-3.5 w-3.5" />,
          onClick: () => {
            if (!node) return;
            const newNode: any = {
              id: `node_${Date.now()}`,
              type: node.type,
              position: {
                x: node.position.x + 40,
                y: node.position.y + 40,
              },
              data: { ...(node.data as any) },
            };
            addNode(newNode);
            selectNodes([newNode.id]);
            toast.success(`"${nodeName}" 노드를 복제했어요`);
          },
        },
        {
          label: "삭제",
          icon: <Trash2 className="h-3.5 w-3.5" />,
          onClick: () => {
            removeNodes([nodeId]);
            toast.success(`"${nodeName}" 노드를 삭제했어요`);
          },
          danger: true,
        },
      ];
    },
    [nodes, selectNodes, addNode, removeNodes],
  );

  // "/" 키로 커맨드 팔레트 열기, Esc로 속성 패널 닫기 등
  const onKeyDown = useCallback(
    (event: React.KeyboardEvent) => {
      const target = event.target as HTMLElement;
      const isInput =
        target.tagName === "INPUT" ||
        target.tagName === "TEXTAREA" ||
        target.isContentEditable;

      if (!isInput && event.key === "/" && !event.ctrlKey && !event.metaKey) {
        event.preventDefault();
        setCommandPaletteOpen(true);
        return;
      }

      if ((event.ctrlKey || event.metaKey) && event.key === "z" && !event.shiftKey) {
        event.preventDefault();
        undo();
        return;
      }

      if (
        ((event.ctrlKey || event.metaKey) && event.key === "y") ||
        ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "z")
      ) {
        event.preventDefault();
        redo();
        return;
      }

      if (
        (event.key === "Delete" || event.key === "Backspace") &&
        selectedNodes.length > 0 &&
        !isInput
      ) {
        event.preventDefault();
        removeNodes(selectedNodes);
      }
    },
    [selectedNodes, removeNodes, undo, redo],
  );

  // 커맨드 팔레트에서 노드 선택 시 뷰포트 중앙에 배치
  const handleCommandSelect = useCallback(
    (nodeType: string) => {
      const viewport = getViewport();
      const wrapper = reactFlowWrapper.current;
      if (!wrapper) return;

      const rect = wrapper.getBoundingClientRect();
      const centerX = rect.width / 2;
      const centerY = rect.height / 2;

      const position = screenToFlowPosition({
        x: rect.left + centerX,
        y: rect.top + centerY,
      });

      const newNode: any = {
        id: `node_${Date.now()}`,
        type: nodeType,
        position,
        data: getDefaultNodeData(nodeType),
      };

      addNode(newNode);
      selectNodes([newNode.id]);
    },
    [screenToFlowPosition, addNode, selectNodes, getViewport],
  );

  // 기존 드래그 앤 드롭 (하위 호환)
  const onDragOver = useCallback((event: React.DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
  }, []);

  const onDrop = useCallback(
    (event: React.DragEvent) => {
      event.preventDefault();
      const type = event.dataTransfer.getData("application/reactflow");
      if (!type) return;

      const position = screenToFlowPosition({
        x: event.clientX,
        y: event.clientY,
      });

      const newNode: any = {
        id: `node_${Date.now()}`,
        type,
        position,
        data: getDefaultNodeData(type),
      };

      addNode(newNode);
    },
    [screenToFlowPosition, addNode],
  );

  return (
    <div
      className="relative flex h-full w-full"
      style={{ height: "100%", overflow: "hidden" }}
    >
      {/* 100% 캔버스 */}
      <div
        className="relative flex-1"
        ref={reactFlowWrapper}
        onKeyDown={onKeyDown}
        tabIndex={0}
      >
        <ReactFlow
          nodes={nodes as any}
          edges={edges as any}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onConnect={onConnect}
          onNodeDragStart={onNodeDragStart}
          onSelectionChange={onSelectionChange}
          onNodeDoubleClick={onNodeDoubleClick}
          onNodeContextMenu={onNodeContextMenu}
          onPaneContextMenu={onPaneContextMenu}
          onPaneClick={() => setContextMenu(null)}
          onDragOver={onDragOver}
          onDrop={onDrop}
          nodeTypes={nodeTypes}
          fitView
          className="bg-muted/30"
          deleteKeyCode={["Delete", "Backspace"]}
        >
          <Background gap={20} size={1} color="hsl(var(--border))" />

          <Controls
            className="!rounded-lg !border !border-border !bg-background !shadow-sm [&>button]:!border-border [&>button]:!bg-background [&>button]:!text-muted-foreground [&>button:hover]:!bg-accent [&>button:hover]:!text-foreground"
            showInteractive={false}
          />

          <MiniMap
            className="!rounded-lg !border !border-border !bg-background !shadow-sm"
            nodeColor={(node) => {
              const item = getNodePaletteItem(node.type || "");
              return item?.color || "#6B7280";
            }}
            maskColor="hsl(var(--muted) / 0.5)"
          />

          {/* Breadcrumb (좌상단) */}
          <Panel position="top-left" className="pointer-events-auto">
            <div className="rounded-lg border border-zinc-700/60 bg-zinc-900/90 px-3 py-2 backdrop-blur-sm">
              <FlowBreadcrumb />
            </div>
          </Panel>

          {/* 플로팅 툴바 (상단 중앙) */}
          <Panel position="top-center" className="pointer-events-auto">
            <FlowToolbar
              validations={validations}
              onSaveComplete={onSaveComplete}
              onOpenCommandPalette={() => setCommandPaletteOpen(true)}
            />
          </Panel>
        </ReactFlow>
      </div>

      {/* Slide-over 속성 패널 */}
      <SlideOverSheet
        isOpen={slideOverOpen && selectedNodes.length > 0}
        onClose={() => setSlideOverOpen(false)}
      />

      {/* Command Palette */}
      <CommandPalette
        isOpen={commandPaletteOpen}
        onClose={() => setCommandPaletteOpen(false)}
        onSelectNode={handleCommandSelect}
      />

      {/* 노드 우클릭 컨텍스트 메뉴 */}
      {contextMenu && (
        <NodeContextMenu
          x={contextMenu.x}
          y={contextMenu.y}
          items={getContextMenuItems(contextMenu.nodeId)}
          onClose={() => setContextMenu(null)}
        />
      )}

      {/* 검증 알림 */}
      <ValidationNotification
        validations={validations}
        onNodeClick={handleValidationNodeClick}
      />

      {/* 빈 캔버스 힌트 */}
      {nodes.length === 0 && !commandPaletteOpen && (
        <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
          <div className="text-center">
            <p className="mb-2 text-sm text-zinc-500">
              캔버스가 비어 있어요
            </p>
            <p className="text-xs text-zinc-600">
              <kbd className="rounded border border-zinc-700 bg-zinc-800 px-1.5 py-0.5 font-mono text-[11px]">
                /
              </kbd>{" "}
              키를 눌러서 노드를 추가해 보세요
            </p>
          </div>
        </div>
      )}
    </div>
  );
}

interface FlowEditorProps {
  initialFlowId?: number | null;
  onSaveComplete?: (flowId: number, flowName: string) => void;
  embedded?: boolean;
}

export function FlowEditor({
  initialFlowId,
  onSaveComplete,
  embedded = false,
}: FlowEditorProps = {}) {
  return (
    <div className="h-full w-full">
      <ReactFlowProvider>
        <FlowEditorInner
          initialFlowId={initialFlowId}
          onSaveComplete={onSaveComplete}
          embedded={embedded}
        />
      </ReactFlowProvider>
    </div>
  );
}
