import { describe, expect, it, vi } from 'vitest';
import type { RuntimeScope } from '@mosaicstack/types';
import { HermesRuntimeProvider, type HermesRuntimeTransport } from './hermes-runtime-provider.js';

const scope: RuntimeScope = { actorId: 'a', tenantId: 't', channelId: 'c', correlationId: 'r' };
const transport = (capabilities = ['session.list', 'session.tree']): HermesRuntimeTransport => ({
  capabilities: vi.fn(async () => capabilities),
  health: vi.fn(async () => ({ status: 'healthy' })),
  sessions: vi.fn(async () => [
    {
      conversation_id: 'child',
      agent_id: 'hermes-a',
      parent_conversation_id: 'parent',
      status: 'running',
      created_at: '2026-01-01T00:00:00Z',
      updated_at: '2026-01-01T00:00:00Z',
    },
    {
      conversation_id: 'parent',
      agent_id: 'hermes-a',
      status: 'unknown',
      created_at: '2026-01-01T00:00:00Z',
      updated_at: '2026-01-01T00:00:00Z',
    },
  ]),
  stream: async function* () {},
  send: vi.fn(),
  attach: vi.fn(),
  detach: vi.fn(),
  terminate: vi.fn(),
});
describe('HermesRuntimeProvider normalization boundary', () => {
  it('returns an exhaustive fail-closed transitional capability matrix', async () => {
    const provider = new HermesRuntimeProvider(transport());

    await expect(provider.transitionalCapabilityMatrix(scope)).resolves.toEqual([
      { capability: 'kanban', status: 'unsupported' },
      { capability: 'skills', status: 'unsupported' },
      { capability: 'memory', status: 'unsupported' },
      { capability: 'tools', status: 'unsupported' },
      { capability: 'cron', status: 'unsupported' },
    ]);
  });

  it('denies unsupported transitional capabilities without calling Hermes', async () => {
    const hermes = transport();
    const provider = new HermesRuntimeProvider(hermes);

    await expect(provider.assertTransitionalCapability('memory', scope)).rejects.toMatchObject({
      code: 'capability_unsupported',
    });
    expect(hermes.capabilities).not.toHaveBeenCalled();
  });

  it('normalizes legacy sessions without exposing legacy fields', async () => {
    const provider = new HermesRuntimeProvider(transport());
    await expect(provider.listSessions(scope)).resolves.toEqual(
      expect.arrayContaining([
        expect.objectContaining({ id: 'child', runtimeId: 'hermes-a', state: 'active' }),
      ]),
    );
    const result = await provider.listSessions(scope);
    expect(result[0]).not.toHaveProperty('conversation_id');
  });
  it('forms normalized hierarchy and fails closed for unbridged operations', async () => {
    const provider = new HermesRuntimeProvider(transport());
    await expect(provider.getSessionTree(scope)).resolves.toEqual([
      expect.objectContaining({
        session: expect.objectContaining({ id: 'parent', state: 'failed' }),
        children: [expect.objectContaining({ session: expect.objectContaining({ id: 'child' }) })],
      }),
    ]);
    await expect(
      provider.sendMessage('parent', { content: 'x', idempotencyKey: 'i' }, scope),
    ).rejects.toMatchObject({ code: 'capability_unsupported' });
  });
});
