import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DefaultPassphraseGenerationOptions } from "@bitwarden/common/tools/generator/passphrase";
import {
  DefaultPasswordGenerationOptions,
  PasswordGenerationServiceAbstraction,
} from "@bitwarden/common/tools/generator/password";
import { PasswordGeneratorOptions } from "@bitwarden/common/tools/generator/password/password-generator-options";

import { Response } from "../models/response";
import { StringResponse } from "../models/response/string.response";
import { CliUtils } from "../utils";

export class GenerateCommand {
  constructor(
    private passwordGenerationService: PasswordGenerationServiceAbstraction,
    private stateService: StateService,
  ) {}

  async run(cmdOptions: Record<string, any>): Promise<Response> {
    const normalizedOptions = new Options(cmdOptions);
    const options: PasswordGeneratorOptions = {
      uppercase: normalizedOptions.uppercase,
      lowercase: normalizedOptions.lowercase,
      number: normalizedOptions.number,
      special: normalizedOptions.special,
      length: normalizedOptions.length,
      type: normalizedOptions.type,
      wordSeparator: normalizedOptions.separator,
      numWords: normalizedOptions.words,
      capitalize: normalizedOptions.capitalize,
      includeNumber: normalizedOptions.includeNumber,
      minNumber: normalizedOptions.minNumber,
      minSpecial: normalizedOptions.minSpecial,
      ambiguous: normalizedOptions.ambiguous,
    };

    const enforcedOptions = (await this.stateService.getIsAuthenticated())
      ? (await this.passwordGenerationService.enforcePasswordGeneratorPoliciesOnOptions(options))[0]
      : options;

    const password = await this.passwordGenerationService.generatePassword(enforcedOptions);
    const res = new StringResponse(password);
    return Response.success(res);
  }
}

class Options {
  uppercase: boolean;
  lowercase: boolean;
  number: boolean;
  special: boolean;
  length: number;
  type: "passphrase" | "password";
  separator: string;
  words: number;
  capitalize: boolean;
  includeNumber: boolean;
  minNumber: number;
  minSpecial: number;
  ambiguous: boolean;

  constructor(passedOptions: Record<string, any>) {
    this.uppercase = CliUtils.convertBooleanOption(passedOptions?.uppercase);
    this.lowercase = CliUtils.convertBooleanOption(passedOptions?.lowercase);
    this.number = CliUtils.convertBooleanOption(passedOptions?.number);
    this.special = CliUtils.convertBooleanOption(passedOptions?.special);
    this.capitalize = CliUtils.convertBooleanOption(passedOptions?.capitalize);
    this.includeNumber = CliUtils.convertBooleanOption(passedOptions?.includeNumber);
    this.ambiguous = CliUtils.convertBooleanOption(passedOptions?.ambiguous);
    this.length = CliUtils.convertNumberOption(
      passedOptions?.length,
      DefaultPasswordGenerationOptions.length,
    );
    this.type = passedOptions?.passphrase ? "passphrase" : "password";
    this.separator = CliUtils.convertStringOption(
      passedOptions?.separator,
      DefaultPassphraseGenerationOptions.wordSeparator,
    );
    this.words = CliUtils.convertNumberOption(
      passedOptions?.words,
      DefaultPassphraseGenerationOptions.numWords,
    );
    this.minNumber = CliUtils.convertNumberOption(
      passedOptions?.minNumber,
      DefaultPasswordGenerationOptions.minNumber,
    );
    this.minSpecial = CliUtils.convertNumberOption(
      passedOptions?.minSpecial,
      DefaultPasswordGenerationOptions.minSpecial,
    );

    if (!this.uppercase && !this.lowercase && !this.special && !this.number) {
      this.lowercase = true;
      this.uppercase = true;
      this.number = true;
    }
    if (this.length < 5) {
      this.length = 5;
    }
    if (this.words < 3) {
      this.words = 3;
    }
    if (this.separator === "space") {
      this.separator = " ";
    } else if (this.separator === "empty") {
      this.separator = "";
    } else if (this.separator != null && this.separator.length > 1) {
      this.separator = this.separator[0];
    }
  }
}
