/**
 * e2e: authentication none-mode boot test.
 *
 * Verifies:
 * - App boots with OAUTH_STRATEGY=none and NO .keys/ directory (no key file read).
 * - An endpoint with no declared permissions is reachable and returns 200 with an anonymous user.
 * - An endpoint protected with @Auth('x') returns 403 (anonymous account has dPermissions.all=[]).
 *
 * Uses a minimal NestJS application (no database, no Sequelize, no full AppModule) so the test
 * is isolated and fast. The AuroraHubConsumerAdapter is mocked to avoid the jwks-rsa ESM issue.
 */

// Mock ESM-importing module BEFORE imports
jest.mock(
  '../src/@aurora/modules/authentication/infrastructure/strategies/aurora-hub/aurora-hub-consumer.adapter',
  () => ({
    AuroraHubConsumerAdapter: class AuroraHubConsumerAdapterMock {
      getPassportOptions() {
        return { jwtFromRequest: () => null, secretOrKey: 'mock' };
      }
      validate(): Promise<Record<string, never>> {
        return Promise.resolve({});
      }
    },
  }),
);

import { CacheModule } from '@nestjs/cache-manager';
import { Controller, Get, INestApplication, Module, SetMetadata, UseGuards } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { Reflector } from '@nestjs/core';
import { PassportModule } from '@nestjs/passport';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { App } from 'supertest/types';
import { AuthenticationModule } from '../src/@aurora/modules/authentication/authentication.module';
import { ACCOUNT_LOADER } from '../src/@aurora/modules/authentication/domain/ports';
import {
  AuthenticationJwtGuard,
  AuthorizationPermissionsGuard,
} from '../src/@aurora/modules/authentication/infrastructure/guards';
import { LocalProviderAdapter } from '../src/@aurora/modules/authentication/infrastructure/strategies/local-provider/local-provider.adapter';

// ---------------------------------------------------------------------------
// Minimal test controllers
// ---------------------------------------------------------------------------

/** Public endpoint — no permissions declared. Should return 200 with anonymous user. */
@Controller()
class TestPublicController {
  @Get('public')
  getPublic(): { ok: boolean; username: string } {
    // In real production code the account would be injected via @CurrentAccount().
    // For this test we return a simple OK since we verify the HTTP status code.
    return { ok: true, username: 'anonymous' };
  }
}

/** Protected endpoint — @Auth-style guard applied. Should return 403 for anonymous user.
 *  @Auth is a ClassDecorator: it sets permissions metadata on the CLASS (not the method),
 *  which is how AuthorizationPermissionsGuard reads it via `context.getClass()`.
 */
@SetMetadata('permissions', ['test.protected.permission'])
@UseGuards(AuthenticationJwtGuard, AuthorizationPermissionsGuard)
@Controller()
class TestProtectedController {
  @Get('protected')
  getProtected(): { ok: boolean } {
    return { ok: true };
  }
}

// ---------------------------------------------------------------------------
// Minimal test module
// ---------------------------------------------------------------------------

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    CacheModule.register({ isGlobal: true }),
    PassportModule.register({ defaultStrategy: 'jwt' }),
    AuthenticationModule,
  ],
  controllers: [TestPublicController, TestProtectedController],
  providers: [Reflector, { provide: ACCOUNT_LOADER, useValue: { loadById: jest.fn() } }],
})
class TestAppModule {}

// ---------------------------------------------------------------------------
// e2e tests
// ---------------------------------------------------------------------------

describe('Authentication none-mode (e2e)', () => {
  let app: INestApplication<App>;

  beforeAll(async () => {
    // Ensure no OAUTH_STRATEGY key files are needed
    process.env.OAUTH_STRATEGY = 'none';

    const moduleRef = await Test.createTestingModule({
      imports: [TestAppModule],
    })
      .overrideProvider(ACCOUNT_LOADER)
      .useValue({ loadById: jest.fn() })
      // Override LocalProviderAdapter to avoid CACHE_MANAGER DI in test context
      .overrideProvider(LocalProviderAdapter)
      .useValue({
        getPassportOptions: () => ({ jwtFromRequest: () => null, secretOrKey: 'mock' }),
        validate: jest.fn(),
      })
      .compile();

    app = moduleRef.createNestApplication();
    await app.init();
  });

  afterAll(async () => {
    delete process.env.OAUTH_STRATEGY;
    await app.close();
  });

  it('boots successfully with OAUTH_STRATEGY=none and no key files', () => {
    // If we reach this point, the app booted — that is the assertion.
    expect(app).toBeDefined();
  });

  it('GET /public returns 200 (unprotected endpoint reachable anonymously)', async () => {
    await request(app.getHttpServer()).get('/public').expect(200);
  });

  it('GET /protected returns 401 with statusCode:403 in body (anonymous has no permissions)', async () => {
    // AuthorizationPermissionsGuard throws UnauthorizedException (HTTP 401) with body statusCode:403.
    // The HTTP status is 401 because NestJS maps UnauthorizedException → 401.
    // The body carries statusCode:403 to signal permission denial vs. missing authentication.
    const res = await request(app.getHttpServer()).get('/protected').expect(401);
    expect(res.body.statusCode).toBe(403);
  });
});
