import { describe, expect, it } from 'vitest';
import { extractLatestToolName, extractToolNameFromText, petStateFromJobStatus, rowIndexForState } from './petState';

describe('petStateFromJobStatus', () => {
  it('maps dispatching to its own state (separate from running)', () => {
    expect(petStateFromJobStatus('dispatching')).toBe('dispatching');
  });

  it('alternates running direction by task id parity', () => {
    expect(petStateFromJobStatus('running', 2)).toBe('running');
    expect(petStateFromJobStatus('running', 3)).toBe('runningAlt');
    expect(petStateFromJobStatus('running', null)).toBe('running');
    expect(petStateFromJobStatus('running', undefined)).toBe('running');
  });

  it('maps waiting_human to waiting (review pose)', () => {
    expect(petStateFromJobStatus('waiting_human')).toBe('waiting');
  });

  it('maps terminal statuses to done / error', () => {
    expect(petStateFromJobStatus('succeeded')).toBe('done');
    expect(petStateFromJobStatus('failed')).toBe('error');
    expect(petStateFromJobStatus('cancelled')).toBe('error');
  });

  it('falls back to idle for unknown / null', () => {
    expect(petStateFromJobStatus(null)).toBe('idle');
    expect(petStateFromJobStatus(undefined)).toBe('idle');
    expect(petStateFromJobStatus('queued')).toBe('idle');
    expect(petStateFromJobStatus('something-new')).toBe('idle');
  });
});

describe('extractToolNameFromText', () => {
  it('extracts PascalCase tool prefix from currentActivity format', () => {
    expect(extractToolNameFromText('ReadPdf: /tmp/foo.pdf')).toBe('ReadPdf');
    expect(extractToolNameFromText('WebSearch: typescript')).toBe('WebSearch');
    expect(extractToolNameFromText('SQLite: SELECT * FROM users')).toBe('SQLite');
    expect(extractToolNameFromText('SpawnSubTask: child task')).toBe('SpawnSubTask');
  });

  it('extracts MCP tool names', () => {
    expect(extractToolNameFromText('mcp__gitea__listIssues: {}')).toBe('mcp__gitea__listIssues');
    expect(extractToolNameFromText('mcp__code_review_graph__detect_changes_tool: foo'))
      .toBe('mcp__code_review_graph__detect_changes_tool');
  });

  it('returns null for non-tool text', () => {
    expect(extractToolNameFromText('')).toBeNull();
    expect(extractToolNameFromText(null)).toBeNull();
    expect(extractToolNameFromText(undefined)).toBeNull();
    expect(extractToolNameFromText('no colon here')).toBeNull();
    expect(extractToolNameFromText('lowercase: nope')).toBeNull();
    expect(extractToolNameFromText(': only colon')).toBeNull();
  });

  it('returns null when prefix is unreasonably long', () => {
    const longPrefix = 'A'.repeat(200);
    expect(extractToolNameFromText(`${longPrefix}: stuff`)).toBeNull();
  });
});

describe('rowIndexForState', () => {
  it('maps runtime states to Codex Pets row indices', () => {
    expect(rowIndexForState('idle', 9)).toBe(0);
    expect(rowIndexForState('running', 9)).toBe(1);
    expect(rowIndexForState('runningAlt', 9)).toBe(2);
    expect(rowIndexForState('done', 9)).toBe(3);
    expect(rowIndexForState('jumping', 9)).toBe(4);
    expect(rowIndexForState('error', 9)).toBe(5);
    expect(rowIndexForState('dispatching', 9)).toBe(7);
    expect(rowIndexForState('waiting', 9)).toBe(8);
  });

  it('clamps to the last available row when grid has fewer rows', () => {
    expect(rowIndexForState('waiting', 3)).toBe(2);
    expect(rowIndexForState('error', 2)).toBe(1);
    expect(rowIndexForState('idle', 1)).toBe(0);
    expect(rowIndexForState('dispatching', 5)).toBe(4);
  });
});

describe('extractLatestToolName', () => {
  it('delegates to extractToolNameFromText on currentActivity', () => {
    expect(extractLatestToolName('ReadImage: foo.png')).toBe('ReadImage');
    expect(extractLatestToolName(null)).toBeNull();
  });
});
