import type {
  Connection,
  Edge,
  EdgeChange,
  Node,
  NodeChange,
  OnNodesChange,
  OnEdgesChange,
  OnConnect,
} from 'reactflow'
import { addEdge, applyNodeChanges, applyEdgeChanges } from 'reactflow'
import { create } from 'zustand'

import type {
  SyntheticFlowParameter,
  FlowParameter,
  IFlow,
} from '../../generated/ManagingApi'

export type IWorkflowStore = {
  nodes: Node<IFlow>[]
  initialNodes: Node<IFlow>[]
  edges: Edge[]
  initialEdges: Edge[]
  onNodesChange: OnNodesChange
  onEdgesChange: OnEdgesChange
  onConnect: OnConnect
  updateNodeData: (nodeId: string, parameterName: string, value: string) => void
  initWorkFlow: (nodes: Node<IFlow>[], edges: Edge[]) => void
  setNodes: (nodes: Node<IFlow>[]) => void
  resetWorkflow: () => void
}

// this is our useStore hook that we can use in our components to get parts of the store and call actions
const useWorkflowStore = create<IWorkflowStore>((set, get) => ({
  edges: [],
  initWorkFlow: (nodes: Node<IFlow>[], edges: Edge[]) => {
    set({
      edges: edges,
      initialEdges: edges,
      initialNodes: nodes,
      nodes: nodes,
    })
  },
  initialEdges: [],
  initialNodes: [],
  nodes: [],
  onConnect: (connection: Connection, callback: void) => {
    set({
      edges: addEdge(connection, get().edges),
    })
  },
  onEdgesChange: (changes: EdgeChange[]) => {
    set({
      edges: applyEdgeChanges(changes, get().edges),
    })
  },
  onNodesChange: (changes: NodeChange[]) => {
    set({
      nodes: applyNodeChanges(changes, get().nodes),
    })
  },
  resetWorkflow: () => {
    set({
      edges: get().initialEdges,
      initialEdges: get().initialEdges,
      initialNodes: get().initialNodes,
      nodes: get().initialNodes,
    })
  },
  setNodes: (nodes: Node<IFlow>[]) => {
    set({
      nodes: nodes,
    })
  },
  updateNodeData: (nodeId: string, parameterName: string, value: string) => {
    set({
      nodes: get().nodes.map((node) => {
        if (node.id === nodeId) {
          node.data.parameters = updateParameters(
            node.data.parameters,
            parameterName,
            value
          )
        }
        return node
      }),
    })
  },
}))

const updateParameters = (
  parameters: FlowParameter[],
  name: string,
  value: string
) => {
  if (!parameters.find((parameter) => parameter.name === name)) {
    parameters.push({
      name: name,
      value: value,
    } as SyntheticFlowParameter)
  } else {
    parameters = parameters.map((parameter) => {
      if (parameter.name === name) {
        parameter.value = value
      }
      return parameter
    })
  }
  return parameters
}

export default useWorkflowStore
