import { ConfidentialClientApplication, LogLevel } from '@azure/msal-node';
import { Module } from '@nestjs/common';
import { Logger } from 'nestjs-pino';
import { assertEntraConfig } from '../config/check-entra-config';
import { SessionModule } from '../session/session.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { ENTRA_CONFIG, type EntraConfig } from './entra-config.token';
import { MSAL_CLIENT } from './msal-client.token';
import { RequireMfaGuard } from './require-mfa.guard';
import { SessionEstablisher } from './session-establisher.service';

/**
 * Auth module — owns the Entra ID configuration, the MSAL Node
 * confidential client, and (in subsequent PRs) the OIDC routes, the
 * session integration, and the route guards. Per ADR-0009.
 *
 * v1 providers:
 *
 *   - `ENTRA_CONFIG` — the parsed, validated Entra app-registration
 *     config (PR #102).
 *   - `MSAL_CLIENT` — a `ConfidentialClientApplication` instance
 *     wired with that config plus a logger callback that forwards
 *     MSAL's internal log lines into the Pino stream so auth flow
 *     diagnostics land alongside the rest of the BFF logs (per
 *     ADR-0012). PII logging is disabled by default — MSAL won't
 *     include tokens or user identifiers in the messages it emits.
 *   - `AuthService` — first-leg-of-the-flow logic: PKCE + state
 *     generation, MSAL auth-code URL building. The controller
 *     stays a thin shell around it.
 *
 * v1 routes (per ADR-0009):
 *
 *   - `GET /api/auth/login` — 302 to Entra's authorize endpoint
 *     with the freshly-generated state + code challenge; sets a
 *     short-lived signed cookie carrying the state + PKCE
 *     verifier so the next-PR callback can verify the round-trip.
 *
 * The module stays non-global: modules state "I depend on auth" by
 * importing it. Re-exports both tokens so a single
 * `imports: [AuthModule]` is enough to consume either.
 */
@Module({
  imports: [SessionModule],
  controllers: [AuthController],
  providers: [
    AuthService,
    RequireMfaGuard,
    SessionEstablisher,
    {
      provide: ENTRA_CONFIG,
      useFactory: () => assertEntraConfig(),
    },
    {
      provide: MSAL_CLIENT,
      inject: [ENTRA_CONFIG, Logger],
      useFactory: (config: EntraConfig, logger: Logger) =>
        new ConfidentialClientApplication({
          auth: {
            clientId: config.clientId,
            authority: config.authority,
            clientSecret: config.clientSecret,
          },
          system: {
            loggerOptions: {
              piiLoggingEnabled: false,
              logLevel: LogLevel.Info,
              loggerCallback: (level, message) => {
                // MSAL levels: 0=Error, 1=Warning, 2=Info, 3=Verbose, 4=Trace.
                // Forward to Pino with the matching level. The whole
                // payload lives under the `msal` context key so a log
                // search filtering on `msal` returns every MSAL line
                // without false positives from app code.
                const ctx = 'msal';
                switch (level) {
                  case LogLevel.Error:
                    logger.error(message, ctx);
                    break;
                  case LogLevel.Warning:
                    logger.warn(message, ctx);
                    break;
                  case LogLevel.Verbose:
                  case LogLevel.Trace:
                    logger.debug(message, ctx);
                    break;
                  default:
                    logger.log(message, ctx);
                }
              },
            },
          },
        }),
    },
  ],
  exports: [ENTRA_CONFIG, MSAL_CLIENT, RequireMfaGuard, AuthService, SessionEstablisher],
})
export class AuthModule {}
