import { describe, it, expect, vi } from 'vitest';
import type { RequestPermissionRequest, CreateElicitationRequest, SessionNotification } from '@agentclientprotocol/sdk';
import { buildAcpClient, type AcpTurnContext } from '../acp-client.js';

/**
 * buildAcpClient (v2.7 audit reshape): the shared ACP `Client` closures. These
 * tests cover the pure routing decisions that don't require the permission-waiter
 * broker machinery — the auto-select/decline fallbacks and the between-turns drop.
 */

describe('buildAcpClient — sessionUpdate', () => {
  it('drops the update when no turn is active (resolveTurn → null)', async () => {
    const client = buildAcpClient('/wt', () => null);
    // Must resolve without throwing and without an onSessionUpdate to call.
    await expect(client.sessionUpdate({ sessionId: 's', update: {} } as unknown as SessionNotification)).resolves.toBeUndefined();
  });

  it('forwards the update to the active turn', async () => {
    const onSessionUpdate = vi.fn();
    const turn: AcpTurnContext = { taskId: 't', sessionId: 's', modeId: undefined, agent: 'goose', onSessionUpdate };
    const client = buildAcpClient('/wt', () => turn);
    const note = { sessionId: 's', update: {} } as unknown as SessionNotification;
    await client.sessionUpdate(note);
    expect(onSessionUpdate).toHaveBeenCalledWith(note);
  });
});

describe('buildAcpClient — requestPermission fallback (no UI routing)', () => {
  function req(options: Array<{ optionId: string }>): RequestPermissionRequest {
    return { options } as unknown as RequestPermissionRequest;
  }

  it('auto-selects the first option when there is no turn', async () => {
    const client = buildAcpClient('/wt', () => null);
    const res = await client.requestPermission(req([{ optionId: 'allow' }, { optionId: 'deny' }]));
    expect(res).toEqual({ outcome: { outcome: 'selected', optionId: 'allow' } });
  });

  it('cancels when there is no turn and no options', async () => {
    const client = buildAcpClient('/wt', () => null);
    const res = await client.requestPermission(req([]));
    expect(res).toEqual({ outcome: { outcome: 'cancelled' } });
  });

  it('auto-selects when the turn has no taskId (UI routing gated off)', async () => {
    const turn: AcpTurnContext = { taskId: undefined, sessionId: 's', modeId: undefined, agent: 'goose', onSessionUpdate: () => {} };
    const client = buildAcpClient('/wt', () => turn);
    const res = await client.requestPermission(req([{ optionId: 'ok' }]));
    expect(res).toEqual({ outcome: { outcome: 'selected', optionId: 'ok' } });
  });
});

describe('buildAcpClient — elicitation fallback', () => {
  it('declines when there is no turn', async () => {
    const client = buildAcpClient('/wt', () => null);
    const res = await client.unstable_createElicitation!({} as CreateElicitationRequest);
    expect(res).toEqual({ action: 'decline' });
  });

  it('declines when the turn has no taskId', async () => {
    const turn: AcpTurnContext = { taskId: undefined, sessionId: 's', modeId: undefined, agent: 'goose', onSessionUpdate: () => {} };
    const client = buildAcpClient('/wt', () => turn);
    const res = await client.unstable_createElicitation!({} as CreateElicitationRequest);
    expect(res).toEqual({ action: 'decline' });
  });
});

describe('buildAcpClient — createTerminal', () => {
  it('returns the noop terminal id', async () => {
    const client = buildAcpClient('/wt', () => null);
    const res = await client.createTerminal!({} as never);
    expect(res).toEqual({ terminalId: 'noop' });
  });
});
