import { describe, it, expect } from '@jest/globals';
import {
  appendRunLog,
  buildSentinelFailureLog,
  buildSentinelFailureOutput,
  extractSentinelKillReason,
  formatSentinelKillError,
  isSentinelKillError,
  parsePolicyFromKillReason,
  SENTINEL_KILL_ERROR_PREFIX,
} from '../security/sentinel-run-failure.js';

describe('sentinel-run-failure', () => {
  describe('parsePolicyFromKillReason', () => {
    it('extracts policy name from policy_violation prefix', () => {
      expect(parsePolicyFromKillReason('policy_violation:timeout')).toBe('timeout');
      expect(parsePolicyFromKillReason('policy_violation:token_cap')).toBe('token_cap');
    });

    it('returns undefined for non-policy reasons', () => {
      expect(parsePolicyFromKillReason('manual_cancellation')).toBeUndefined();
      expect(parsePolicyFromKillReason('operator_stop')).toBeUndefined();
    });
  });

  describe('buildSentinelFailureOutput', () => {
    it('returns stable deny shape for policy violations', () => {
      expect(buildSentinelFailureOutput('policy_violation:idle')).toEqual({
        error: 'Run terminated by Sentinel',
        source: 'sentinel',
        action: 'deny',
        reason: 'policy_violation:idle',
        policy: 'idle',
      });
    });

    it('returns stable deny shape for manual cancellation', () => {
      expect(buildSentinelFailureOutput('manual_cancellation')).toEqual({
        error: 'Run terminated by Sentinel',
        source: 'sentinel',
        action: 'deny',
        reason: 'manual_cancellation',
      });
    });
  });

  describe('buildSentinelFailureLog', () => {
    it('uses consistent log prefix', () => {
      expect(buildSentinelFailureLog('policy_violation:cost')).toBe(
        'Sentinel denied run: policy_violation:cost'
      );
    });
  });

  describe('extractSentinelKillReason', () => {
    it('parses processor throw message', () => {
      expect(
        extractSentinelKillReason('Run killed by Sentinel: policy_violation:timeout')
      ).toBe('policy_violation:timeout');
    });

    it('returns null for unrelated errors', () => {
      expect(extractSentinelKillReason('Agent not found')).toBeNull();
      expect(isSentinelKillError('Agent not found')).toBe(false);
    });

    it('returns null when reason after prefix is empty', () => {
      expect(extractSentinelKillReason(SENTINEL_KILL_ERROR_PREFIX)).toBeNull();
      expect(extractSentinelKillReason(`${SENTINEL_KILL_ERROR_PREFIX}   `)).toBeNull();
    });
  });

  describe('formatSentinelKillError', () => {
    it('round-trips with extractSentinelKillReason', () => {
      const reason = 'policy_violation:idle';
      const message = formatSentinelKillError(reason);
      expect(extractSentinelKillReason(message)).toBe(reason);
    });
  });

  describe('appendRunLog', () => {
    it('preserves existing entries', () => {
      const first = { timestamp: 't1', level: 'info', message: 'started' };
      const second = { timestamp: 't2', level: 'error', message: 'denied' };
      expect(appendRunLog([first], second)).toEqual([first, second]);
      expect(appendRunLog(undefined, second)).toEqual([second]);
    });
  });
});
