import { OAuthRevokeTokenInput } from '@api/graphql';
import { OAuthRevokeTokenHandler } from '@app/o-auth/flows';
import { oAuthParseBasicAuth } from '@app/o-auth/shared';
import { GqlThrottlerGuard } from '@aurora/modules';
import { Injectable, UseGuards } from '@nestjs/common';
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
import { Throttle } from '@nestjs/throttler';
import type { Request } from 'express';

const THROTTLE_LIMIT = Number(process.env.THROTTLE_OAUTH_TOKEN_LIMIT ?? 5);
const THROTTLE_TTL_MS = Number(process.env.THROTTLE_OAUTH_TOKEN_TTL_MS ?? 60_000);

/**
 * GraphQL twin of `POST /api/o-auth/revoke` (mutation: `oAuthRevokeToken`). Pure transport:
 *   - Decodes HTTP Basic client credentials from the `Authorization` header (RFC 6749 §2.3.1 —
 *     same wire encoding as the REST endpoint; the Aurora frontend attaches it per-operation).
 *   - Maps the typed payload to the canonical handler input.
 *
 * ALL business logic — client authentication, presence validation, revocation — lives in
 * `OAuthRevokeTokenHandler`. Per RFC 7009 §2.2 this always resolves `true` (idempotent,
 * non-enumerable); only an unauthenticated client (`invalid_client`) or a missing token
 * (`invalid_request`) produce an error, raised by the handler.
 */
@Injectable()
@Resolver()
export class OAuthRevokeTokenResolver {
  constructor(private readonly handler: OAuthRevokeTokenHandler) {}

  @Mutation('oAuthRevokeToken')
  // Rate-limited at parity with the REST `POST /api/o-auth/revoke` endpoint (same env-driven
  // budget as token issuance) so the GraphQL transport is not an unthrottled probing vector.
  @UseGuards(GqlThrottlerGuard)
  @Throttle({ default: { limit: THROTTLE_LIMIT, ttl: THROTTLE_TTL_MS } })
  async oAuthRevokeToken(
    @Args('payload') payload: OAuthRevokeTokenInput,
    @Context() context: { req: Request },
  ): Promise<boolean> {
    const authorization = extractAuthorizationHeader(context.req);
    const { clientId, clientSecret } = oAuthParseBasicAuth(authorization);

    const response = await this.handler.main({
      clientId,
      clientSecret,
      token: payload.token?.trim() ?? '',
      tokenTypeHint: payload.tokenTypeHint?.trim() || undefined,
    });

    return response.revoked;
  }
}

function extractAuthorizationHeader(req: Request | undefined): string | undefined {
  const header = req?.headers?.authorization;
  return typeof header === 'string' ? header : undefined;
}
