'use strict';

/**
 * Unit tests for status-formatter.js
 */

const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

const {
  format,
  formatElapsed,
  formatTokens,
  statusIcon,
  truncateLine,
  extractAgentId,
} = require('../../src/status-formatter');

const NOW = Date.now();

function makeState(overrides = {}) {
  return {
    sessionKey: 'agent:main:mattermost:channel:abc:thread:xyz',
    status: 'active',
    startTime: NOW - 38000, // 38s ago
    lines: [],
    children: [],
    agentId: 'main',
    depth: 0,
    tokenCount: 0,
    ...overrides,
  };
}

describe('status-formatter.js', () => {
  describe('format()', () => {
    it('formats active session with header', () => {
      const state = makeState();
      const result = format(state);
      assert.ok(result.includes('[ACTIVE]'));
      assert.ok(result.includes('main'));
      assert.ok(result.match(/\d+s/));
    });

    it('formats done session with footer', () => {
      const state = makeState({ status: 'done' });
      const result = format(state);
      assert.ok(result.includes('[DONE]'));
    });

    it('formats error session', () => {
      const state = makeState({ status: 'error' });
      const result = format(state);
      assert.ok(result.includes('[ERROR]'));
    });

    it('formats interrupted session', () => {
      const state = makeState({ status: 'interrupted' });
      const result = format(state);
      assert.ok(result.includes('[INTERRUPTED]'));
    });

    it('includes status lines', () => {
      const state = makeState({
        lines: ['Reading files...', '  exec: ls [OK]', 'Writing results...'],
      });
      const result = format(state);
      assert.ok(result.includes('Reading files...'));
      assert.ok(result.includes('exec: ls [OK]'));
      assert.ok(result.includes('Writing results...'));
    });

    it('limits status lines to maxLines', () => {
      const lines = Array.from({ length: 30 }, (_, i) => `Line ${i + 1}`);
      const state = makeState({ lines });
      const result = format(state, { maxLines: 5 });
      // Only last 5 lines should appear
      assert.ok(result.includes('Line 26'));
      assert.ok(result.includes('Line 30'));
      assert.ok(!result.includes('Line 1'));
    });

    it('includes token count in done footer', () => {
      const state = makeState({ status: 'done', tokenCount: 12400 });
      const result = format(state);
      assert.ok(result.includes('12.4k'));
    });

    it('no token count in footer when zero', () => {
      const state = makeState({ status: 'done', tokenCount: 0 });
      const result = format(state);
      // Should not include "tokens" for zero count
      assert.ok(!result.includes('tokens'));
    });

    it('renders nested child sessions', () => {
      const child = makeState({
        sessionKey: 'agent:main:subagent:uuid-1',
        agentId: 'proj035-planner',
        depth: 1,
        status: 'done',
        lines: ['Reading protocol...'],
      });
      const parent = makeState({
        lines: ['Starting plan...'],
        children: [child],
      });
      const result = format(parent);
      assert.ok(result.includes('proj035-planner'));
      assert.ok(result.includes('Reading protocol...'));
      // Child should be indented — top-level uses blockquote prefix ("> ") so child
      // lines appear as ">   ..." ("> " + depth*2 spaces). Verify indentation exists.
      const childLine = result.split('\n').find((l) => l.includes('proj035-planner'));
      assert.ok(childLine && /^> {2}/.test(childLine));
    });

    it('active session has no done footer', () => {
      const state = makeState({ status: 'active' });
      const result = format(state);
      const lines = result.split('\n');
      // No line should contain [DONE], [ERROR], [INTERRUPTED]
      assert.ok(!lines.some((l) => /\[(DONE|ERROR|INTERRUPTED)\]/.test(l)));
    });
  });

  describe('formatElapsed()', () => {
    it('formats seconds', () => {
      assert.equal(formatElapsed(0), '0s');
      assert.equal(formatElapsed(1000), '1s');
      assert.equal(formatElapsed(59000), '59s');
    });

    it('formats minutes', () => {
      assert.equal(formatElapsed(60000), '1m0s');
      assert.equal(formatElapsed(90000), '1m30s');
      assert.equal(formatElapsed(3599000), '59m59s');
    });

    it('formats hours', () => {
      assert.equal(formatElapsed(3600000), '1h0m');
      assert.equal(formatElapsed(7260000), '2h1m');
    });

    it('handles negative values', () => {
      assert.equal(formatElapsed(-1000), '0s');
    });
  });

  describe('formatTokens()', () => {
    it('formats small counts', () => {
      assert.equal(formatTokens(0), '0');
      assert.equal(formatTokens(999), '999');
    });

    it('formats thousands', () => {
      assert.equal(formatTokens(1000), '1.0k');
      assert.equal(formatTokens(12400), '12.4k');
      assert.equal(formatTokens(999900), '999.9k');
    });

    it('formats millions', () => {
      assert.equal(formatTokens(1000000), '1.0M');
      assert.equal(formatTokens(2500000), '2.5M');
    });
  });

  describe('statusIcon()', () => {
    it('returns correct icons', () => {
      assert.equal(statusIcon('active'), '[ACTIVE]');
      assert.equal(statusIcon('done'), '[DONE]');
      assert.equal(statusIcon('error'), '[ERROR]');
      assert.equal(statusIcon('interrupted'), '[INTERRUPTED]');
      assert.equal(statusIcon('unknown'), '[UNKNOWN]');
      assert.equal(statusIcon(''), '[UNKNOWN]');
    });
  });

  describe('truncateLine()', () => {
    it('does not truncate short lines', () => {
      const line = 'Short line';
      assert.equal(truncateLine(line), line);
    });

    it('truncates long lines', () => {
      const line = 'x'.repeat(200);
      const result = truncateLine(line);
      assert.ok(result.length <= 120);
      assert.ok(result.endsWith('...'));
    });
  });

  describe('extractAgentId()', () => {
    it('extracts agent ID from session key', () => {
      assert.equal(extractAgentId('agent:main:mattermost:channel:abc'), 'main');
      assert.equal(extractAgentId('agent:coder-agent:session:123'), 'coder-agent');
    });

    it('handles non-standard keys', () => {
      assert.equal(extractAgentId('main'), 'main');
      assert.equal(extractAgentId(''), 'unknown');
    });

    it('handles null/undefined', () => {
      assert.equal(extractAgentId(null), 'unknown');
      assert.equal(extractAgentId(undefined), 'unknown');
    });
  });
});
