/** Test adapter for the production conversation.details.tool registration. */
import type { ConnectionGeneration } from '@deepseek-ai/dsh-client-connection/client'
import type { SessionLiveEventEntry } from '@deepseek-ai/dsh-api-session-controller/client'
import { isJsonValue, type JsonValue } from '@deepseek-ai/dsh-session'
import type {
  ChatConversationViewNode, ChatSnapshot, ConversationNode, DetailsSlotProps,
  DetailsToolOwnerProps, RunningToolCall, ToolResultNode,
} from '@deepseek-ai/dsh-client-ui-chat/client'
import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
import { ToolDetails } from '../src/client/tool/ToolDetails.tsx'

type TrajectorySnapshot = Parameters<Parameters<DetailsSlotProps['useTrajectory']>[0]>[0]

const emptyTrajectory: TrajectorySnapshot = {
  eventNodes: [],
  eventLocations: new Map(),
  requests: [],
  callSchemas: new Map(),
  partial: null,
  runningCalls: [],
}

/** Stable empty Trajectory source for DetailsPanel fixtures. */
export const useEmptyTrajectory: DetailsSlotProps['useTrajectory'] = selector => selector(emptyTrajectory)

function jsonFixture(value: unknown): JsonValue {
  if (!isJsonValue(value)) throw new Error('tool event fixture must be lossless JSON')
  return value as JsonValue
}

/** Build the canonical Chat slice consumed by Tool rows and details tests. */
export function toolChatSnapshot(
  settled: readonly ConversationNode[] = [],
  running: readonly RunningToolCall[] = [],
): ChatSnapshot {
  const roots = [...settled.filter(node => node.kind === 'tool-result'), ...running]
  const nodes: ChatConversationViewNode[] = roots.map(root => ({
    key: `tool:${root.callId}`,
    kind: 'tool-call',
    id: root.callId,
    target: 'chat',
    anchorSeq: 'kind' in root ? root.seq : Number.MAX_SAFE_INTEGER,
    location: { kind: 'session' },
    visibility: 'visible',
    data: { root },
  }))
  const byKey = new Map(nodes.map(node => [node.key, node]))
  const empty: readonly string[] = []
  return {
    order: nodes.map(node => node.key),
    nodes: {
      get: key => byKey.get(key),
      values: () => nodes,
    },
    locations: {
      getTurn: () => empty,
      getStep: () => empty,
    },
    navigation: { items: () => [] },
    timeline: { turnOrder: [], turns: new Map() },
    legacy: {
      nodes: settled,
      runningCalls: running,
      partial: null,
      turnTimings: new Map(),
      turnEnds: new Map(),
    },
  }
}

/** Build the Session event window that projects settled root Tool calls into Chat. */
export function toolSessionEvents(nodes: readonly ToolResultNode[]): readonly SessionLiveEventEntry[] {
  const firstTime = nodes[0]?.callTime ?? nodes[0]?.time ?? 0
  const entries: SessionLiveEventEntry[] = [
    {
      type: 'event',
      event: {
        seq: 1,
        time: firstTime - 2,
        type: 'turn/start',
        data: { turn: 1 },
      },
    },
    {
      type: 'event',
      event: {
        seq: 2,
        time: firstTime - 1,
        type: 'step/start',
        data: { turn: 1, step: 1 },
      },
    },
  ]
  for (const [index, node] of nodes.entries()) {
    if (node.call === null) throw new Error(`tool fixture "${node.callId}" requires its call event`)
    const callSeq = 3 + index * 2
    const callEntry: SessionLiveEventEntry = {
      type: 'event',
      event: {
        seq: callSeq,
        time: node.callTime ?? node.time - 1,
        type: 'tool/call',
        data: {
          turn: 1,
          step: 1,
          callId: node.callId,
          name: node.call.name,
          arguments: node.call.argsRaw,
        },
      } as unknown as SessionLiveEventEntry['event'],
    }
    entries.push(callEntry)
    const resultEntry: SessionLiveEventEntry = {
      type: 'event',
      event: {
        seq: callSeq + 1,
        time: node.time,
        type: 'tool/result',
        data: jsonFixture({
          turn: 1,
          step: 1,
          message: {
            id: `result-${node.callId}`,
            role: 'user',
            source: { kind: 'tool', callId: node.callId },
            content: [{
              type: 'tool-result',
              toolCallId: node.callId,
              content: node.content.map(block => ({ ...block })),
              isError: node.isError,
            }],
          },
          ...(node.error === undefined ? {} : { error: node.error }),
          ...(node.meta === undefined ? {} : { meta: node.meta }),
        }),
        surfaceOp: 'append',
      } as unknown as SessionLiveEventEntry['event'],
    }
    entries.push(resultEntry)
  }
  return entries
}

/**
 * Bind ui-tool's details renderer to the conversation slot callback shape.
 * @param t - conversation locale seat used by Tool cards.
 * @param generation - optional Connection generation carrying the Host home.
 * @returns a direct-test renderSlot implementation.
 */
export function renderToolDetails(
  t: TranslateNS<'conversation'>,
  generation?: ConnectionGeneration,
): DetailsSlotProps['renderSlot'] {
  return (_key, owner) => {
    // PropsRenderSlots keeps its key generic even for this one-key share;
    // recover the concrete owner selected by the adapter's fixed slot.
    const details = owner as unknown as DetailsToolOwnerProps
    return <ToolDetails
      block={details.block}
      cwd={details.cwd}
      useConnectionGeneration={selector => selector(generation)}
      t={t}
    />
  }
}
