/**
 * whoami op contract tests — pins the v0.28 transport-detection shape.
 *
 * The test surface is the op's handler called against synthesized
 * OperationContext rather than the full HTTP stack — keeps the test pure
 * and fast. End-to-end coverage (real HTTP MCP) lives in
 * test/e2e/serve-http-oauth.test.ts and test/e2e/sources-remote-mcp.test.ts.
 */

import { test, expect, describe } from 'bun:test';
import { operations, OperationError } from '../src/core/operations.ts';
import type { OperationContext, AuthInfo } from '../src/core/operations.ts';

const whoami = operations.find(o => o.name === 'whoami')!;

function ctxWith(overrides: Partial<OperationContext>): OperationContext {
  // Shape exposes only what whoami reads. Every required field gets a
  // safe stub; the test-relevant overrides come last to win.
  return {
    engine: {} as any,
    config: {} as any,
    logger: { info() {}, warn() {}, error() {} },
    dryRun: false,
    remote: true, // default for tests; specific cases override
    ...overrides,
  } as OperationContext;
}

describe('whoami op contract', () => {
  test('local transport (ctx.remote === false) returns empty scopes', async () => {
    const result = (await whoami.handler(
      ctxWith({ remote: false }),
      {},
    )) as any;
    expect(result.transport).toBe('local');
    expect(result.scopes).toEqual([]);
  });

  test('local transport ignores ctx.auth even if a stale value leaked through', async () => {
    // Defense in depth: even if some buggy transport set both remote=false
    // AND a stale auth blob, the local return shape stays explicit.
    const result = (await whoami.handler(
      ctxWith({
        remote: false,
        auth: {
          token: 'x',
          clientId: 'gbrain_cl_123',
          scopes: ['admin'],
          expiresAt: 999999,
        } as AuthInfo,
      }),
      {},
    )) as any;
    expect(result.transport).toBe('local');
    expect(result.scopes).toEqual([]);
  });

  test('oauth transport returns client identity and exact source grants', async () => {
    const auth: AuthInfo = {
      token: 'gbrain_at_xxx',
      clientId: 'gbrain_cl_abc',
      clientName: 'gstack-test',
      scopes: ['read', 'sources_admin'],
      expiresAt: 1234567890,
      sourceId: 'hot-memory',
      allowedSources: ['hot-memory', 'canonical-brain'],
    };
    const result = (await whoami.handler(
      ctxWith({ remote: true, sourceId: 'transport-fallback', auth }),
      {},
    )) as any;
    expect(result).toEqual({
      transport: 'oauth',
      client_id: 'gbrain_cl_abc',
      client_name: 'gstack-test',
      scopes: ['read', 'sources_admin'],
      expires_at: 1234567890,
      source_id: 'hot-memory',
      federated_read: ['hot-memory', 'canonical-brain'],
    });
  });

  test('oauth transport uses fail-closed empty values when source grants are absent', async () => {
    const auth: AuthInfo = {
      token: 'gbrain_at_pre_migration',
      clientId: 'gbrain_cl_pre_migration',
      scopes: ['read'],
    };
    const result = (await whoami.handler(
      ctxWith({ remote: true, sourceId: 'transport-fallback', auth }),
      {},
    )) as any;
    expect(result.source_id).toBeNull();
    expect(result.federated_read).toEqual([]);
  });

  test('oauth transport preserves an explicit empty federated grant', async () => {
    const auth: AuthInfo = {
      token: 'gbrain_at_empty',
      clientId: 'gbrain_cl_empty',
      scopes: ['read', 'write'],
      sourceId: 'hot-memory',
      allowedSources: [],
    };
    const result = (await whoami.handler(
      ctxWith({ remote: true, auth }),
      {},
    )) as any;
    expect(result.source_id).toBe('hot-memory');
    expect(result.federated_read).toEqual([]);
  });

  test('oauth transport does not widen federated_read with the write source', async () => {
    const auth: AuthInfo = {
      token: 'gbrain_at_narrow',
      clientId: 'gbrain_cl_narrow',
      scopes: ['read', 'write'],
      sourceId: 'hot-memory',
      allowedSources: ['canonical-brain'],
    };
    const result = (await whoami.handler(
      ctxWith({ remote: true, auth }),
      {},
    )) as any;
    expect(result.source_id).toBe('hot-memory');
    expect(result.federated_read).toEqual(['canonical-brain']);
  });

  test('legacy transport (token name as clientId, no gbrain_cl_ prefix)', async () => {
    const auth: AuthInfo = {
      token: 'legacy-token',
      clientId: 'my-personal-token',
      clientName: 'my-personal-token',
      scopes: ['read', 'write', 'admin'],
      // Legacy tokens have a synthetic 1y expiry — whoami exposes null
      // since legacy tokens don't actually expire.
      expiresAt: 999999999,
    };
    const result = (await whoami.handler(
      ctxWith({ remote: true, auth }),
      {},
    )) as any;
    expect(result.transport).toBe('legacy');
    expect(result.token_name).toBe('my-personal-token');
    expect(result.scopes).toEqual(['read', 'write', 'admin']);
    expect(result.expires_at).toBeNull();
  });

  // #1061: stdio MCP is remote/untrusted by design but has no per-token auth
  // (local pipe). The stdio dispatch marks ctx.transport='stdio'; whoami
  // reports it instead of throwing unknown_transport.
  test('stdio transport (remote=true, no auth, transport marker) reports stdio', async () => {
    const result = (await whoami.handler(
      ctxWith({ remote: true, auth: undefined, transport: 'stdio' }),
      {},
    )) as any;
    expect(result.transport).toBe('stdio');
    expect(result.scopes).toEqual([]);
  });

  test('stdio marker does not mask real auth (auth still wins)', async () => {
    const result = (await whoami.handler(
      ctxWith({
        remote: true,
        transport: 'stdio',
        auth: {
          token: 'gbrain_at_xxx',
          clientId: 'gbrain_cl_abc',
          scopes: ['read'],
          expiresAt: 1,
        } as AuthInfo,
      }),
      {},
    )) as any;
    expect(result.transport).toBe('oauth');
  });

  // Q3: ambiguous transport — fail-closed. The footgun this guards against
  // is a future transport that lands without threading auth, where a buggy
  // caller might trust whoami's output to gate sensitive ops.
  test('unknown_transport throws when remote=true AND auth is missing', async () => {
    try {
      await whoami.handler(ctxWith({ remote: true, auth: undefined }), {});
      throw new Error('expected throw');
    } catch (e) {
      expect(e).toBeInstanceOf(OperationError);
      expect((e as OperationError).message).toMatch(/unknown_transport|did not thread/);
    }
  });

  test('unknown_transport throws when remote is undefined (cast bypass guard)', async () => {
    // F7b contract: ctx.remote is REQUIRED. If a caller widens the type to
    // Partial<> and passes through undefined, whoami should treat it as
    // remote (the fail-closed default) and throw because auth is missing.
    try {
      await whoami.handler(ctxWith({ remote: undefined as any, auth: undefined }), {});
      throw new Error('expected throw');
    } catch (e) {
      expect(e).toBeInstanceOf(OperationError);
    }
  });
});

describe('whoami op metadata', () => {
  test('description documents OAuth source grant fields', () => {
    expect(whoami.description).toContain('source_id');
    expect(whoami.description).toContain('federated_read');
  });

  test('scope is read (any authenticated caller can introspect itself)', () => {
    expect(whoami.scope).toBe('read');
  });

  test('not localOnly (must work over HTTP MCP for gstack /setup-gbrain)', () => {
    expect(whoami.localOnly).toBeFalsy();
  });

  test('mutating is false', () => {
    expect(whoami.mutating).toBeFalsy();
  });
});
