/**
 * Workflow Actions
 *
 * Actions for managing workflow status and checkpoint state.
 */

import type { WorkflowState, WorkflowStatus, WorkflowView, LoopState, ChainedState, InputState, TriggeredAgentState, ControllerState,
  AutonomousMode, AgentTelemetry, AgentStatus } from "../types"
import { debug } from "../../../../../../../shared/logging/logger.js"
import { updateControllerTelemetry as updateControllerTelemetryUtil } from "../../../state/utils"

export type WorkflowActionsContext = {
  getState(): WorkflowState
  setState(state: WorkflowState): void
  notify(): void
}

export function createWorkflowActions(ctx: WorkflowActionsContext) {
  function setWorkflowName(name: string): void {
    const state = ctx.getState()
    if (state.workflowName === name) return
    ctx.setState({ ...state, workflowName: name })
    ctx.notify()
  }

  function setWorkflowStatus(status: WorkflowStatus): void {
    const state = ctx.getState()
    if (state.workflowStatus === status) return
    if (status === "completed" || status === "stopped" || status === "stopping") {
      ctx.setState({ ...state, endTime: state.endTime ?? Date.now(), workflowStatus: status })
    } else {
      ctx.setState({ ...state, workflowStatus: status })
    }
    ctx.notify()
  }

  function setCheckpointState(checkpoint: { active: boolean; reason?: string } | null): void {
    const state = ctx.getState()
    ctx.setState({ ...state, checkpointState: checkpoint })
    if (checkpoint && checkpoint.active) {
      setWorkflowStatus("awaiting")
    } else {
      setWorkflowStatus("running")
    }
    ctx.notify()
  }

  function setInputState(inputState: InputState | null): void {
    const state = ctx.getState()
    const prevQueue = state.inputState?.queuedPrompts
    const prevIndex = state.inputState?.currentIndex

    // Persist queue from previous state if new state doesn't have one
    let mergedState = inputState
    if (inputState && !inputState.queuedPrompts && prevQueue && prevQueue.length > 0) {
      mergedState = { ...inputState, queuedPrompts: prevQueue, currentIndex: prevIndex }
    } else if (!inputState && prevQueue && prevQueue.length > 0) {
      // Even when clearing, preserve queue info for UI display
      mergedState = { active: false, queuedPrompts: prevQueue, currentIndex: prevIndex }
    }

    ctx.setState({ ...state, inputState: mergedState })
    if (mergedState && mergedState.active) {
      // Only show "Paused" for manual pause (no queue), not for chained prompts
      const hasQueue = mergedState.queuedPrompts && mergedState.queuedPrompts.length > 0
      if (!hasQueue) {
        setWorkflowStatus("paused")
      }
      // With queue (chained prompts), keep current status
    } else {
      setWorkflowStatus("running")
    }
    ctx.notify()
  }

  /** @deprecated Use setInputState instead */
  function setChainedState(chainedState: ChainedState | null): void {
    const state = ctx.getState()
    ctx.setState({ ...state, chainedState })
    if (chainedState && chainedState.active) {
      setWorkflowStatus("paused")
    } else {
      setWorkflowStatus("running")
    }
    ctx.notify()
  }

  function setLoopState(loopState: LoopState | null): void {
    let state = ctx.getState()
    state = { ...state, loopState }
    if (loopState && loopState.active) {
      state = {
        ...state,
        agents: state.agents.map((agent) =>
          agent.id === loopState.sourceAgent
            ? { ...agent, loopRound: loopState.iteration, loopReason: loopState.reason }
            : agent,
        ),
      }
    }
    ctx.setState(state)
    ctx.notify()
  }

  function clearLoopRound(agentId: string): void {
    const state = ctx.getState()
    ctx.setState({
      ...state,
      agents: state.agents.map((agent) =>
        agent.id === agentId ? { ...agent, loopRound: undefined, loopReason: undefined } : agent,
      ),
    })
    ctx.notify()
  }

  function addTriggeredAgent(sourceAgentId: string, triggeredAgent: TriggeredAgentState): void {
    const state = ctx.getState()
    ctx.setState({
      ...state,
      triggeredAgents: [...state.triggeredAgents, triggeredAgent],
    })
    ctx.notify()
  }

  function addSeparator(separator: { id: string; text: string; stepIndex: number }): void {
    const state = ctx.getState()
    const exists = state.separators.some((s) => s.id === separator.id || s.stepIndex === separator.stepIndex)
    if (exists) return

    ctx.setState({
      ...state,
      separators: [...state.separators, separator],
    })
    ctx.notify()
  }

  function logMessage(agentId: string, message: string): void {
    const state = ctx.getState()
    const newLogs = new Map(state.agentLogs)
    const agentMessages = newLogs.get(agentId) || []
    newLogs.set(agentId, [...agentMessages, message])
    ctx.setState({ ...state, agentLogs: newLogs })
    ctx.notify()
  }

  function setAutonomousMode(enabled: AutonomousMode): void {
    const state = ctx.getState()
    if (state.autonomousMode === enabled) return
    ctx.setState({ ...state, autonomousMode: enabled })
    ctx.notify()
  }

  function setControllerState(controllerState: ControllerState | null): void {
    const state = ctx.getState()
    ctx.setState({ ...state, controllerState })
    ctx.notify()
  }

  function updateControllerTelemetry(telemetry: Partial<AgentTelemetry>): void {
    const state = ctx.getState()
    if (!state.controllerState) return

    ctx.setState({
      ...state,
      controllerState: updateControllerTelemetryUtil(state.controllerState, telemetry),
    })
    ctx.notify()
  }

  function updateControllerStatus(status: AgentStatus): void {
    const state = ctx.getState()

    // Create partial controllerState if it doesn't exist yet
    // (status event can arrive before controller:info)
    if (!state.controllerState) {
      ctx.setState({
        ...state,
        controllerState: {
          id: '',
          name: '',
          engine: '',
          status,
          telemetry: { tokensIn: 0, tokensOut: 0 },
        },
      })
    } else {
      if (state.controllerState.status === status) return
      ctx.setState({
        ...state,
        controllerState: {
          ...state.controllerState,
          status,
        },
      })
    }
    ctx.notify()
  }

  function updateControllerMonitoring(monitoringId: number): void {
    const state = ctx.getState()

    // Create partial controllerState if it doesn't exist yet
    // (monitoring event can arrive before controller:info)
    if (!state.controllerState) {
      ctx.setState({
        ...state,
        controllerState: {
          id: '',
          name: '',
          engine: '',
          monitoringId,
          telemetry: { tokensIn: 0, tokensOut: 0 },
        },
      })
    } else {
      ctx.setState({
        ...state,
        controllerState: {
          ...state.controllerState,
          monitoringId,
        },
      })
    }
    ctx.notify()
  }

  function setWorkflowView(view: WorkflowView): void {
    const state = ctx.getState()
    if (state.view === view) return
    ctx.setState({ ...state, view })
    ctx.notify()
  }

  return {
    setWorkflowName,
    setWorkflowStatus,
    setWorkflowView,
    setCheckpointState,
    setInputState,
    setChainedState,
    setLoopState,
    clearLoopRound,
    addTriggeredAgent,
    addSeparator,
    logMessage,
    setAutonomousMode,
    setControllerState,
    updateControllerTelemetry,
    updateControllerStatus,
    updateControllerMonitoring,
  }
}
