import WorkflowComponent from "./Component";
import ComponentSettingsModal from "./ComponentSettingsModal";
import ComponentsModal from "./ComponentsModal";
import RunModal from "./RunModal";
import { loadComponentsAndCategories } from "./Utils";
import IconProp from "../../../Types/Icon/IconProp";
import ObjectID from "../../../Types/ObjectID";
import { ComponentType, NodeType, } from "../../../Types/Workflow/Component";
import React, { useCallback, useEffect, useRef, useState, } from "react";
import ReactFlow, { Background, BackgroundVariant, Controls, MarkerType, MiniMap, addEdge, getConnectedEdges, updateEdge, useEdgesState, useNodesState, } from "reactflow";
// 👇 you need to import the reactflow styles
import "reactflow/dist/style.css";
export const getPlaceholderTriggerNode = () => {
    return {
        id: ObjectID.generate().toString(),
        type: "node",
        position: { x: 100, y: 100 },
        data: {
            metadata: {
                iconProp: IconProp.Bolt,
                componentType: ComponentType.Trigger,
                title: "Trigger",
                description: "Please click here to add trigger",
            },
            metadataId: "",
            internalId: "",
            nodeType: NodeType.PlaceholderNode,
            id: "",
            error: "",
        },
    };
};
const nodeTypes = {
    node: WorkflowComponent,
};
const edgeStyle = {
    strokeWidth: "2px",
    stroke: "#94a3b8",
    color: "#94a3b8",
};
const selectedEdgeStyle = {
    strokeWidth: "2.5px",
    stroke: "#6366f1",
    color: "#6366f1",
};
export const getEdgeDefaultProps = (selected) => {
    var _a, _b;
    return {
        type: "smoothstep",
        animated: selected,
        markerEnd: {
            type: MarkerType.ArrowClosed,
            color: selected
                ? ((_a = selectedEdgeStyle.color) === null || _a === void 0 ? void 0 : _a.toString()) || ""
                : ((_b = edgeStyle.color) === null || _b === void 0 ? void 0 : _b.toString()) || "",
            width: 20,
            height: 20,
        },
        style: selected ? Object.assign({}, selectedEdgeStyle) : Object.assign({}, edgeStyle),
    };
};
const Workflow = (props) => {
    const [allComponentMetadata, setAllComponentMetadata] = useState([]);
    const [allComponentCategories, setAllComponentCategories] = useState([]);
    useEffect(() => {
        const value = loadComponentsAndCategories();
        setAllComponentCategories(value.categories);
        setAllComponentMetadata(value.components);
    }, []);
    const edgeUpdateSuccessful = useRef(true);
    const [showComponentSettingsModal, setShowComponentSettingsModal] = useState(false);
    const [selectedNodeData, setSelectedNodeData] = useState(null);
    const onNodeClick = (data) => {
        // if placeholder node is clicked then show modal.
        if (data.nodeType === NodeType.PlaceholderNode) {
            if (data.componentType === ComponentType.Component) {
                setShowComponentsModal(true);
            }
            else {
                setShowTriggersModal(true);
            }
        }
        else {
            setShowComponentSettingsModal(true);
            setSelectedNodeData(data);
        }
    };
    useEffect(() => {
        if (props.showComponentsPickerModal) {
            setShowComponentsModal(true);
        }
        else {
            setShowComponentsModal(false);
        }
    }, [props.showComponentsPickerModal]);
    useEffect(() => {
        if (props.showRunModal) {
            setShowRunModal(true);
        }
        else {
            setShowComponentsModal(false);
        }
    }, [props.showRunModal]);
    const deleteNode = (id) => {
        // remove the node.
        const nodesToDelete = [...nodes].filter((node) => {
            return node.data.id === id;
        });
        const edgeToDelete = getConnectedEdges(nodesToDelete, edges);
        setNodes((nds) => {
            let nodeToUpdate = nds.filter((node) => {
                return node.data.id !== id;
            });
            if (nodeToUpdate.filter((n) => {
                return (n.data.componentType === ComponentType.Trigger &&
                    n.data.nodeType === NodeType.Node);
            }).length === 0) {
                nodeToUpdate = nodeToUpdate.concat(getPlaceholderTriggerNode());
            }
            return nodeToUpdate;
        });
        setEdges((eds) => {
            return eds
                .filter((edge) => {
                const idsToDelete = edgeToDelete.map((e) => {
                    return e.id;
                });
                return !idsToDelete.includes(edge.id);
            })
                .map((edge) => {
                return Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
            });
        });
    };
    const [nodes, setNodes, onNodesChange] = useNodesState(props.initialNodes.map((node) => {
        node.data.onClick = onNodeClick;
        return node;
    }));
    const [edges, setEdges, onEdgesChange] = useEdgesState(props.initialEdges.map((edge) => {
        // add style.
        edge = Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
        return edge;
    }));
    useEffect(() => {
        if (props.onWorkflowUpdated) {
            props.onWorkflowUpdated(nodes, edges);
        }
    }, [nodes, edges]);
    const proOptions = { hideAttribution: true };
    const onConnect = useCallback((params) => {
        return setEdges((eds) => {
            return addEdge(Object.assign(Object.assign({}, params), getEdgeDefaultProps(params.selected)), eds.map((edge) => {
                return Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
            }));
        });
    }, [setEdges]);
    const onEdgeUpdateStart = useCallback(() => {
        edgeUpdateSuccessful.current = false;
    }, []);
    const onEdgeUpdate = useCallback((oldEdge, newConnection) => {
        edgeUpdateSuccessful.current = true;
        setEdges((eds) => {
            var _a;
            return updateEdge(Object.assign(Object.assign({}, oldEdge), { markerEnd: {
                    type: MarkerType.ArrowClosed,
                    color: ((_a = edgeStyle.color) === null || _a === void 0 ? void 0 : _a.toString()) || "",
                }, style: edgeStyle }), newConnection, eds.map((edge) => {
                return Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
            }));
        });
    }, []);
    const onEdgeUpdateEnd = useCallback((_props, edge) => {
        if (!edgeUpdateSuccessful.current) {
            setEdges((eds) => {
                return eds
                    .filter((e) => {
                    return e.id !== edge.id;
                })
                    .map((edge) => {
                    return Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
                });
            });
        }
        edgeUpdateSuccessful.current = true;
    }, []);
    const [showComponentsModal, setShowComponentsModal] = useState(false);
    const [showTriggersModal, setShowTriggersModal] = useState(false);
    const [showRunModal, setShowRunModal] = useState(false);
    useEffect(() => {
        props.onComponentPickerModalUpdate(showComponentsModal);
    }, [showComponentsModal]);
    const refreshEdges = () => {
        setEdges((eds) => {
            return eds.map((edge) => {
                return Object.assign(Object.assign({}, edge), getEdgeDefaultProps(edge.selected || false));
            });
        });
    };
    useEffect(() => {
        props.onRunModalUpdate(showRunModal);
    }, [showRunModal]);
    const addToGraph = (componentMetadata) => {
        const metaDataId = componentMetadata.id;
        let hasFoundExistingId = true;
        let idCounter = 1;
        while (hasFoundExistingId) {
            const id = `${metaDataId}-${idCounter}`;
            const exitingNode = nodes.find((i) => {
                return i.data.id === id;
            });
            if (!exitingNode) {
                hasFoundExistingId = false;
                break;
            }
            idCounter++;
        }
        const compToAdd = {
            id: ObjectID.generate().toString(), // react-flow id
            type: "node",
            position: { x: 200, y: 200 },
            selected: true,
            data: {
                nodeType: NodeType.Node,
                id: `${metaDataId}-${idCounter}`,
                error: "",
                metadata: Object.assign({}, componentMetadata),
                metadataId: componentMetadata.id,
                internalId: ObjectID.generate().toString(), // runner id
                componentType: componentMetadata.componentType,
            },
        };
        if (componentMetadata.componentType === ComponentType.Trigger) {
            // remove the placeholder trigger element from graph.
            setNodes((nds) => {
                return nds
                    .filter((node) => {
                    return node.data.componentType === ComponentType.Component;
                })
                    .map((n) => {
                    return Object.assign(Object.assign({}, n), { selected: false });
                })
                    .concat(Object.assign({}, compToAdd));
            });
        }
        else {
            setNodes((nds) => {
                return nds
                    .map((n) => {
                    return Object.assign(Object.assign({}, n), { selected: false });
                })
                    .concat(Object.assign({}, compToAdd));
            });
        }
    };
    return (React.createElement("div", { style: {
            height: "calc(100vh - 220px)",
            minHeight: "600px",
            borderRadius: "8px",
            overflow: "hidden",
            border: "1px solid #e2e8f0",
        } },
        React.createElement("style", null, `
          .react-flow__minimap {
            border-radius: 8px !important;
            border: 1px solid #e2e8f0 !important;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.07) !important;
            overflow: hidden !important;
            background: #ffffff !important;
          }
          .react-flow__controls {
            border-radius: 8px !important;
            border: 1px solid #e2e8f0 !important;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.07) !important;
            overflow: hidden !important;
          }
          .react-flow__controls-button {
            border-bottom: 1px solid #f1f5f9 !important;
            width: 32px !important;
            height: 32px !important;
          }
          .react-flow__controls-button:hover {
            background: #f8fafc !important;
          }
          .react-flow__controls-button svg {
            max-width: 14px !important;
            max-height: 14px !important;
          }
          .react-flow__edge:hover .react-flow__edge-path {
            stroke: #6366f1 !important;
            stroke-width: 2.5px !important;
          }
          .react-flow__handle:hover {
            transform: scale(1.3) !important;
          }
          .react-flow__connection-line {
            stroke: #6366f1 !important;
            stroke-width: 2px !important;
            stroke-dasharray: 5 5 !important;
          }
          @keyframes flow-dash {
            to {
              stroke-dashoffset: -10;
            }
          }
          .react-flow__edge.animated .react-flow__edge-path {
            animation: flow-dash 0.5s linear infinite !important;
            stroke-dasharray: 5 5 !important;
          }
        `),
        React.createElement(ReactFlow, { nodes: nodes, edges: edges, fitView: true, onEdgeClick: () => {
                refreshEdges();
            }, onNodeClick: () => {
                refreshEdges();
            }, proOptions: proOptions, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, onConnect: onConnect, multiSelectionKeyCode: null, onEdgeUpdate: onEdgeUpdate, nodeTypes: nodeTypes, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, snapToGrid: true, snapGrid: [16, 16], connectionLineStyle: {
                stroke: "#6366f1",
                strokeWidth: 2,
                strokeDasharray: "5 5",
            }, defaultEdgeOptions: {
                type: "smoothstep",
                style: Object.assign({}, edgeStyle),
            } },
            React.createElement(MiniMap, { nodeStrokeWidth: 3, nodeColor: (node) => {
                    if (node.data &&
                        node.data.metadata &&
                        node.data.metadata.componentType === ComponentType.Trigger) {
                        return "#f59e0b";
                    }
                    return "#6366f1";
                }, maskColor: "rgba(241, 245, 249, 0.7)", style: {
                    backgroundColor: "#ffffff",
                } }),
            React.createElement(Controls, null),
            React.createElement(Background, { variant: BackgroundVariant.Dots, gap: 20, size: 1, color: "#cbd5e1" })),
        showComponentsModal && (React.createElement(ComponentsModal, { componentsType: ComponentType.Component, onCloseModal: () => {
                setShowComponentsModal(false);
            }, categories: allComponentCategories, components: allComponentMetadata.filter((comp) => {
                return comp.componentType === ComponentType.Component;
            }), onComponentClick: (component) => {
                setShowComponentsModal(false);
                addToGraph(component);
            } })),
        showTriggersModal && (React.createElement(ComponentsModal, { componentsType: ComponentType.Trigger, onCloseModal: () => {
                setShowTriggersModal(false);
            }, categories: allComponentCategories, components: allComponentMetadata.filter((comp) => {
                return comp.componentType === ComponentType.Trigger;
            }), onComponentClick: (component) => {
                setShowTriggersModal(false);
                addToGraph(component);
            } })),
        showComponentSettingsModal && selectedNodeData && (React.createElement(ComponentSettingsModal, { graphComponents: nodes.map((node) => {
                return node.data;
            }), workflowId: props.workflowId, webhookSecretKey: props.webhookSecretKey, component: selectedNodeData, title: selectedNodeData && selectedNodeData.metadata.title
                ? selectedNodeData.metadata.title
                : "Component Properties", onDelete: (component) => {
                deleteNode(component.id);
            }, description: selectedNodeData && selectedNodeData.metadata.description
                ? selectedNodeData.metadata.description
                : "Edit Component Properties and variables here.", onClose: () => {
                setShowComponentSettingsModal(false);
            }, onSave: (componentData) => {
                // Update the node.
                setNodes((nds) => {
                    return nds.map((n) => {
                        if (n.data.internalId === componentData.internalId) {
                            n.data = componentData;
                        }
                        return n;
                    });
                });
                setShowComponentSettingsModal(false);
            } })),
        showRunModal && (React.createElement(RunModal, { trigger: (nodes.find((i) => {
                return i.data.metadata.componentType === ComponentType.Trigger;
            }) || getPlaceholderTriggerNode()).data, onClose: () => {
                setShowRunModal(false);
            }, onRun: (trigger) => {
                props.onRun(trigger);
            } }))));
};
export default Workflow;
//# sourceMappingURL=Workflow.js.map