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

import { BoardTaskActivityEntryBuilder } from '../../../../src/main/services/team/taskLogs/activity/BoardTaskActivityEntryBuilder';

import type { TeamTask } from '../../../../src/shared/types/team';
import type { RawTaskActivityMessage } from '../../../../src/main/services/team/taskLogs/activity/BoardTaskActivityTranscriptReader';

function makeTask(task: Partial<TeamTask> & Pick<TeamTask, 'id' | 'subject' | 'status'>): TeamTask {
  return {
    displayId: task.displayId ?? task.id.slice(0, 8),
    createdAt: '2026-04-12T10:00:00.000Z',
    updatedAt: '2026-04-12T10:00:00.000Z',
    ...task,
  };
}

describe('BoardTaskActivityEntryBuilder', () => {
  it('builds same-task execution rows and external board actions', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });
    const taskB = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174001',
      displayId: 'efgh5678',
      subject: 'Task B',
      status: 'pending',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/a.jsonl',
        uuid: 'msg-1',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        agentId: 'agent-a',
        agentName: 'alice',
        isSidechain: true,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'execution',
            actorContext: { relation: 'same_task' },
          },
        ],
        boardTaskToolActions: [],
      },
      {
        filePath: '/tmp/b.jsonl',
        uuid: 'msg-2',
        timestamp: '2026-04-12T10:01:00.000Z',
        sessionId: 'session-1',
        agentId: 'agent-a',
        agentName: 'alice',
        isSidechain: true,
        sourceOrder: 2,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-2',
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: {
              relation: 'other_active_task',
              activeTask: { ref: 'efgh5678', refKind: 'display', canonicalId: taskB.id },
              activePhase: 'work',
              activeExecutionSeq: 2,
            },
          },
        ],
        boardTaskToolActions: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-2',
            canonicalToolName: 'task_add_comment',
            resultRefs: { commentId: 'comment-1' },
          },
        ],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA, taskB],
      messages,
    });

    expect(entries).toHaveLength(2);
    expect(entries[0]?.linkKind).toBe('execution');
    expect(entries[1]?.actorContext.relation).toBe('other_active_task');
    expect(entries[1]?.action?.canonicalToolName).toBe('task_add_comment');
    expect(entries[1]?.action?.category).toBe('comment');
    expect(entries[1]?.action?.details?.commentId).toBe('comment-1');
  });

  it('marks display-id collisions as ambiguous instead of guessing', () => {
    const liveTask = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Live task',
      status: 'in_progress',
    });
    const deletedTask = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174099',
      displayId: 'abcd1234',
      subject: 'Deleted task',
      status: 'deleted',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/a.jsonl',
        uuid: 'msg-1',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        isSidechain: true,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            task: { ref: 'abcd1234', refKind: 'display' },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
        ],
        boardTaskToolActions: [],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: liveTask,
      tasks: [liveTask, deletedTask],
      messages,
    });

    expect(entries).toHaveLength(1);
    expect(entries[0]?.task.resolution).toBe('ambiguous');
  });

  it('preserves deleted peer tasks on relationship rows', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });
    const deletedPeer = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174002',
      displayId: 'ijkl9012',
      subject: 'Task B',
      status: 'deleted',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/relationships.jsonl',
        uuid: 'msg-3',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        agentName: 'lead',
        isSidechain: false,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-3',
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
          {
            schemaVersion: 1,
            toolUseId: 'tool-3',
            task: { ref: 'ijkl9012', refKind: 'display', canonicalId: deletedPeer.id },
            targetRole: 'related',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
        ],
        boardTaskToolActions: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-3',
            canonicalToolName: 'task_link',
            input: { relationship: 'related' },
          },
        ],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA, deletedPeer],
      messages,
    });

    expect(entries).toHaveLength(1);
    expect(entries[0]?.action?.peerTask?.resolution).toBe('deleted');
    expect(entries[0]?.action?.details?.relationship).toBe('related');
    expect(entries[0]?.action?.category).toBe('relationship');
    expect(entries[0]?.action?.relationshipPerspective).toBe('symmetric');
  });

  it('resolves display locators case-insensitively and canonical-like unknown refs safely', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/case.jsonl',
        uuid: 'msg-4',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        isSidechain: false,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            task: { ref: 'ABCD1234', refKind: 'display' },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
          {
            schemaVersion: 1,
            task: { ref: taskA.id, refKind: 'unknown' },
            targetRole: 'subject',
            linkKind: 'execution',
            actorContext: { relation: 'same_task' },
          },
        ],
        boardTaskToolActions: [],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA],
      messages,
    });

    expect(entries).toHaveLength(2);
    expect(entries[0]?.task.resolution).toBe('resolved');
    expect(entries[1]?.task.resolution).toBe('resolved');
  });

  it('marks main-session actor without explicit name as unknown instead of forcing lead', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/unknown-actor.jsonl',
        uuid: 'msg-5',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        isSidechain: false,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
        ],
        boardTaskToolActions: [],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA],
      messages,
    });

    expect(entries).toHaveLength(1);
    expect(entries[0]?.actor.role).toBe('unknown');
  });

  it('keeps named main-session teammates as members instead of forcing lead role', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/main-member.jsonl',
        uuid: 'msg-main-member',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        agentName: 'tom',
        isSidechain: false,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'same_task' },
          },
        ],
        boardTaskToolActions: [],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA],
      messages,
    });

    expect(entries).toHaveLength(1);
    expect(entries[0]?.actor).toMatchObject({
      memberName: 'tom',
      role: 'member',
      isSidechain: false,
    });
  });

  it('never joins action payloads onto execution rows', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174000',
      displayId: 'abcd1234',
      subject: 'Task A',
      status: 'in_progress',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/execution-malformed.jsonl',
        uuid: 'msg-6',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        agentId: 'agent-a',
        agentName: 'alice',
        isSidechain: true,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-1',
            task: { ref: 'abcd1234', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'execution',
            actorContext: { relation: 'same_task' },
          },
        ],
        boardTaskToolActions: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-1',
            canonicalToolName: 'task_start',
          },
        ],
      },
    ];

    const entries = new BoardTaskActivityEntryBuilder().buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA],
      messages,
    });

    expect(entries).toHaveLength(1);
    expect(entries[0]?.linkKind).toBe('execution');
    expect(entries[0]?.action).toBeUndefined();
  });

  it('derives relationship perspective from target role', () => {
    const taskA = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174010',
      displayId: 'taska010',
      subject: 'Task A',
      status: 'in_progress',
    });
    const taskB = makeTask({
      id: '123e4567-e89b-12d3-a456-426614174011',
      displayId: 'taskb011',
      subject: 'Task B',
      status: 'pending',
    });

    const messages: RawTaskActivityMessage[] = [
      {
        filePath: '/tmp/relationship-perspective.jsonl',
        uuid: 'msg-7',
        timestamp: '2026-04-12T10:00:00.000Z',
        sessionId: 'session-1',
        agentName: 'lead',
        isSidechain: false,
        sourceOrder: 1,
        boardTaskLinks: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-7',
            task: { ref: 'taska010', refKind: 'display', canonicalId: taskA.id },
            targetRole: 'subject',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
          {
            schemaVersion: 1,
            toolUseId: 'tool-7',
            task: { ref: 'taskb011', refKind: 'display', canonicalId: taskB.id },
            targetRole: 'related',
            linkKind: 'board_action',
            actorContext: { relation: 'idle' },
          },
        ],
        boardTaskToolActions: [
          {
            schemaVersion: 1,
            toolUseId: 'tool-7',
            canonicalToolName: 'task_link',
            input: { relationship: 'blocked-by' },
          },
        ],
      },
    ];

    const builder = new BoardTaskActivityEntryBuilder();
    const entriesForTaskA = builder.buildForTask({
      teamName: 'demo',
      targetTask: taskA,
      tasks: [taskA, taskB],
      messages,
    });
    const entriesForTaskB = builder.buildForTask({
      teamName: 'demo',
      targetTask: taskB,
      tasks: [taskA, taskB],
      messages,
    });

    expect(entriesForTaskA).toHaveLength(1);
    expect(entriesForTaskA[0]?.action?.relationshipPerspective).toBe('incoming');
    expect(entriesForTaskA[0]?.action?.peerTask?.taskRef?.taskId).toBe(taskB.id);

    expect(entriesForTaskB).toHaveLength(1);
    expect(entriesForTaskB[0]?.action?.relationshipPerspective).toBe('outgoing');
    expect(entriesForTaskB[0]?.action?.peerTask?.taskRef?.taskId).toBe(taskA.id);
  });
});
