import type { Provider } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ExtractJwt, type StrategyOptionsWithRequest } from 'passport-jwt';
import { AUTHENTICATION_STRATEGY, type IAuthenticationStrategy } from '../../domain/ports';
import { resolveAuthenticationMode } from '../../domain/resolve-authentication-mode';
import { AuroraHubConsumerAdapter } from '../strategies/aurora-hub/aurora-hub-consumer.adapter';
import { LocalProviderAdapter } from '../strategies/local-provider/local-provider.adapter';

/**
 * Authentication strategy registry. To add a new strategy (e.g. an OIDC external consumer
 * for Phase 3 — Keycloak, Auth0, Azure AD), follow these four steps:
 *
 *   1. Create the adapter under `infrastructure/strategies/<name>/<name>.adapter.ts`
 *      implementing `IAuthenticationStrategy`. The adapter's `getPassportOptions()` is
 *      called once at boot when this strategy is selected; `validate(req, payload)` runs
 *      per request and must return the same `AuthenticatedAccount` shape that
 *      `AuroraHubConsumerAdapter` produces (downstream guards expect it).
 *   2. Add the adapter class to the `inject` array below.
 *   3. Add a new `if (mode === '<name>')` branch in `useFactory` before the throw.
 *   4. Add the value to `resolveAuthenticationMode`'s `VALID_MODES` list (controls the
 *      error message and validation).
 *
 * The adapter MUST be registered as a provider in `AuthenticationModule` (see the
 * `providers` array there). If the new adapter has cross-BC dependencies (e.g. an IAM
 * service), follow the `catalyst-cross-bounded-context-ports` skill: declare a port in
 * `domain/ports/`, an adapter under `@bridges/<supplier-bc>/`, and a bridge provider in
 * `BridgesModule`.
 */

/**
 * Dormant strategy bound under `AUTHENTICATION_STRATEGY` when `OAUTH_STRATEGY=none`.
 *
 * `getPassportOptions()` returns a minimal valid options object so `JwtPassportStrategy`
 * constructs without crashing. `validate()` is never invoked because `AuthenticationJwtGuard`
 * short-circuits before passport runs in none mode.
 */
const NONE_STRATEGY_DUMMY_SECRET = 'aurora-none-mode-dummy-secret-never-used';

const noneStrategy: IAuthenticationStrategy = {
  getPassportOptions(): StrategyOptionsWithRequest {
    return {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      passReqToCallback: true,
      secretOrKey: NONE_STRATEGY_DUMMY_SECRET,
    };
  },
  validate(): Promise<object> {
    // This method is NEVER called in none mode: the guard short-circuits before passport.
    // If somehow reached, fail loudly so the bug is visible immediately.
    throw new Error('noneStrategy.validate() must never be invoked');
  },
};

export const authenticationStrategyProvider: Provider = {
  provide: AUTHENTICATION_STRATEGY,
  useFactory: (
    configService: ConfigService,
    auroraHubAdapter: AuroraHubConsumerAdapter,
    localProviderAdapter: LocalProviderAdapter,
  ): IAuthenticationStrategy => {
    const mode = resolveAuthenticationMode(configService.get<string>('OAUTH_STRATEGY'));

    if (mode === 'none') {
      return noneStrategy;
    }

    if (mode === 'aurora-hub') {
      return auroraHubAdapter;
    }

    if (mode === 'local-provider') {
      return localProviderAdapter;
    }

    // resolveAuthenticationMode already throws for invalid values, but TypeScript
    // requires exhaustive handling of the union for the return type.
    const _exhaustive: never = mode;
    throw new Error(`Unhandled authentication mode: ${String(_exhaustive)}`);
  },
  inject: [ConfigService, AuroraHubConsumerAdapter, LocalProviderAdapter],
};
