import { describe, expect, it } from 'vitest';

import { BoardTaskExactLogDetailSelector } from '../../../../src/main/services/team/taskLogs/exact/BoardTaskExactLogDetailSelector';

import type { ParsedMessage } from '../../../../src/main/types';
import type { BoardTaskActivityRecord } from '../../../../src/main/services/team/taskLogs/activity/BoardTaskActivityRecord';
import type { BoardTaskExactLogBundleCandidate } from '../../../../src/main/services/team/taskLogs/exact/BoardTaskExactLogTypes';

function makeRecord(): BoardTaskActivityRecord {
  return {
    id: 'record-1',
    timestamp: '2026-04-12T16:00:00.000Z',
    task: {
      locator: { ref: 'abcd1234', refKind: 'display', canonicalId: 'task-a' },
      resolution: 'resolved',
    },
    linkKind: 'board_action',
    targetRole: 'subject',
    actor: {
      memberName: 'alice',
      role: 'member',
      sessionId: 'session-1',
      agentId: 'agent-1',
      isSidechain: true,
    },
    actorContext: { relation: 'same_task' },
    action: {
      canonicalToolName: 'task_add_comment',
      toolUseId: 'tool-1',
      category: 'comment',
    },
    source: {
      filePath: '/tmp/task.jsonl',
      messageUuid: 'assistant-1',
      toolUseId: 'tool-1',
      sourceOrder: 1,
    },
  };
}

function makeCandidate(records: BoardTaskActivityRecord[]): BoardTaskExactLogBundleCandidate {
  return {
    id: 'tool:/tmp/task.jsonl:tool-1',
    timestamp: '2026-04-12T16:00:00.000Z',
    actor: records[0]!.actor,
    source: {
      filePath: '/tmp/task.jsonl',
      messageUuid: 'assistant-1',
      toolUseId: 'tool-1',
      sourceOrder: 1,
    },
    records,
    anchor: {
      kind: 'tool',
      filePath: '/tmp/task.jsonl',
      messageUuid: 'assistant-1',
      toolUseId: 'tool-1',
    },
    actionLabel: 'Added a comment',
    actionCategory: 'comment',
    canonicalToolName: 'task_add_comment',
    linkKinds: ['board_action'],
    targetRoles: ['subject'],
    canLoadDetail: true,
    sourceGeneration: 'gen-1',
  };
}

describe('BoardTaskExactLogDetailSelector', () => {
  it('keeps the matched tool flow, preserves anchor output, and deduplicates assistant streaming rows anchor-aware', () => {
    const records = [makeRecord()];
    const candidate = makeCandidate(records);
    const parsedMessagesByFile = new Map<string, ParsedMessage[]>([
      [
        '/tmp/task.jsonl',
        [
          {
            uuid: 'assistant-0',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:00:00.000Z'),
            role: 'assistant',
            content: [
              { type: 'thinking', thinking: 'draft' } as never,
              { type: 'text', text: 'old tool draft' } as never,
              { type: 'tool_use', id: 'tool-1', name: 'task_add_comment', input: { taskId: 'x' } } as never,
            ],
            toolCalls: [],
            toolResults: [],
            requestId: 'req-1',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'assistant-1',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:00:01.000Z'),
            role: 'assistant',
            content: [
              { type: 'text', text: 'stream tail without anchor tool call' } as never,
              { type: 'tool_use', id: 'tool-2', name: 'task_get', input: { taskId: 'y' } } as never,
            ],
            toolCalls: [],
            toolResults: [],
            requestId: 'req-1',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'user-1',
            parentUuid: null,
            type: 'user',
            timestamp: new Date('2026-04-12T16:00:02.000Z'),
            role: 'user',
            content: [
              { type: 'tool_result', tool_use_id: 'tool-1', content: 'ok' } as never,
              { type: 'tool_result', tool_use_id: 'tool-2', content: 'ignore' } as never,
            ],
            toolCalls: [],
            toolResults: [],
            sourceToolUseID: 'tool-1',
            sourceToolAssistantUUID: 'assistant-1',
            toolUseResult: { output: 'kept' },
            requestId: 'req-1',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'assistant-2',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:00:03.000Z'),
            role: 'assistant',
            content: [{ type: 'text', text: 'comment saved' } as never],
            toolCalls: [],
            toolResults: [],
            sourceToolUseID: 'tool-1',
            requestId: 'req-2',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
        ],
      ],
    ]);

    const detail = new BoardTaskExactLogDetailSelector().selectDetail({
      candidate,
      records,
      parsedMessagesByFile,
    });

    expect(detail).not.toBeNull();
    expect(detail?.filteredMessages).toHaveLength(3);
    expect(detail?.filteredMessages[0]?.uuid).toBe('assistant-0');
    expect(detail?.filteredMessages[1]?.uuid).toBe('user-1');
    expect(detail?.filteredMessages[2]?.uuid).toBe('assistant-2');
    expect(detail?.filteredMessages[0]?.toolCalls).toHaveLength(1);
    expect(detail?.filteredMessages[1]?.toolResults).toHaveLength(1);
    expect(detail?.filteredMessages[1]?.toolUseResult).toEqual({ output: 'kept' });
    expect(detail?.filteredMessages[1]?.sourceToolAssistantUUID).toBeUndefined();
    expect(detail?.filteredMessages[2]?.sourceToolUseID).toBe('tool-1');
  });

  it('scopes repeated tool use ids to the explicit tool-result parent assistant', () => {
    const record = {
      ...makeRecord(),
      id: 'record-reused-tool-id',
      action: {
        canonicalToolName: 'task_add_comment',
        toolUseId: 'item_24',
        category: 'comment' as const,
      },
      source: {
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-target',
        toolUseId: 'item_24',
        sourceOrder: 1,
      },
    } satisfies BoardTaskActivityRecord;
    const candidate: BoardTaskExactLogBundleCandidate = {
      id: 'tool:/tmp/task.jsonl:item_24',
      timestamp: '2026-04-12T16:00:00.000Z',
      actor: record.actor,
      source: {
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-target',
        toolUseId: 'item_24',
        sourceOrder: 1,
      },
      records: [record],
      anchor: {
        kind: 'tool',
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-target',
        toolUseId: 'item_24',
      },
      actionLabel: 'Added a comment',
      actionCategory: 'comment',
      canonicalToolName: 'task_add_comment',
      linkKinds: ['board_action'],
      targetRoles: ['subject'],
      canLoadDetail: true,
      sourceGeneration: 'gen-reused-tool-id',
    };
    const parsedMessagesByFile = new Map<string, ParsedMessage[]>([
      [
        '/tmp/task.jsonl',
        [
          {
            uuid: 'assistant-target',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:00:00.000Z'),
            role: 'assistant',
            content: [
              { type: 'tool_use', id: 'item_24', name: 'task_add_comment', input: { taskId: 'task-a' } } as never,
            ],
            toolCalls: [],
            toolResults: [],
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'user-target',
            parentUuid: 'assistant-target',
            type: 'user',
            timestamp: new Date('2026-04-12T16:00:01.000Z'),
            role: 'user',
            content: [
              { type: 'tool_result', tool_use_id: 'item_24', content: 'target result' } as never,
            ],
            toolCalls: [],
            toolResults: [],
            sourceToolUseID: 'item_24',
            sourceToolAssistantUUID: 'assistant-target',
            toolUseResult: { toolUseId: 'item_24', content: 'target result' },
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'assistant-other',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:10:00.000Z'),
            role: 'assistant',
            content: [
              { type: 'tool_use', id: 'item_24', name: 'task_complete', input: { taskId: 'other-task' } } as never,
            ],
            toolCalls: [],
            toolResults: [],
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'user-other',
            parentUuid: 'assistant-other',
            type: 'user',
            timestamp: new Date('2026-04-12T16:10:01.000Z'),
            role: 'user',
            content: [
              { type: 'tool_result', tool_use_id: 'item_24', content: 'other result' } as never,
            ],
            toolCalls: [],
            toolResults: [],
            sourceToolUseID: 'item_24',
            sourceToolAssistantUUID: 'assistant-other',
            toolUseResult: { toolUseId: 'item_24', content: 'other result' },
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
        ],
      ],
    ]);

    const detail = new BoardTaskExactLogDetailSelector().selectDetail({
      candidate,
      records: [record],
      parsedMessagesByFile,
    });

    expect(detail).not.toBeNull();
    expect(detail?.filteredMessages.map((message) => message.uuid)).toEqual([
      'assistant-target',
      'user-target',
    ]);
  });

  it('drops stale derived tool metadata when a message-linked row survives filtering', () => {
    const record = {
      ...makeRecord(),
      id: 'record-message-1',
      source: {
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-2',
        sourceOrder: 2,
      },
      action: undefined,
    } satisfies BoardTaskActivityRecord;
    const candidate: BoardTaskExactLogBundleCandidate = {
      id: 'message:/tmp/task.jsonl:user-2',
      timestamp: '2026-04-12T16:01:00.000Z',
      actor: record.actor,
      source: {
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-2',
        sourceOrder: 2,
      },
      records: [record],
      anchor: {
        kind: 'message',
        filePath: '/tmp/task.jsonl',
        messageUuid: 'user-2',
      },
      actionLabel: 'Worked on task',
      linkKinds: ['execution'],
      targetRoles: ['subject'],
      canLoadDetail: true,
      sourceGeneration: 'gen-2',
    };
    const parsedMessagesByFile = new Map<string, ParsedMessage[]>([
      [
        '/tmp/task.jsonl',
        [
          {
            uuid: 'user-2',
            parentUuid: null,
            type: 'user',
            timestamp: new Date('2026-04-12T16:01:00.000Z'),
            role: 'user',
            content: [
              { type: 'text', text: 'status update' } as never,
              { type: 'tool_result', tool_use_id: 'other-tool', content: 'stale tool result' } as never,
            ],
            toolCalls: [],
            toolResults: [],
            sourceToolUseID: 'other-tool',
            sourceToolAssistantUUID: 'assistant-other',
            toolUseResult: { output: 'stale' },
            requestId: 'req-2',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
        ],
      ],
    ]);

    const detail = new BoardTaskExactLogDetailSelector().selectDetail({
      candidate,
      records: [record],
      parsedMessagesByFile,
    });

    expect(detail).not.toBeNull();
    expect(detail?.filteredMessages).toHaveLength(1);
    expect(detail?.filteredMessages[0]?.content).toEqual([{ type: 'text', text: 'status update' }]);
    expect(detail?.filteredMessages[0]?.toolResults).toEqual([]);
    expect(detail?.filteredMessages[0]?.sourceToolUseID).toBeUndefined();
    expect(detail?.filteredMessages[0]?.sourceToolAssistantUUID).toBeUndefined();
    expect(detail?.filteredMessages[0]?.toolUseResult).toBeUndefined();
  });

  it('preserves toolUseResult for a matched tool_result even when sourceToolUseID is absent', () => {
    const records = [makeRecord()];
    const candidate = makeCandidate(records);
    const parsedMessagesByFile = new Map<string, ParsedMessage[]>([
      [
        '/tmp/task.jsonl',
        [
          {
            uuid: 'assistant-1',
            parentUuid: null,
            type: 'assistant',
            timestamp: new Date('2026-04-12T16:00:00.000Z'),
            role: 'assistant',
            content: [
              { type: 'tool_use', id: 'tool-1', name: 'task_add_comment', input: { taskId: 'x' } } as never,
            ],
            toolCalls: [],
            toolResults: [],
            requestId: 'req-1',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
          {
            uuid: 'user-1',
            parentUuid: null,
            type: 'user',
            timestamp: new Date('2026-04-12T16:00:01.000Z'),
            role: 'user',
            content: [
              { type: 'tool_result', tool_use_id: 'tool-1', content: 'ok' } as never,
            ],
            toolCalls: [],
            toolResults: [],
            toolUseResult: {
              toolUseId: 'tool-1',
              content: 'ok',
            },
            requestId: 'req-1',
            isSidechain: true,
            isMeta: false,
            isCompactSummary: false,
          },
        ],
      ],
    ]);

    const detail = new BoardTaskExactLogDetailSelector().selectDetail({
      candidate,
      records,
      parsedMessagesByFile,
    });

    expect(detail).not.toBeNull();
    expect(detail?.filteredMessages).toHaveLength(2);
    expect(detail?.filteredMessages[1]?.sourceToolUseID).toBe('tool-1');
    expect(detail?.filteredMessages[1]?.toolUseResult).toEqual({
      toolUseId: 'tool-1',
      content: 'ok',
    });
  });
});
