import { describe, it, expect, vi } from 'vitest'
import { Workflow } from '../workflow'
import type { WorkflowDefinition, WorkflowConfig, WorkflowStep, NextConfig } from '../types'
import { StepType } from '../types'
import type { Contract, ExecutionContext } from '../../base/types'
import { z } from 'zod'
import { WorkflowStepError, WorkflowValidationError } from '../errors'

describe('Workflow Execution', () => {
  // Helper: Create mock execution context
  const createMockContext = (): ExecutionContext => ({
    executionId: 'exec-123',
    organizationId: 'org-123',
    userId: 'user-123',
    logger: {
      info: vi.fn(),
      error: vi.fn(),
      warn: vi.fn(),
      debug: vi.fn()
    }
  })

  // Helper: Create step with handler
  const createStep = <T, R>(
    id: string,
    description: string,
    handler: (input: T, context: ExecutionContext) => Promise<R> | R,
    next: NextConfig = null
  ): WorkflowStep => ({
    id,
    name: `Step ${id}`,
    description,
    handler,
    inputSchema: z.unknown(),
    outputSchema: z.unknown(),
    next
  })

  // Helper: Create minimal workflow definition
  const createWorkflowDefinition = (steps: WorkflowStep[], entryPoint: string): WorkflowDefinition => {
    const config: WorkflowConfig = {
      type: 'workflow',
      resourceId: 'test-workflow',
      name: 'Test Workflow',
      description: 'Workflow for testing',
      environment: 'dev'
    }

    const contract: Contract = {
      inputSchema: z.object({ value: z.number() }),
      outputSchema: z.object({ result: z.number() })
    }

    const stepsRecord: Record<string, WorkflowStep> = {}
    steps.forEach((step) => { stepsRecord[step.id] = step })

    return {
      config,
      contract,
      steps: stepsRecord,
      entryPoint
    }
  }

  describe('Constructor Validation', () => {
    it('creates workflow instance from definition', () => {
      const step1 = createStep('step1', 'Terminal step', async (input) => input)
      const definition = createWorkflowDefinition([step1], 'step1')

      const workflow = new Workflow(definition)

      expect(workflow.config).toEqual(definition.config)
      expect(workflow.contract).toEqual(definition.contract)
      expect(workflow.steps).toEqual(definition.steps)
      expect(workflow.entryPoint).toBe('step1')
    })

    it('validates entry point exists', () => {
      const step1 = createStep('step1', 'Step 1', async (input) => input)
      const definition = createWorkflowDefinition([step1], 'nonexistent')

      expect(() => new Workflow(definition)).toThrow(WorkflowValidationError)
      expect(() => new Workflow(definition)).toThrow(/Entry point.*not found/)
    })

    it('validates at least one terminal step exists', () => {
      const step1 = createStep('step1', 'Step 1', async (input) => input, {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const definition = createWorkflowDefinition([step1], 'step1')

      expect(() => new Workflow(definition)).toThrow(WorkflowValidationError)
    })

    it('validates step references point to existing steps', () => {
      const step1 = createStep('step1', 'Step 1', async (input) => input, {
        type: StepType.LINEAR,
        target: 'missing'
      })
      const definition = createWorkflowDefinition([step1], 'step1')

      // This will actually fail on "no terminal steps" first, since step1 points to 'missing' (not null)
      expect(() => new Workflow(definition)).toThrow(WorkflowValidationError)
    })

    it('allows valid workflow with multiple steps', () => {
      const step1 = createStep('step1', 'Step 1', async (input) => input, {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', async (input) => input)
      const definition = createWorkflowDefinition([step1, step2], 'step1')

      expect(() => new Workflow(definition)).not.toThrow()
    })
  })

  describe('Linear Execution', () => {
    it('executes single step workflow', async () => {
      const handler = vi.fn(async (input: { value: number }) => ({ result: input.value * 2 }))
      const step1 = createStep('step1', 'Multiply by 2', handler)
      const definition = createWorkflowDefinition([step1], 'step1')

      const workflow = new Workflow(definition)
      const context = createMockContext()

      const output = await workflow.execute({ value: 5 }, context)

      expect(handler).toHaveBeenCalledWith({ value: 5 }, context)
      expect(output).toEqual({ result: 10 })
    })

    it('executes steps in sequence (linear flow)', async () => {
      const executionOrder: string[] = []

      const step1 = createStep(
        'step1',
        'Add 1',
        async (input: { value: number }) => {
          executionOrder.push('step1')
          return { value: input.value + 1 }
        },
        { type: StepType.LINEAR, target: 'step2' }
      )

      const step2 = createStep(
        'step2',
        'Multiply by 2',
        async (input: { value: number }) => {
          executionOrder.push('step2')
          return { value: input.value * 2 }
        },
        { type: StepType.LINEAR, target: 'step3' }
      )

      const step3 = createStep(
        'step3',
        'Return result',
        async (input: { value: number }) => {
          executionOrder.push('step3')
          return { result: input.value }
        }
      )

      const definition = createWorkflowDefinition([step1, step2, step3], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      const output = await workflow.execute({ value: 5 }, context)

      expect(executionOrder).toEqual(['step1', 'step2', 'step3'])
      expect(output).toEqual({ result: 12 }) // (5 + 1) * 2 = 12
    })

    it('passes data between steps', async () => {
      const step1 = createStep(
        'step1',
        'Add 10',
        async (input: { value: number }) => ({ intermediate: input.value + 10 }),
        { type: StepType.LINEAR, target: 'step2' }
      )

      const step2Handler = vi.fn(async (input: { intermediate: number }) => ({
        result: input.intermediate * 3
      }))
      const step2 = createStep('step2', 'Multiply by 3', step2Handler)

      const definition = createWorkflowDefinition([step1, step2], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      const output = await workflow.execute({ value: 5 }, context)

      expect(step2Handler).toHaveBeenCalledWith({ intermediate: 15 }, context)
      expect(output).toEqual({ result: 45 })
    })

    it('validates input against contract schema', async () => {
      const step1 = createStep('step1', 'Return result', async (_input) => ({ result: 0 }))
      const definition = createWorkflowDefinition([step1], 'step1')

      const workflow = new Workflow(definition)
      const context = createMockContext()

      const invalidInput = { wrong: 'field' }

      await expect(workflow.execute(invalidInput as unknown, context)).rejects.toThrow()
    })

    it('validates step input schemas', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Step 1',
        description: 'Process value',
        handler: async (input: { value: number }) => ({ result: input.value }),
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ result: z.number() }),
        next: null
      }

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Valid input
      const output = await workflow.execute({ value: 10 }, context)
      expect(output).toEqual({ result: 10 })
    })

    it('validates step output schemas', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Step 1',
        description: 'Invalid output',
        handler: async () => ({ wrong: 'output' }), // Invalid output
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ data: z.number() }),
        next: null
      }

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 10 }, context)).rejects.toThrow(WorkflowStepError)
    })

    it('validates terminal step output against workflow contract', async () => {
      const step1 = createStep('step1', 'Invalid result', async () => ({ result: 'string instead of number' }))

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 10 }, context)).rejects.toThrow()
    })
  })

  describe('Conditional Branching', () => {
    it('executes conditional next step', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Decision',
        description: 'Route based on value',
        handler: async (input: { value: number }) => ({ value: input.value }),
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ value: z.number() }),
        next: {
          type: StepType.CONDITIONAL,
          routes: [
            {
              condition: (output: { value: number }) => output.value > 10,
              target: 'pathA'
            }
          ],
          default: 'pathB'
        }
      }

      const pathA = createStep('pathA', 'Path A', async () => ({ result: 100 }))
      const pathB = createStep('pathB', 'Path B', async () => ({ result: 200 }))

      const definition = createWorkflowDefinition([step1, pathA, pathB], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Test pathA (value > 10)
      const outputA = await workflow.execute({ value: 15 }, context)
      expect(outputA).toEqual({ result: 100 })

      // Test pathB (value <= 10)
      const outputB = await workflow.execute({ value: 5 }, context)
      expect(outputB).toEqual({ result: 200 })
    })

    it('handles complex branching logic', async () => {
      const executionPath: string[] = []

      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Router',
        description: 'Route by category',
        handler: async (input: { value: number }) => {
          executionPath.push('step1')
          return { value: input.value }
        },
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ value: z.number() }),
        next: {
          type: StepType.CONDITIONAL,
          routes: [
            {
              condition: (output: { value: number }) => output.value < 10,
              target: 'handleSmall'
            },
            {
              condition: (output: { value: number }) => output.value < 50,
              target: 'handleMedium'
            }
          ],
          default: 'handleLarge'
        }
      }

      const handleSmall = createStep('handleSmall', 'Handle small', async () => {
        executionPath.push('handleSmall')
        return { result: 1 }
      })

      const handleMedium = createStep('handleMedium', 'Handle medium', async () => {
        executionPath.push('handleMedium')
        return { result: 2 }
      })

      const handleLarge = createStep('handleLarge', 'Handle large', async () => {
        executionPath.push('handleLarge')
        return { result: 3 }
      })

      const definition = createWorkflowDefinition([step1, handleSmall, handleMedium, handleLarge], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Test small path
      executionPath.length = 0
      const small = await workflow.execute({ value: 5 }, context)
      expect(executionPath).toEqual(['step1', 'handleSmall'])
      expect(small).toEqual({ result: 1 })

      // Test medium path
      executionPath.length = 0
      const medium = await workflow.execute({ value: 25 }, context)
      expect(executionPath).toEqual(['step1', 'handleMedium'])
      expect(medium).toEqual({ result: 2 })

      // Test large path (default)
      executionPath.length = 0
      const large = await workflow.execute({ value: 100 }, context)
      expect(executionPath).toEqual(['step1', 'handleLarge'])
      expect(large).toEqual({ result: 3 })
    })
  })

  describe('Error Handling', () => {
    it('propagates errors from step handlers as WorkflowStepError', async () => {
      const step1 = createStep('step1', 'Failing step', async () => {
        throw new Error('Step handler failed')
      })

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(WorkflowStepError)
      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(/Step failed.*step1/)
    })

    it('includes step ID and name in error context', async () => {
      const step1: WorkflowStep = {
        id: 'processData',
        name: 'Data Processor',
        description: 'Process data',
        handler: async () => {
          throw new Error('Processing error')
        },
        inputSchema: z.unknown(),
        outputSchema: z.unknown(),
        next: null
      }

      const definition = createWorkflowDefinition([step1], 'processData')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      try {
        await workflow.execute({ value: 1 }, context)
        expect.unreachable('Should have thrown')
      } catch (error) {
        expect(error).toBeInstanceOf(WorkflowStepError)
        const stepError = error as WorkflowStepError
        expect(stepError.context).toHaveProperty('stepId', 'processData')
        expect(stepError.context).toHaveProperty('stepName', 'Data Processor')
        expect(stepError.context).toHaveProperty('workflowId', 'test-workflow')
      }
    })

    it('stops execution on step failure', async () => {
      const step1Handler = vi.fn(async () => {
        throw new Error('Failed')
      })
      const step2Handler = vi.fn(async () => ({ result: 1 }))

      const step1 = createStep('step1', 'Step 1', step1Handler, {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', step2Handler)

      const definition = createWorkflowDefinition([step1, step2], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()

      expect(step1Handler).toHaveBeenCalled()
      expect(step2Handler).not.toHaveBeenCalled()
    })

    it('handles validation errors in step input', async () => {
      const step1 = createStep('step1', 'Step 1', async () => ({ wrong: 'output' }), {
        type: StepType.LINEAR,
        target: 'step2'
      })

      const step2: WorkflowStep = {
        id: 'step2',
        name: 'Step 2',
        description: 'Requires string input',
        handler: async (input: { required: string }) => ({ result: input.required.length }),
        inputSchema: z.object({ required: z.string() }),
        outputSchema: z.object({ result: z.number() }),
        next: null
      }

      const definition = createWorkflowDefinition([step1, step2], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles null input gracefully', async () => {
      const step1 = createStep('step1', 'Step 1', async () => ({ result: 1 }))
      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute(null as unknown, context)).rejects.toThrow()
    })

    it('handles undefined input gracefully', async () => {
      const step1 = createStep('step1', 'Step 1', async () => ({ result: 1 }))
      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute(undefined as unknown, context)).rejects.toThrow()
    })

    it('handles step handler returning null', async () => {
      const step1 = createStep('step1', 'Returns null', async () => null, {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', async () => ({ result: 1 }))

      const definition = createWorkflowDefinition([step1, step2], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Workflow may pass null to next step which handles it gracefully
      // This tests that the workflow doesn't crash
      const output = await workflow.execute({ value: 1 }, context)
      expect(output).toBeDefined()
    })

    it('handles step handler returning undefined', async () => {
      const step1 = createStep('step1', 'Returns undefined', async () => undefined)

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles async errors in step handlers', async () => {
      const step1 = createStep('step1', 'Async error', async () => {
        await new Promise((resolve) => setTimeout(resolve, 10))
        throw new Error('Async operation failed')
      })

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(/Async operation failed/)
    })

    it('handles errors in conditional route evaluation', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Broken condition',
        description: 'Condition throws error',
        handler: async (input: { value: number }) => ({ value: input.value }),
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ value: z.number() }),
        next: {
          type: StepType.CONDITIONAL,
          routes: [
            {
              condition: (_output: unknown) => {
                throw new Error('Condition evaluation failed')
              },
              target: 'pathA'
            }
          ],
          default: 'pathB'
        }
      }

      const pathA = createStep('pathA', 'Path A', async () => ({ result: 1 }))
      const pathB = createStep('pathB', 'Path B', async () => ({ result: 2 }))

      const definition = createWorkflowDefinition([step1, pathA, pathB], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // If condition throws, workflow may fall back to default route
      // Test that it handles this gracefully (either throws or uses default)
      try {
        const output = await workflow.execute({ value: 1 }, context)
        // If it succeeds, should have used default path
        expect(output).toEqual({ result: 2 })
      } catch (error) {
        // Or it may throw the error - both are acceptable
        expect(error).toBeDefined()
      }
    })

    it('handles missing conditional default route when no condition matches', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'No default',
        description: 'No default route',
        handler: async (input: { value: number }) => ({ value: input.value }),
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ value: z.number() }),
        next: {
          type: StepType.CONDITIONAL,
          routes: [
            {
              condition: (output: unknown) => (output as { value: number }).value > 100,
              target: 'pathA'
            }
          ],
          default: 'pathTerminal' // Need a valid default
        }
      }

      const pathA = createStep('pathA', 'Path A', async () => ({ result: 1 }))
      const pathTerminal = createStep('pathTerminal', 'Terminal', async () => ({ result: 0 }))

      const definition = createWorkflowDefinition([step1, pathA, pathTerminal], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // When value <= 100, should use default route
      const output = await workflow.execute({ value: 50 }, context)
      expect(output).toEqual({ result: 0 })
    })

    it('handles step that violates output schema constraints', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Schema violation',
        description: 'Violates constraints',
        handler: async () => ({ count: -5 }), // Negative number
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({ count: z.number().positive() }), // Must be positive
        next: null
      }

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles complex nested schema validation failures', async () => {
      const step1: WorkflowStep = {
        id: 'step1',
        name: 'Complex schema',
        description: 'Complex nested output',
        handler: async () => ({
          data: {
            items: [
              { id: 'invalid-format' } // Missing required fields
            ]
          }
        }),
        inputSchema: z.object({ value: z.number() }),
        outputSchema: z.object({
          data: z.object({
            items: z.array(
              z.object({
                id: z.string().uuid(),
                name: z.string(),
                count: z.number()
              })
            )
          })
        }),
        next: null
      }

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles step throwing non-Error objects', async () => {
      const step1 = createStep('step1', 'Throws string', async () => {
        throw 'String error'
      })

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles step throwing object without message', async () => {
      const step1 = createStep('step1', 'Throws object', async () => {
        throw { code: 'ERR_CUSTOM', details: 'Something went wrong' }
      })

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()
    })

    it('handles errors in multi-step workflows at different positions', async () => {
      const step1Handler = vi.fn(async (input: { value: number }) => ({ value: input.value + 1 }))
      const step2Handler = vi.fn(async () => {
        throw new Error('Error in middle step')
      })
      const step3Handler = vi.fn(async () => ({ result: 1 }))

      const step1 = createStep('step1', 'Step 1', step1Handler, {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', step2Handler, {
        type: StepType.LINEAR,
        target: 'step3'
      })
      const step3 = createStep('step3', 'Step 3', step3Handler)

      const definition = createWorkflowDefinition([step1, step2, step3], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(WorkflowStepError)

      // Verify execution stopped at step2
      expect(step1Handler).toHaveBeenCalled()
      expect(step2Handler).toHaveBeenCalled()
      expect(step3Handler).not.toHaveBeenCalled()
    })

    it('logs detailed error information', async () => {
      const step1 = createStep('step1', 'Failing step', async () => {
        throw new Error('Detailed error message')
      })

      const definition = createWorkflowDefinition([step1], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()

      // Verify error was logged
      expect(context.logger.error).toHaveBeenCalled()
    })
  })

  describe('Cycle Detection', () => {
    it('detects simple cycle (A -> A) at runtime', async () => {
      const stepA: WorkflowStep = {
        id: 'stepA',
        name: 'Step A',
        description: 'Points to itself',
        handler: async (input: unknown) => input,
        inputSchema: z.unknown(),
        outputSchema: z.unknown(),
        next: { type: StepType.LINEAR, target: 'stepA' }
      }

      // Add terminal step to pass constructor validation
      const terminalStep = createStep('terminal', 'Terminal', async () => ({ result: 1 }))

      const definition = createWorkflowDefinition([stepA, terminalStep], 'stepA')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Cycle will be detected during execution
      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(WorkflowStepError)
    })

    it('detects two-step cycle (A -> B -> A) at runtime', async () => {
      const stepA = createStep('stepA', 'Step A', async (input: unknown) => input, {
        type: StepType.LINEAR,
        target: 'stepB'
      })

      const stepB = createStep('stepB', 'Step B', async (input: unknown) => input, {
        type: StepType.LINEAR,
        target: 'stepA'
      })

      // Add terminal step to pass constructor validation
      const terminalStep = createStep('terminal', 'Terminal', async () => ({ result: 1 }))

      const definition = createWorkflowDefinition([stepA, stepB, terminalStep], 'stepA')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Cycle will be detected during execution
      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow(WorkflowStepError)
    })

    it('allows revisiting steps in different execution paths (no cycle)', async () => {
      const step1 = createStep('step1', 'Step 1', async (input: { value: number }) => ({ value: input.value }), {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', async () => ({ result: 100 }))

      const definition = createWorkflowDefinition([step1, step2], 'step1')
      const workflow = new Workflow(definition)
      const context = createMockContext()

      // Execute twice - should work both times
      await workflow.execute({ value: 1 }, context)
      await workflow.execute({ value: 2 }, context)

      expect(true).toBe(true) // Both executions succeeded
    })
  })

  describe('Execution Logging', () => {
    it('logs workflow start', async () => {
      const step1 = createStep('step1', 'Step 1', async () => ({ result: 1 }))
      const definition = createWorkflowDefinition([step1], 'step1')

      const workflow = new Workflow(definition)
      const context = createMockContext()

      await workflow.execute({ value: 1 }, context)

      expect(context.logger.info).toHaveBeenCalledWith(
        expect.stringContaining('Executing workflow'),
        expect.any(Object)
      )
    })

    it('logs workflow success with execution path', async () => {
      const step1 = createStep('step1', 'Step 1', async () => ({ value: 1 }), {
        type: StepType.LINEAR,
        target: 'step2'
      })
      const step2 = createStep('step2', 'Step 2', async () => ({ result: 1 }))
      const definition = createWorkflowDefinition([step1, step2], 'step1')

      const workflow = new Workflow(definition)
      const context = createMockContext()

      await workflow.execute({ value: 1 }, context)

      expect(context.logger.info).toHaveBeenCalledWith(
        expect.stringContaining('Workflow completed successfully'),
        expect.any(Object)
      )
    })

    it('logs workflow failure', async () => {
      const step1 = createStep('step1', 'Step 1', async () => {
        throw new Error('Failed')
      })
      const definition = createWorkflowDefinition([step1], 'step1')

      const workflow = new Workflow(definition)
      const context = createMockContext()

      await expect(workflow.execute({ value: 1 }, context)).rejects.toThrow()

      expect(context.logger.error).toHaveBeenCalled()
    })
  })
})
