import { Api } from '@aurorajs.dev/core-back';
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
import { createAnonymousAccount } from '../../domain/anonymous-account';
import { resolveAuthenticationMode } from '../../domain/resolve-authentication-mode';

@Injectable()
export class AuthenticationJwtGuard extends AuthGuard('jwt') {
  constructor(private readonly configService: ConfigService) {
    super();
  }

  override canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const mode = resolveAuthenticationMode(this.configService.get<string>('OAUTH_STRATEGY'));

    if (mode === 'none') {
      // Short-circuit: inject an anonymous account and allow the request through.
      // AuthorizationPermissionsGuard (unchanged) handles permission checks:
      // dPermissions.all=[] lets through public endpoints and rejects @Auth('x') with 403.
      const request = Api.getRequestFromExecutionContext<{ user?: unknown }>(context);
      request.user = createAnonymousAccount();
      return true;
    }

    // JWT modes: delegate to passport (the existing path is unchanged).
    return super.canActivate(context);
  }

  // override the getRequest() method for return request from graphql or rest api.
  getRequest<Request>(context: ExecutionContext): Request {
    return Api.getRequestFromExecutionContext(context);
  }

  // Account returned by jwt.strategy in validate method (JwtStrategy).
  // NOT weakened: passport rejection path is identical to before for JWT modes.
  handleRequest<AccountResponse>(err: unknown, user: AccountResponse): AccountResponse {
    if (err || !user) {
      throw (
        err ||
        new UnauthorizedException({
          statusCode: 401,
          message: 'OAuthAccessToken not allowed',
        })
      );
    }
    return user;
  }
}
