/**
 * Workflow Pinia store
 */

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { workflowApi } from '@/api/workflow'
import type {
  WorkflowDefinition,
  WorkflowSummary,
  WorkflowExecution,
} from '@/utils/workflowSerializer'
import type { Node, Edge } from '@vue-flow/core'
import { flowToWorkflow, workflowToFlow, getDefaultNodeData, type WorkflowNodeData } from '@/utils/workflowSerializer'

// --- Command Pattern ---

interface Command {
  execute(): void
  undo(): void
  description: string
}

export const useWorkflowStore = defineStore('workflow', () => {
  // --- State ---
  const workflows = ref<WorkflowSummary[]>([])
  const currentWorkflow = ref<WorkflowDefinition | null>(null)
  const currentExecution = ref<WorkflowExecution | null>(null)
  const isExecuting = ref(false)
  const isLoading = ref(false)
  const error = ref<string | null>(null)

  // Vue Flow state — use any[] to avoid @vue-flow/core deep type recursion with Vue 3.5+
  const flowNodes = ref<any[]>([])
  const flowEdges = ref<any[]>([])
  const selectedNodeId = ref<string | null>(null)

  // Undo/Redo state
  const undoStack = ref<Command[]>([])
  const redoStack = ref<Command[]>([])

  // WebSocket state
  let wsConnection: WebSocket | null = null
  let wsReconnectCount = 0
  const WS_MAX_RECONNECT = 3
  let currentExecutionId: string | null = null
  let wsReconnectTimer: ReturnType<typeof setTimeout> | null = null

  // Execution history state
  const executionHistory = ref<WorkflowExecution[]>([])
  const executionHistoryTotal = ref(0)

  // --- Getters ---
  const selectedNode = computed<any | null>(() => {
    if (!selectedNodeId.value) return null
    return flowNodes.value.find((n: any) => n.id === selectedNodeId.value) || null
  })

  const selectedNodeData = computed<WorkflowNodeData | null>(() => {
    return selectedNode.value?.data || null
  })

  const canUndo = computed(() => undoStack.value.length > 0)
  const canRedo = computed(() => redoStack.value.length > 0)

  // --- Internal mutation methods (no command tracking) ---

  function _addNodeDirect(node: any): void {
    flowNodes.value.push(node)
  }

  function _removeNodeDirect(nodeId: string): { node: any; edges: any[] } | null {
    const node = flowNodes.value.find((n: any) => n.id === nodeId)
    if (!node) return null
    const removedEdges = flowEdges.value.filter(
      (e) => e.source === nodeId || e.target === nodeId
    )
    flowNodes.value = flowNodes.value.filter((n) => n.id !== nodeId)
    flowEdges.value = flowEdges.value.filter(
      (e) => e.source !== nodeId && e.target !== nodeId
    )
    if (selectedNodeId.value === nodeId) {
      selectedNodeId.value = null
    }
    return { node, edges: removedEdges }
  }

  function _addEdgeDirect(edge: Edge): void {
    flowEdges.value = [...flowEdges.value, edge]
  }

  function _removeEdgeDirect(edgeId: string): Edge | null {
    const edge = flowEdges.value.find((e) => e.id === edgeId)
    if (!edge) return null
    flowEdges.value = flowEdges.value.filter((e) => e.id !== edgeId)
    return edge
  }

  function _updateNodeDataDirect(nodeId: string, data: Partial<WorkflowNodeData>): void {
    const index = flowNodes.value.findIndex((n: any) => n.id === nodeId)
    if (index !== -1) {
      const existing = flowNodes.value[index]
      flowNodes.value[index] = {
        ...existing,
        data: { ...existing.data, ...data },
      }
    }
  }

  // --- Command execution ---

  function executeCommand(command: Command): void {
    command.execute()
    undoStack.value.push(command)
    redoStack.value = []
  }

  function undo(): void {
    const command = undoStack.value.pop()
    if (!command) return
    command.undo()
    redoStack.value.push(command)
  }

  function redo(): void {
    const command = redoStack.value.pop()
    if (!command) return
    command.execute()
    undoStack.value.push(command)
  }

  function clearUndoRedo(): void {
    undoStack.value = []
    redoStack.value = []
  }

  // --- WebSocket execution tracking ---

  /** Update a node's execution status on the canvas */
  function updateNodeStatus(stageId: string, status: string): void {
    const node = flowNodes.value.find(
      (n) => n.data?.stage_id === stageId || n.id === stageId || n.data?.label === stageId
    )
    if (node && node.data) {
      node.data = { ...node.data, status }
    }
  }

  /** Connect WebSocket for real-time execution updates */
  function connectExecutionWebSocket(executionId: string): void {
    currentExecutionId = executionId
    wsReconnectCount = 0
    _openWebSocket()
  }

  function _openWebSocket(): void {
    if (!currentExecutionId) return
    _cancelPendingReconnect()
    if (wsConnection) {
      wsConnection.onclose = null
      wsConnection.close()
      wsConnection = null
    }
    wsReconnectCount++

    try {
      wsConnection = workflowApi.createWorkflowWebSocket()

      wsConnection.onopen = () => {
        wsReconnectCount = 0
        // Re-subscribe to execution after reconnect
        if (currentExecutionId) {
          wsConnection?.send(JSON.stringify({
            type: 'subscribe',
            execution_id: currentExecutionId,
          }))
        }
      }

      wsConnection.onmessage = (event: MessageEvent) => {
        try {
          const data = JSON.parse(event.data)
          if (data.execution_id && data.execution_id !== currentExecutionId) return

          switch (data.type) {
            case 'stage_started':
              updateNodeStatus(data.stage_id, 'running')
              break
            case 'stage_completed':
              updateNodeStatus(data.stage_id, 'completed')
              break
            case 'stage_failed':
              updateNodeStatus(data.stage_id, 'failed')
              break
            case 'approval_required':
              updateNodeStatus(data.stage_id, 'waiting_approval')
              break
            case 'execution_completed':
            case 'execution_failed':
              isExecuting.value = false
              disconnectExecutionWebSocket()
              // Fetch final execution state
              if (currentExecutionId) {
                workflowApi.getExecution(currentExecutionId).then((execution) => {
                  currentExecution.value = execution
                }).catch(() => {})
              }
              break
          }
        } catch {
          // ignore malformed messages
        }
      }

      wsConnection.onclose = () => {
        wsConnection = null
        if (isExecuting.value && wsReconnectCount < WS_MAX_RECONNECT) {
          wsReconnectTimer = setTimeout(() => _openWebSocket(), 2000)
        }
      }

      wsConnection.onerror = () => {
        // onclose will fire after onerror, reconnect logic is handled there
      }
    } catch {
      if (wsReconnectCount < WS_MAX_RECONNECT) {
        wsReconnectTimer = setTimeout(() => _openWebSocket(), 2000)
      }
    }
  }

  function _cancelPendingReconnect(): void {
    if (wsReconnectTimer) {
      clearTimeout(wsReconnectTimer)
      wsReconnectTimer = null
    }
  }

  /** Disconnect the execution WebSocket */
  function disconnectExecutionWebSocket(): void {
    _cancelPendingReconnect()
    if (wsConnection) {
      wsConnection.onclose = null // prevent reconnect on intentional close
      wsConnection.close()
      wsConnection = null
    }
    currentExecutionId = null
  }

  /** Reset all node statuses on the canvas */
  function resetNodeStatuses(): void {
    flowNodes.value = flowNodes.value.map((n) => {
      if (n.data?.status) {
        const { status: _, ...rest } = n.data
        return { ...n, data: rest as WorkflowNodeData }
      }
      return n
    })
  }

  // --- Actions ---

  /** Load all workflows from server */
  async function loadWorkflows(): Promise<void> {
    isLoading.value = true
    error.value = null
    try {
      const response = await workflowApi.listWorkflows()
      workflows.value = response.workflows
    } catch (err) {
      error.value = err instanceof Error ? err.message : '加载工作流列表失败'
      console.error('Failed to load workflows:', err)
    } finally {
      isLoading.value = false
    }
  }

  /** Create a new workflow */
  async function createWorkflow(name: string): Promise<WorkflowDefinition | null> {
    isLoading.value = true
    error.value = null
    try {
      const workflow = await workflowApi.createWorkflow({
        name,
        stages: [],
      })
      currentWorkflow.value = workflow
      flowNodes.value = []
      flowEdges.value = []
      selectedNodeId.value = null
      clearUndoRedo()
      await loadWorkflows()
      return workflow
    } catch (err) {
      error.value = err instanceof Error ? err.message : '创建工作流失败'
      console.error('Failed to create workflow:', err)
      return null
    } finally {
      isLoading.value = false
    }
  }

  /** Load a workflow for editing */
  async function loadWorkflow(workflowId: string): Promise<void> {
    isLoading.value = true
    error.value = null
    try {
      const workflow = await workflowApi.getWorkflow(workflowId)
      currentWorkflow.value = workflow
      const { nodes, edges } = workflowToFlow(workflow)
      flowNodes.value = nodes
      flowEdges.value = edges
      selectedNodeId.value = null
      clearUndoRedo()
    } catch (err) {
      error.value = err instanceof Error ? err.message : '加载工作流失败'
      console.error('Failed to load workflow:', err)
    } finally {
      isLoading.value = false
    }
  }

  /** Save current workflow to server */
  async function saveWorkflow(): Promise<boolean> {
    if (!currentWorkflow.value) return false
    isLoading.value = true
    error.value = null
    try {
      const definition = flowToWorkflow(
        flowNodes.value,
        flowEdges.value,
        currentWorkflow.value.name,
        currentWorkflow.value.workflow_id,
      )
      const saved = await workflowApi.updateWorkflow(
        currentWorkflow.value.workflow_id,
        {
          name: definition.name,
          stages: definition.stages,
          triggers: definition.triggers,
          variables_schema: definition.variables_schema,
          output_schema: definition.output_schema,
        }
      )
      currentWorkflow.value = saved
      await loadWorkflows()
      return true
    } catch (err) {
      error.value = err instanceof Error ? err.message : '保存工作流失败'
      console.error('Failed to save workflow:', err)
      return false
    } finally {
      isLoading.value = false
    }
  }

  /** Delete a workflow */
  async function deleteWorkflow(workflowId: string): Promise<boolean> {
    isLoading.value = true
    error.value = null
    try {
      await workflowApi.deleteWorkflow(workflowId)
      if (currentWorkflow.value?.workflow_id === workflowId) {
        currentWorkflow.value = null
        flowNodes.value = []
        flowEdges.value = []
        selectedNodeId.value = null
        clearUndoRedo()
      }
      await loadWorkflows()
      return true
    } catch (err) {
      error.value = err instanceof Error ? err.message : '删除工作流失败'
      console.error('Failed to delete workflow:', err)
      return false
    } finally {
      isLoading.value = false
    }
  }

  /** Execute current workflow */
  async function executeWorkflow(variables?: Record<string, unknown>): Promise<void> {
    if (!currentWorkflow.value) return
    isExecuting.value = true
    error.value = null
    resetNodeStatuses()
    try {
      const result = await workflowApi.executeWorkflow(
        currentWorkflow.value.workflow_id,
        variables
      )
      // Connect WebSocket for real-time status updates
      connectExecutionWebSocket(result.execution_id)
      // Fetch initial execution state
      const execution = await workflowApi.getExecution(result.execution_id)
      currentExecution.value = execution
    } catch (err) {
      error.value = err instanceof Error ? err.message : '执行工作流失败'
      console.error('Failed to execute workflow:', err)
      isExecuting.value = false
    }
  }

  /** Approve a paused execution */
  async function approveExecution(
    executionId: string,
    approved: boolean,
    comment?: string
  ): Promise<void> {
    try {
      const execution = await workflowApi.approveExecution(executionId, approved, comment)
      currentExecution.value = execution
    } catch (err) {
      error.value = err instanceof Error ? err.message : '审批操作失败'
      console.error('Failed to approve execution:', err)
    }
  }

  /** Cancel an execution */
  async function cancelExecution(executionId: string): Promise<void> {
    try {
      const execution = await workflowApi.cancelExecution(executionId)
      currentExecution.value = execution
      isExecuting.value = false
      disconnectExecutionWebSocket()
    } catch (err) {
      error.value = err instanceof Error ? err.message : '取消执行失败'
      console.error('Failed to cancel execution:', err)
    }
  }

  /** Load execution history for current workflow */
  async function loadExecutionHistory(limit: number = 50, offset: number = 0): Promise<void> {
    if (!currentWorkflow.value) return
    try {
      const response = await workflowApi.listExecutions(
        currentWorkflow.value.workflow_id,
        limit,
        offset
      )
      executionHistory.value = response.executions
      executionHistoryTotal.value = response.total
    } catch (err) {
      console.error('Failed to load execution history:', err)
    }
  }

  /** Add a node to the flow canvas (with undo support) */
  function addNode(nodeType: string, position: { x: number; y: number }): void {
    const id = `node-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`
    const data = getDefaultNodeData(nodeType)
    const typeMap: Record<string, string> = {
      skill: 'skill',
      condition: 'condition',
      approval: 'approval',
      parallel: 'parallel',
    }
    const node: Node<WorkflowNodeData> = {
      id,
      type: typeMap[nodeType] || 'skill',
      position,
      data,
    }

    const command: Command = {
      execute: () => _addNodeDirect(node),
      undo: () => _removeNodeDirect(node.id),
      description: `添加节点 ${data.label}`,
    }

    executeCommand(command)
  }

  /** Update node data (with undo support) */
  function updateNodeData(nodeId: string, data: Partial<WorkflowNodeData>): void {
    const index = flowNodes.value.findIndex((n) => n.id === nodeId)
    if (index === -1) return

    const currentNode = flowNodes.value[index]
    const currentData = currentNode.data!
    const oldData: Record<string, unknown> = {}
    for (const key of Object.keys(data)) {
      oldData[key] = currentData[key as keyof WorkflowNodeData]
    }

    const command: Command = {
      execute: () => _updateNodeDataDirect(nodeId, data),
      undo: () => _updateNodeDataDirect(nodeId, oldData as Partial<WorkflowNodeData>),
      description: `更新节点属性`,
    }

    executeCommand(command)
  }

  /** Remove a node (with undo support) */
  function removeNode(nodeId: string): void {
    const node = flowNodes.value.find((n) => n.id === nodeId)
    if (!node) return
    const edges = flowEdges.value.filter(
      (e) => e.source === nodeId || e.target === nodeId
    )

    const command: Command = {
      execute: () => {
        flowNodes.value = flowNodes.value.filter((n) => n.id !== nodeId)
        flowEdges.value = flowEdges.value.filter(
          (e) => e.source !== nodeId && e.target !== nodeId
        )
        if (selectedNodeId.value === nodeId) {
          selectedNodeId.value = null
        }
      },
      undo: () => {
        flowNodes.value = [...flowNodes.value, node]
        flowEdges.value = [...flowEdges.value, ...edges]
      },
      description: `删除节点 ${node.data?.label || nodeId}`,
    }

    executeCommand(command)
  }

  /** Add an edge (with undo support) */
  function addEdge(edge: Edge): void {
    const command: Command = {
      execute: () => _addEdgeDirect(edge),
      undo: () => _removeEdgeDirect(edge.id),
      description: `添加连线`,
    }

    executeCommand(command)
  }

  /** Clear the canvas */
  function clearCanvas(): void {
    flowNodes.value = []
    flowEdges.value = []
    selectedNodeId.value = null
    clearUndoRedo()
  }

  /** Select a node */
  function selectNode(nodeId: string | null): void {
    selectedNodeId.value = nodeId
  }

  return {
    // State
    workflows,
    currentWorkflow,
    currentExecution,
    isExecuting,
    isLoading,
    error,
    flowNodes,
    flowEdges,
    selectedNodeId,
    undoStack,
    redoStack,
    executionHistory,
    executionHistoryTotal,
    // Getters
    selectedNode,
    selectedNodeData,
    canUndo,
    canRedo,
    // Actions
    loadWorkflows,
    createWorkflow,
    loadWorkflow,
    saveWorkflow,
    deleteWorkflow,
    executeWorkflow,
    approveExecution,
    cancelExecution,
    loadExecutionHistory,
    addNode,
    updateNodeData,
    removeNode,
    addEdge,
    clearCanvas,
    selectNode,
    undo,
    redo,
    executeCommand,
    clearUndoRedo,
    updateNodeStatus,
    connectExecutionWebSocket,
    disconnectExecutionWebSocket,
    resetNodeStatuses,
  }
})
