import { useMemo, useCallback } from 'react'
import type { ApiEvent, ExtendedEvent } from '../types'
import { processSlideEvents, attachSlideMetadata } from '../utils/slideEventHelpers'
import { getEventRenderProps } from '../utils/event-render-props'

type TodoListItem = {
  id?: string
  title?: string
  status?: string
  [key: string]: unknown
}

function getEventTaskId(event: ApiEvent | ExtendedEvent): string | undefined {
  const directKeys = ['task_id', 'plan_id', 'conversation_id'] as const

  for (const key of directKeys) {
    if (key in event) {
      const value = event[key]
      if (value !== undefined && value !== null) {
        return String(value)
      }
    }
  }

  const metadataTaskId =
    event.metadata &&
    typeof event.metadata === 'object' &&
    (('task_id' in event.metadata && event.metadata.task_id) ||
      ('plan_id' in event.metadata && event.metadata.plan_id) ||
      ('conversation_id' in event.metadata && event.metadata.conversation_id))
      ? ((event.metadata.task_id ??
          event.metadata.plan_id ??
          event.metadata.conversation_id) as string | number | undefined | null)
      : undefined

  return metadataTaskId !== undefined && metadataTaskId !== null
    ? String(metadataTaskId)
    : undefined
}

function isTodoListEvent(event: ExtendedEvent): boolean {
  return (
    event.event_type === 'task_update' &&
    !!event.renderProps?.taskTodoConfig?.list?.length
  )
}

function getTodoItemKey(item: TodoListItem, index: number): string {
  if (item.id !== undefined && item.id !== null) {
    return `id:${String(item.id)}`
  }

  if (typeof item.title === 'string' && item.title.trim()) {
    return `title:${item.title.trim()}`
  }

  return `index:${index}`
}

function mergeTodoListItems(group: ExtendedEvent[]): TodoListItem[] {
  const itemsByKey = new Map<string, TodoListItem>()
  const firstSeenKeys: string[] = []

  for (const event of group) {
    const items = (event.renderProps?.taskTodoConfig?.list || []) as TodoListItem[]

    items.forEach((item, index) => {
      const key = getTodoItemKey(item, index)

      if (!itemsByKey.has(key)) {
        firstSeenKeys.push(key)
      }

      itemsByKey.set(key, {
        ...(itemsByKey.get(key) || {}),
        ...item,
      })
    })
  }

  const latestItems = (group[group.length - 1]?.renderProps?.taskTodoConfig?.list ||
    []) as TodoListItem[]
  const orderedKeys = Array.from(
    new Set([
      ...latestItems.map((item, index) => getTodoItemKey(item, index)),
      ...firstSeenKeys,
    ]),
  )

  return orderedKeys
    .map(key => itemsByKey.get(key))
    .filter((item): item is TodoListItem => !!item)
}

function mergeTodoListGroup(group: ExtendedEvent[]): ExtendedEvent {
  const lastEvent = group[group.length - 1]

  return {
    ...lastEvent,
    renderProps: lastEvent.renderProps
      ? {
          ...lastEvent.renderProps,
          taskTodoConfig: {
            list: mergeTodoListItems(group),
          },
        }
      : lastEvent.renderProps,
  }
}

/**
 * 将 ApiEvent 转换为 ExtendedEvent，并填充 renderProps
 */
export function useEventConverter() {
  return useCallback((event: ApiEvent): ExtendedEvent => {
    const isUserInput = event.event_type === 'user_input' || event.role === 'user'

    const extEvent: ExtendedEvent = {
      event_id: event.event_id,
      event_type: event.event_type || 'message',
      timestamp: event.created_at
        ? new Date(event.created_at).getTime()
        : typeof event.timestamp === 'number'
          ? event.timestamp
          : event.timestamp
            ? new Date(event.timestamp).getTime()
            : Date.now(),
      content: {
        text: (event.content?.text || event.content?.content || '') as string,
        ...event.content,
      },
      metadata: {
        ...event.metadata,
        isUserInput,
        task_id: getEventTaskId(event),
        role: event.role,
      },
      event_status: event.event_status,
      stream: event.stream,
      plan_step_state: event.plan_step_state,
      // 填充 renderProps（核心：将原始事件结构化为渲染所需属性）
      renderProps: getEventRenderProps(event),
    }

    return extEvent
  }, [])
}

/**
 * 处理原始事件数据，去重、合并流式事件，并生成 renderProps
 */
export function useEventProcessor(
  rawEvents: ApiEvent[],
) {
  const convertEvent = useEventConverter()

  return useMemo(() => {
    if (!rawEvents || rawEvents.length === 0) {
      return [[] as ExtendedEvent[]]
    }

    // 按 event_id 去重，后来的替换先来的（流式事件也遵循此规则）
    const eventsMap = new Map<string, ApiEvent>()
    for (const event of rawEvents) {
      eventsMap.set(event.event_id, event)
    }

    // 处理 slide 流式数据
    const slideData = processSlideEvents(rawEvents)

    // 转换、过滤、附加 slide 元数据
    const displayEvents = Array.from(eventsMap.values())
      .filter(event => event.is_display !== false)
      // 对应 Remix task-event.ts notEmpty：过滤 text 类型但内容为空的流式中间状态
      .filter(event => {
        if (event.event_type !== 'text') return true
        const content = event.content?.content
        return !(!content || (typeof content === 'string' && !content.trim()))
      })
      .map(event => {
        const extEvent = convertEvent(event)

        const slideMetadata = attachSlideMetadata(event, slideData)
        if (slideMetadata) {
          extEvent.metadata = {
            ...extEvent.metadata,
            slide: slideMetadata,
          }
        }

        return extEvent
      })

    // 按时间戳升序排列
    displayEvents.sort(
      (a: ExtendedEvent, b: ExtendedEvent) =>
        (a.timestamp || 0) - (b.timestamp || 0),
    )

    // ─────────────────────────────────────────────
    // browser_use 事件合并：
    // - 按 tool_call_id 维度将同一次浏览器操作的多条事件合并为一条
    // - 保留「最后一个事件」作为展示主体
    // - 如果最后一个没有截图，则从整组中向前查找最近一个带截图的 tool_output 补上
    // 这样从一开始就只渲染一条 BrowserUseAction，不会出现多条拆散的浏览器卡片
    // ─────────────────────────────────────────────

    const browserGroups = new Map<string, ExtendedEvent[]>()
    const nonBrowserEvents: ExtendedEvent[] = []

    for (const event of displayEvents) {
      const actionType = event.renderProps?.action?.action_type
      if (actionType === 'browser_use') {
        const toolCallId =
          (event.content as any)?.tool_call_id ||
          (event.renderProps?.action?.tool_input as any)?.tool_call_id ||
          event.event_id

        const group = browserGroups.get(toolCallId)
        if (group) {
          group.push(event)
        } else {
          browserGroups.set(toolCallId, [event])
        }
      } else {
        nonBrowserEvents.push(event)
      }
    }

    const mergedBrowserEvents: ExtendedEvent[] = []

    for (const group of browserGroups.values()) {
      if (group.length === 0) continue

      const lastEvent = group[group.length - 1]
      const lastAction = lastEvent.renderProps?.action

      if (lastAction) {
        const lastToolOutput = lastAction.tool_output as
          | { result?: { clean_screenshot_path?: string; screenshot_path?: string } }
          | undefined

        const hasScreenshot =
          lastToolOutput?.result?.clean_screenshot_path ||
          lastToolOutput?.result?.screenshot_path

        if (!hasScreenshot) {
          // 从该组中找最近一个有截图的 tool_output
          for (let j = group.length - 2; j >= 0; j--) {
            const candidateOutput = group[j].renderProps?.action?.tool_output as
              | { result?: { clean_screenshot_path?: string; screenshot_path?: string } }
              | undefined
            if (
              candidateOutput?.result?.clean_screenshot_path ||
              candidateOutput?.result?.screenshot_path
            ) {
              lastEvent.renderProps!.action = {
                ...lastEvent.renderProps!.action!,
                tool_output: candidateOutput,
              }
              break
            }
          }
        }
      }

      mergedBrowserEvents.push(lastEvent)
    }

    const mergedEvents = [...nonBrowserEvents, ...mergedBrowserEvents]

    // 合并后再次按时间排序，保证整体对话时间线正确
    mergedEvents.sort(
      (a: ExtendedEvent, b: ExtendedEvent) =>
        (a.timestamp || 0) - (b.timestamp || 0),
    )

    // ─────────────────────────────────────────────
    // TodoList(task_update) 事件合并：
    // - 有 task_id 时，按 task_id 聚合同一条任务线的多次 task_update
    // - 没有 task_id 时，回退到当前用户轮次，避免无 task_id 的卡片被全局误并
    // - 保留最后一个事件作为展示主体
    // - Todo 列表按 id/title 增量合并，兼容后续事件只返回局部步骤的情况
    // 这样消息列表中只展示一张最新的 Todo 卡片，不会被中间更新刷出多条
    // ─────────────────────────────────────────────

    const todoGroups = new Map<string, ExtendedEvent[]>()
    const nonTodoEvents: ExtendedEvent[] = []
    let currentTurnKey = 'conversation-start'

    for (const event of mergedEvents) {
      if (event.metadata?.isUserInput) {
        currentTurnKey = event.event_id
      }

      if (isTodoListEvent(event)) {
        const taskKey = getEventTaskId(event)
        const groupKey = taskKey ? `task:${taskKey}` : `${currentTurnKey}:no-task`
        const group = todoGroups.get(groupKey)

        if (group) {
          group.push(event)
        } else {
          todoGroups.set(groupKey, [event])
        }
      } else {
        nonTodoEvents.push(event)
      }
    }

    const mergedTodoEvents = Array.from(todoGroups.entries()).map(
      ([, group]) => mergeTodoListGroup(group),
    )

    const timelineEvents = [...nonTodoEvents, ...mergedTodoEvents]
    timelineEvents.sort(
      (a: ExtendedEvent, b: ExtendedEvent) =>
        (a.timestamp || 0) - (b.timestamp || 0),
    )

    return [timelineEvents]
  }, [rawEvents, convertEvent])
}
