import type { WorkflowTranslate } from '../parallel'
import type { NodeTracing } from '@/types/workflow'
import { BlockEnum } from '@/app/components/workflow/types'
import { withSelectorKey } from '@/test/i18n-mock'
import formatToTracingNodeList from '../index'

const mockFormatAgentNode = vi.hoisted(() => vi.fn())
const mockFormatHumanInputNode = vi.hoisted(() => vi.fn())
const mockFormatRetryNode = vi.hoisted(() => vi.fn())
const mockAddChildrenToLoopNode = vi.hoisted(() => vi.fn())
const mockAddChildrenToIterationNode = vi.hoisted(() => vi.fn())
const mockFormatParallelNode = vi.hoisted(() => vi.fn())

vi.mock('../agent', () => ({
  __esModule: true,
  default: (...args: unknown[]) => mockFormatAgentNode(...args),
}))

vi.mock('../human-input', () => ({
  __esModule: true,
  default: (...args: unknown[]) => mockFormatHumanInputNode(...args),
}))

vi.mock('../retry', () => ({
  __esModule: true,
  default: (...args: unknown[]) => mockFormatRetryNode(...args),
}))

vi.mock('../loop', () => ({
  addChildrenToLoopNode: (...args: unknown[]) => mockAddChildrenToLoopNode(...args),
}))

vi.mock('../iteration', () => ({
  addChildrenToIterationNode: (...args: unknown[]) => mockAddChildrenToIterationNode(...args),
}))

vi.mock('../parallel', () => ({
  __esModule: true,
  default: (...args: unknown[]) => mockFormatParallelNode(...args),
}))

const createTrace = (overrides: Partial<NodeTracing> = {}): NodeTracing => ({
  id: overrides.id ?? overrides.node_id ?? 'node-1',
  index: overrides.index ?? 0,
  predecessor_node_id: '',
  node_id: overrides.node_id ?? 'node-1',
  node_type: overrides.node_type ?? BlockEnum.Tool,
  title: overrides.title ?? 'Node',
  inputs: {},
  inputs_truncated: false,
  process_data: {},
  process_data_truncated: false,
  outputs_truncated: false,
  status: overrides.status ?? 'succeeded',
  error: overrides.error,
  elapsed_time: 1,
  execution_metadata: overrides.execution_metadata ?? {
    total_tokens: 0,
    total_price: 0,
    currency: 'USD',
  },
  metadata: {
    iterator_length: 0,
    iterator_index: 0,
    loop_length: 0,
    loop_index: 0,
  },
  created_at: 0,
  created_by: {
    id: 'user-1',
    name: 'User',
    email: 'user@example.com',
  },
  finished_at: 1,
})

const createExecutionMetadata = (
  overrides: Partial<NonNullable<NodeTracing['execution_metadata']>> = {},
): NonNullable<NodeTracing['execution_metadata']> => ({
  total_tokens: 0,
  total_price: 0,
  currency: 'USD',
  ...overrides,
})

describe('formatToTracingNodeList', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockFormatAgentNode.mockImplementation((list: NodeTracing[]) => list)
    mockFormatHumanInputNode.mockImplementation((list: NodeTracing[]) => list)
    mockFormatRetryNode.mockImplementation((list: NodeTracing[]) => list)
    mockAddChildrenToLoopNode.mockImplementation((item: NodeTracing, children: NodeTracing[]) => ({
      ...item,
      loopChildren: children.map((child) => child.node_id),
      details: [[{ id: 'loop-detail-row' }]],
    }))
    mockAddChildrenToIterationNode.mockImplementation(
      (item: NodeTracing, children: NodeTracing[]) => ({
        ...item,
        iterationChildren: children.map((child) => child.node_id),
        details: [[{ id: 'iteration-detail-row' }]],
      }),
    )
    mockFormatParallelNode.mockImplementation((list: unknown[]) =>
      list.map((item) => ({
        ...(item as Record<string, unknown>),
        parallelFormatted: true,
      })),
    )
  })

  it('should sort the input by index and run the formatter pipeline in order', () => {
    const t = withSelectorKey(
      vi.fn((key: string) => key),
      'workflow',
    ) as WorkflowTranslate
    const traces = [
      createTrace({ id: 'b', node_id: 'b', title: 'B', index: 2 }),
      createTrace({ id: 'a', node_id: 'a', title: 'A', index: 0 }),
      createTrace({ id: 'c', node_id: 'c', title: 'C', index: 1 }),
    ]

    const result = formatToTracingNodeList(traces, t)

    expect(mockFormatAgentNode).toHaveBeenCalledWith([
      expect.objectContaining({ node_id: 'a' }),
      expect.objectContaining({ node_id: 'c' }),
      expect.objectContaining({ node_id: 'b' }),
    ])
    expect(mockFormatHumanInputNode).toHaveBeenCalledWith(
      mockFormatAgentNode.mock.results[0]!.value,
    )
    expect(mockFormatRetryNode).toHaveBeenCalledWith(
      mockFormatHumanInputNode.mock.results[0]!.value,
    )
    expect(mockFormatParallelNode).toHaveBeenLastCalledWith(expect.any(Array), t)
    expect(result).toEqual([
      expect.objectContaining({ node_id: 'a', parallelFormatted: true }),
      expect.objectContaining({ node_id: 'c', parallelFormatted: true }),
      expect.objectContaining({ node_id: 'b', parallelFormatted: true }),
    ])
  })

  it('should collapse loop and iteration children into parent nodes and propagate child failures', () => {
    const t = withSelectorKey(
      vi.fn((key: string) => key),
      'workflow',
    ) as WorkflowTranslate
    const loopParent = createTrace({
      id: 'loop-parent',
      node_id: 'loop-parent',
      node_type: BlockEnum.Loop,
      index: 0,
    })
    const loopChild = createTrace({
      id: 'loop-child',
      node_id: 'loop-child',
      index: 1,
      status: 'failed',
      error: 'loop child failed',
      execution_metadata: createExecutionMetadata({ loop_id: 'loop-parent' }),
    })
    const iterationParent = createTrace({
      id: 'iteration-parent',
      node_id: 'iteration-parent',
      node_type: BlockEnum.Iteration,
      index: 2,
    })
    const iterationChild = createTrace({
      id: 'iteration-child',
      node_id: 'iteration-child',
      index: 3,
      status: 'failed',
      error: 'iteration child failed',
      execution_metadata: createExecutionMetadata({ iteration_id: 'iteration-parent' }),
    })

    const result = formatToTracingNodeList(
      [loopParent, loopChild, iterationParent, iterationChild],
      t,
    )

    expect(mockAddChildrenToLoopNode).toHaveBeenCalledWith(
      expect.objectContaining({
        node_id: 'loop-parent',
        status: 'failed',
        error: 'loop child failed',
      }),
      [expect.objectContaining({ node_id: 'loop-child' })],
    )
    expect(mockAddChildrenToIterationNode).toHaveBeenCalledWith(
      expect.objectContaining({
        node_id: 'iteration-parent',
        status: 'failed',
        error: 'iteration child failed',
      }),
      [expect.objectContaining({ node_id: 'iteration-child' })],
    )
    expect(mockFormatParallelNode).toHaveBeenCalledTimes(3)
    expect(result).toEqual([
      expect.objectContaining({
        node_id: 'loop-parent',
        loopChildren: ['loop-child'],
        parallelFormatted: true,
      }),
      expect.objectContaining({
        node_id: 'iteration-parent',
        iterationChildren: ['iteration-child'],
        parallelFormatted: true,
      }),
    ])
  })
})
