import { OAuthSequelizeAccessTokenRepository } from '@app/o-auth/access-token';
import { OAuthFindApplicationService } from '@app/o-auth/application';
import { OAuthSequelizeRefreshTokenRepository } from '@app/o-auth/refresh-token';
import { oAuthAuthenticateApplication } from '@app/o-auth/shared';
import { Injectable } from '@nestjs/common';
import type { OAuthRevokeTokenInput, OAuthRevokeTokenResponse } from '../../../domain/flows.types';

@Injectable()
export class OAuthRevokeTokenService {
  constructor(
    private readonly findApplication: OAuthFindApplicationService,
    private readonly refreshTokenRepo: OAuthSequelizeRefreshTokenRepository,
    private readonly accessTokenRepo: OAuthSequelizeAccessTokenRepository,
  ) {}

  async main(payload: OAuthRevokeTokenInput): Promise<OAuthRevokeTokenResponse> {
    // 1. Authenticate the client (RFC 7009 §2.1). invalid_client on bad/unknown credentials.
    await oAuthAuthenticateApplication(this.findApplication, {
      clientId: payload.clientId,
      clientSecret: payload.clientSecret,
    });

    // 2. Look up the refresh token. RFC 7009 §2.2 + RFC 6819 §5.3.4: unknown or already-revoked
    // tokens are a successful no-op — never reveal whether the token existed.
    const refreshRow = await this.findRefreshToken(payload.token);
    if (refreshRow && !refreshRow.isRevoked) {
      // Revoke the refresh token AND its paired access token (mirror the rotation in
      // o-auth-create-token-refresh.service.ts). The access flag is audit/consistency only —
      // access JWTs are validated statelessly and are NOT checked against isRevoked.
      await this.refreshTokenRepo.repository.update(
        { isRevoked: true },
        { where: { id: refreshRow.id } },
      );
      await this.accessTokenRepo.repository.update(
        { isRevoked: true },
        { where: { id: refreshRow.accessTokenId } },
      );
    }

    return { revoked: true };
  }

  /** Find a refresh token row by its plaintext token value. Returns null when not found. */
  private async findRefreshToken(token: string): Promise<RefreshTokenRow | null> {
    const row = (await this.refreshTokenRepo.repository.findOne({
      where: { token },
    })) as unknown as RefreshTokenRow | null;
    return row;
  }
}

interface RefreshTokenRow {
  id: string;
  accessTokenId: string;
  isRevoked: boolean;
}
