import { describe, it, expect } from 'vitest';
import { SENTINEL_KINDS, isAnySentinel, isCapHitSentinel, isDoomLoopSentinel, isMistakeRecoverySentinel } from '../inference/sentinels.js';
import type { Message } from '../../types/api.js';

function makeSentinel(kind: string): Message {
  return {
    id: 'msg-1',
    session_id: 's',
    chat_id: 'c',
    role: 'system',
    content: '',
    kind: 'message',
    tool_calls: null,
    tool_results: null,
    status: 'complete',
    last_seq: 0,
    tokens_used: null,
    ctx_used: null,
    ctx_max: null,
    started_at: null,
    finished_at: null,
    created_at: new Date().toISOString(),
    metadata: { kind } as unknown as import('../../types/api.js').MessageMetadata,
    summary: false,
    tail_start_id: null,
    compacted_at: null,
  };
}

describe('SENTINEL_KINDS — single source of truth', () => {
  it('contains the three known sentinel kinds', () => {
    expect(SENTINEL_KINDS.has('cap_hit')).toBe(true);
    expect(SENTINEL_KINDS.has('doom_loop')).toBe(true);
    expect(SENTINEL_KINDS.has('mistake_recovery')).toBe(true);
  });

  it('does not contain arbitrary strings', () => {
    expect(SENTINEL_KINDS.has('user')).toBe(false);
    expect(SENTINEL_KINDS.has('assistant')).toBe(false);
    expect(SENTINEL_KINDS.has('')).toBe(false);
  });
});

describe('isAnySentinel', () => {
  it('returns true for cap_hit', () => {
    expect(isAnySentinel(makeSentinel('cap_hit'))).toBe(true);
  });

  it('returns true for doom_loop', () => {
    expect(isAnySentinel(makeSentinel('doom_loop'))).toBe(true);
  });

  it('returns true for mistake_recovery', () => {
    expect(isAnySentinel(makeSentinel('mistake_recovery'))).toBe(true);
  });

  it('returns false for non-system role', () => {
    const m = { ...makeSentinel('cap_hit'), role: 'user' as const };
    expect(isAnySentinel(m)).toBe(false);
  });

  it('returns false for null metadata', () => {
    const m = { ...makeSentinel('cap_hit'), metadata: null };
    expect(isAnySentinel(m)).toBe(false);
  });

  it('returns false for unknown kind', () => {
    expect(isAnySentinel(makeSentinel('unknown_kind'))).toBe(false);
  });
});

describe('individual sentinel predicates still work', () => {
  it('isCapHitSentinel matches cap_hit only', () => {
    expect(isCapHitSentinel(makeSentinel('cap_hit'))).toBe(true);
    expect(isCapHitSentinel(makeSentinel('doom_loop'))).toBe(false);
  });

  it('isDoomLoopSentinel matches doom_loop only', () => {
    expect(isDoomLoopSentinel(makeSentinel('doom_loop'))).toBe(true);
    expect(isDoomLoopSentinel(makeSentinel('cap_hit'))).toBe(false);
  });

  it('isMistakeRecoverySentinel matches mistake_recovery only', () => {
    expect(isMistakeRecoverySentinel(makeSentinel('mistake_recovery'))).toBe(true);
    expect(isMistakeRecoverySentinel(makeSentinel('cap_hit'))).toBe(false);
  });
});
