import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
import { describe, expect, it, vi } from 'vitest';

import type { ServerDefinition } from '../src/config.js';
import { isUnauthorizedError, maybeEnableOAuth } from '../src/runtime-oauth-support.js';

const logger = {
  info: vi.fn(),
  warn: vi.fn(),
  error: vi.fn(),
};

describe('maybeEnableOAuth', () => {
  const baseDefinition: ServerDefinition = {
    name: 'adhoc-server',
    command: { kind: 'http', url: new URL('https://example.com/mcp') },
    source: { kind: 'local', path: '<adhoc>' },
  };

  it('returns an updated definition for ad-hoc HTTP servers', () => {
    const updated = maybeEnableOAuth(baseDefinition, logger as never);
    expect(updated).toBeDefined();
    expect(updated?.auth).toBe('oauth');
    expect(updated?.tokenCacheDir).toBeUndefined();
    expect(logger.info).toHaveBeenCalled();
  });

  it('enables OAuth for non-ad-hoc HTTP servers (issue #38)', () => {
    const def: ServerDefinition = {
      name: 'local-server',
      command: { kind: 'http', url: new URL('https://example.com') },
      source: { kind: 'local', path: '/tmp/config.json' },
    };
    const updated = maybeEnableOAuth(def, logger as never);
    expect(updated).toBeDefined();
    expect(updated?.auth).toBe('oauth');
  });

  it('does not mutate stdio servers', () => {
    const def: ServerDefinition = {
      name: 'stdio-server',
      command: { kind: 'stdio', command: 'echo', args: [], cwd: process.cwd() },
    };
    const updated = maybeEnableOAuth(def, logger as never);
    expect(updated).toBeUndefined();
  });

  it('does not re-promote servers already configured for oauth', () => {
    const def: ServerDefinition = {
      name: 'oauth-server',
      auth: 'oauth',
      command: { kind: 'http', url: new URL('https://example.com') },
    };
    const updated = maybeEnableOAuth(def, logger as never);
    expect(updated).toBeUndefined();
  });

  it('does not promote refreshable bearer servers to oauth', () => {
    const def: ServerDefinition = {
      name: 'refreshable-server',
      auth: 'refreshable_bearer',
      command: { kind: 'http', url: new URL('https://example.com') },
    };
    const updated = maybeEnableOAuth(def, logger as never);
    expect(updated).toBeUndefined();
  });

  it('promotes unsupported auth markers consistently with config normalization', () => {
    const def: ServerDefinition = {
      name: 'unsupported-auth-server',
      auth: 'bearer',
      command: { kind: 'http', url: new URL('https://example.com') },
    };
    const updated = maybeEnableOAuth(def, logger as never);
    expect(updated?.auth).toBe('oauth');
  });
});

describe('isUnauthorizedError helper', () => {
  it('matches UnauthorizedError instances', () => {
    const err = new UnauthorizedError('Unauthorized');
    expect(isUnauthorizedError(err)).toBe(true);
  });

  it('matches generic errors with 401 codes', () => {
    expect(isUnauthorizedError(new Error('SSE error: Non-200 status code (401)'))).toBe(true);
  });

  it('ignores unrelated errors', () => {
    expect(isUnauthorizedError(new Error('network timeout'))).toBe(false);
  });

  it('matches errors with code=401 even when message lacks 401', () => {
    const err = Object.assign(new Error('Error POSTing to endpoint: {}'), { code: 401 });
    expect(isUnauthorizedError(err)).toBe(true);
  });
});
