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

import {
  resolveTurnOutcome,
  shouldAppendCompletedContent,
} from '../../src/runtime/turn-outcome';

describe('shouldAppendCompletedContent', () => {
  test('does not re-append when deltas already streamed the answer', () => {
    // Claude/OpenCode emit the final answer as assistant_delta AND as the
    // content-bearing assistant_completed event. Folding the completed
    // content in again would produce "<answer><answer>".
    expect(shouldAppendCompletedContent('the answer', 'the answer')).toBe(
      false,
    );
  });

  test('appends completed content when nothing has streamed yet', () => {
    expect(shouldAppendCompletedContent('', 'the final result')).toBe(true);
  });

  test('treats whitespace-only streamed text as empty', () => {
    expect(shouldAppendCompletedContent('   \n', 'the final result')).toBe(
      true,
    );
  });

  test('does not append when the completed event carries no content', () => {
    expect(shouldAppendCompletedContent('the answer', undefined)).toBe(false);
    expect(shouldAppendCompletedContent('', undefined)).toBe(false);
    expect(shouldAppendCompletedContent('', '')).toBe(false);
  });
});

describe('resolveTurnOutcome', () => {
  test('partial streamed text + error is surfaced as failed and text preserved', () => {
    const outcome = resolveTurnOutcome({
      assistantText: 'partial answer before crash',
      error: 'exit code 1: rate limited',
      runtime: 'codex',
    });
    expect(outcome.text).toBe('partial answer before crash');
    expect(outcome.failure).toBe('codex failed:\nexit code 1: rate limited');
  });

  test('successful turn with streamed text and no error has no failure', () => {
    const outcome = resolveTurnOutcome({
      assistantText: 'complete answer',
      runtime: 'codex',
    });
    expect(outcome.text).toBe('complete answer');
    expect(outcome.failure).toBeUndefined();
  });

  test('empty turn with error is surfaced as failed', () => {
    const outcome = resolveTurnOutcome({
      assistantText: '   ',
      error: 'exit code 137',
      runtime: 'claude',
    });
    expect(outcome.failure).toBe('claude failed:\nexit code 137');
  });

  test('empty turn with no error reports the no-response failure', () => {
    const outcome = resolveTurnOutcome({
      assistantText: '',
      runtime: 'codex',
    });
    expect(outcome.failure).toBe(
      'Codex completed without producing an assistant response.',
    );
  });

  test('recovers finalMessage text when nothing streamed and no error', () => {
    const outcome = resolveTurnOutcome({
      assistantText: '',
      recoveredText: 'recovered from last-message file',
      runtime: 'codex',
    });
    expect(outcome.text).toBe('recovered from last-message file');
    expect(outcome.failure).toBeUndefined();
  });

  test('recovered finalMessage text + error is still surfaced as failed', () => {
    const outcome = resolveTurnOutcome({
      assistantText: '',
      recoveredText: 'recovered partial',
      error: 'network drop',
      runtime: 'codex',
    });
    expect(outcome.text).toBe('recovered partial');
    expect(outcome.failure).toBe('codex failed:\nnetwork drop');
  });
});
