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

import {
  collapseOverflowStacks,
  collapseOverflowStacksWithMeta,
} from '@features/agent-graph/core/domain/collapseOverflowStacks';

import type { GraphNode } from '@claude-teams/agent-graph';

function makeTaskNode(taskId: string, ownerName: string | null = 'alice'): GraphNode {
  return {
    id: `task:my-team:${taskId}`,
    kind: 'task',
    label: `#${taskId}`,
    displayId: `#${taskId}`,
    sublabel: `Task ${taskId}`,
    state: 'waiting',
    taskStatus: 'pending',
    reviewState: 'none',
    ownerId: ownerName ? `member:my-team:${ownerName}` : null,
    domainRef: { kind: 'task', teamName: 'my-team', taskId },
  };
}

describe('collapseOverflowStacks', () => {
  it('keeps all tasks visible when the column fits within the max row count', () => {
    const nodes = Array.from({ length: 6 }, (_, index) => makeTaskNode(`task-${index + 1}`));

    const result = collapseOverflowStacks(nodes, 'my-team', 6);

    expect(result).toHaveLength(6);
    expect(result.every((node) => !node.isOverflowStack)).toBe(true);
  });

  it('replaces the hidden tail with a single overflow stack node while preserving visible order', () => {
    const nodes = Array.from({ length: 7 }, (_, index) => makeTaskNode(`task-${index + 1}`));

    const result = collapseOverflowStacks(nodes, 'my-team', 6);

    expect(result).toHaveLength(6);
    expect(result.slice(0, 5).map((node) => node.domainRef.kind === 'task' && node.domainRef.taskId)).toEqual([
      'task-1',
      'task-2',
      'task-3',
      'task-4',
      'task-5',
    ]);
    expect(result[5]).toMatchObject({
      isOverflowStack: true,
      overflowCount: 2,
      overflowTaskIds: ['task-6', 'task-7'],
      domainRef: {
        kind: 'task_overflow',
        teamName: 'my-team',
        ownerMemberName: 'alice',
        columnKey: 'todo',
      },
    });
  });

  it('applies the same stack rules to unassigned task columns', () => {
    const nodes = Array.from({ length: 7 }, (_, index) => makeTaskNode(`task-${index + 1}`, null));

    const result = collapseOverflowStacks(nodes, 'my-team', 6);
    const stack = result.find((node) => node.isOverflowStack);

    expect(stack).toMatchObject({
      overflowCount: 2,
      overflowTaskIds: ['task-6', 'task-7'],
      ownerId: null,
      domainRef: {
        kind: 'task_overflow',
        teamName: 'my-team',
        ownerMemberName: null,
        columnKey: 'todo',
      },
    });
  });

  it('returns a visible-node mapping for hidden tasks behind the stack', () => {
    const nodes = Array.from({ length: 7 }, (_, index) => makeTaskNode(`task-${index + 1}`));

    const result = collapseOverflowStacksWithMeta(nodes, 'my-team', 6);
    const stackNode = result.visibleNodes.find((node) => node.isOverflowStack);

    expect(stackNode).toBeDefined();
    expect(result.visibleNodeIdByTaskId.get('task-1')).toBe('task:my-team:task-1');
    expect(result.visibleNodeIdByTaskId.get('task-6')).toBe(stackNode?.id);
    expect(result.visibleNodeIdByTaskId.get('task-7')).toBe(stackNode?.id);
  });
});
