import assert from 'assert';
import { BasicRepresentation } from '../../../../http/representation/BasicRepresentation';
import type { Representation } from '../../../../http/representation/Representation';
import { getLoggerFor } from '../../../../logging/LogUtil';
import { APPLICATION_JSON } from '../../../../util/ContentTypes';
import { readJsonStream } from '../../../../util/StreamUtil';
import { BaseInteractionHandler } from '../../BaseInteractionHandler';
import type { InteractionHandlerInput } from '../../InteractionHandler';
import { assertPassword } from '../EmailPasswordUtil';
import type { AccountStore } from '../storage/AccountStore';

const resetPasswordView = {
  required: {
    password: 'string',
    confirmPassword: 'string',
    recordId: 'string',
  },
} as const;

/**
 * Resets a password if a valid `recordId` is provided,
 * which should have been generated by a different handler.
 */
export class ResetPasswordHandler extends BaseInteractionHandler {
  protected readonly logger = getLoggerFor(this);

  private readonly accountStore: AccountStore;

  public constructor(accountStore: AccountStore) {
    super(resetPasswordView);
    this.accountStore = accountStore;
  }

  public async handlePost({ operation }: InteractionHandlerInput): Promise<Representation> {
    // Validate input data
    const { password, confirmPassword, recordId } = await readJsonStream(operation.body.data);
    assert(
      typeof recordId === 'string' && recordId.length > 0,
      'Invalid request. Open the link from your email again',
    );
    assertPassword(password, confirmPassword);

    await this.resetPassword(recordId, password);
    return new BasicRepresentation(JSON.stringify({}), operation.target, APPLICATION_JSON);
  }

  /**
   * Resets the password for the account associated with the given recordId.
   */
  private async resetPassword(recordId: string, newPassword: string): Promise<void> {
    const email = await this.accountStore.getForgotPasswordRecord(recordId);
    assert(email, 'This reset password link is no longer valid.');
    await this.accountStore.deleteForgotPasswordRecord(recordId);
    await this.accountStore.changePassword(email, newPassword);
    this.logger.debug(`Resetting password for user ${email}`);
  }
}
