import { OAuthRevokeTokenHandler } from '@app/o-auth/flows';
import { UnsupportedMediaTypeException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { ThrottlerGuard } from '@nestjs/throttler';
import { OAuthRevokeTokenController } from './o-auth-revoke-token.controller';

const FORM_URLENCODED = 'application/x-www-form-urlencoded';

/**
 * Encodes a `clientId:clientSecret` pair as HTTP Basic auth header value (RFC 6749 §2.3.1).
 */
const basic = (clientId: string, clientSecret: string): string =>
  `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`;

describe('OAuthRevokeTokenController (transport — pure mapping)', () => {
  let controller: OAuthRevokeTokenController;
  let handler: { main: jest.Mock };

  const buildModule = async (): Promise<void> => {
    handler = { main: jest.fn().mockResolvedValue({ revoked: true }) };

    const module: TestingModule = await Test.createTestingModule({
      controllers: [OAuthRevokeTokenController],
      providers: [{ provide: OAuthRevokeTokenHandler, useValue: handler }],
    })
      // ThrottlerGuard is the only NestJS guard wired by the controller. Bypass it here —
      // rate-limit behavior is an integration concern, not a transport-mapping concern.
      .overrideGuard(ThrottlerGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get(OAuthRevokeTokenController);
  };

  describe('content-type enforcement', () => {
    it('throws UnsupportedMediaTypeException when content-type is missing', async () => {
      await buildModule();

      await expect(
        controller.main(basic('cli', 'sec'), undefined, { token: 't' }),
      ).rejects.toBeInstanceOf(UnsupportedMediaTypeException);
      expect(handler.main).not.toHaveBeenCalled();
    });

    it('throws UnsupportedMediaTypeException when content-type is application/json', async () => {
      await buildModule();

      await expect(
        controller.main(basic('cli', 'sec'), 'application/json', { token: 't' }),
      ).rejects.toBeInstanceOf(UnsupportedMediaTypeException);
    });

    it('accepts content-type with charset suffix', async () => {
      await buildModule();

      await controller.main(basic('cli', 'sec'), `${FORM_URLENCODED}; charset=utf-8`, {
        token: 't',
      });

      expect(handler.main).toHaveBeenCalledTimes(1);
    });
  });

  describe('Basic auth parsing', () => {
    it('decodes the Basic header into clientId + clientSecret', async () => {
      await buildModule();

      await controller.main(basic('aurora-cli', 'top-secret-12345'), FORM_URLENCODED, {
        token: 'opaque-refresh',
      });

      expect(handler.main).toHaveBeenCalledWith(
        expect.objectContaining({ clientId: 'aurora-cli', clientSecret: 'top-secret-12345' }),
      );
    });

    it('forwards EMPTY client credentials when Authorization header is missing', async () => {
      await buildModule();

      await controller.main(undefined, FORM_URLENCODED, { token: 't' });

      expect(handler.main).toHaveBeenCalledWith(
        expect.objectContaining({ clientId: '', clientSecret: '' }),
      );
    });

    it('forwards EMPTY client credentials when Authorization scheme is not Basic', async () => {
      await buildModule();

      await controller.main('Bearer some-token', FORM_URLENCODED, { token: 't' });

      expect(handler.main).toHaveBeenCalledWith(
        expect.objectContaining({ clientId: '', clientSecret: '' }),
      );
    });
  });

  describe('payload field extraction', () => {
    it('forwards the token and token_type_hint', async () => {
      await buildModule();

      await controller.main(basic('cli', 'sec'), FORM_URLENCODED, {
        token: 'opaque-refresh',
        token_type_hint: 'refresh_token',
      });

      expect(handler.main).toHaveBeenCalledWith(
        expect.objectContaining({ token: 'opaque-refresh', tokenTypeHint: 'refresh_token' }),
      );
    });

    it('forwards an EMPTY token and undefined hint when the body fields are missing', async () => {
      await buildModule();

      await controller.main(basic('cli', 'sec'), FORM_URLENCODED, {});

      expect(handler.main).toHaveBeenCalledWith(
        expect.objectContaining({ token: '', tokenTypeHint: undefined }),
      );
    });

    it('forwards an EMPTY token when the body field is not a string', async () => {
      await buildModule();

      await controller.main(basic('cli', 'sec'), FORM_URLENCODED, { token: 123 });

      expect(handler.main).toHaveBeenCalledWith(expect.objectContaining({ token: '' }));
    });
  });

  describe('response pass-through', () => {
    it('returns the handler response as-is', async () => {
      await buildModule();

      const result = await controller.main(basic('cli', 'sec'), FORM_URLENCODED, { token: 't' });

      expect(result).toEqual({ revoked: true });
    });
  });
});
