<template>
  <v-dialog
    ref="dialog"
    :model-value="visible"
    persistent
    max-width="500"
    class="p-dialog modal-dialog p-settings-passcode"
    @keydown.esc.exact="close"
    @after-enter="afterEnter"
    @after-leave="afterLeave"
  >
    <v-form ref="form" validate-on="invalid-input" accept-charset="UTF-8" class="form-password" tabindex="-1" @submit.prevent>
      <v-card>
        <v-card-title class="d-flex justify-start align-center ga-3">
          <v-icon v-if="page === 'setup'" size="28" color="primary">mdi-shield-alert</v-icon>
          <v-icon v-else-if="page === 'deactivate'" size="28" color="primary">mdi-shield-check</v-icon>
          <v-icon v-else size="28" color="primary">mdi-cog</v-icon>
          <h6 class="text-h6">{{ $gettext(`2-Factor Authentication`) }}</h6>
        </v-card-title>
        <!-- Setup -->
        <template v-if="page === 'setup'">
          <v-card-text class="dense">
            <v-row align="start" dense>
              <v-col cols="12" class="text-subtitle-2">
                {{
                  $gettext(
                    `After entering your password for confirmation, you can set up two-factor authentication with a compatible authenticator app or device:`
                  )
                }}
              </v-col>
              <v-col cols="12">
                <v-text-field
                  v-model="password"
                  :disabled="busy"
                  :type="showPassword ? 'text' : 'password'"
                  :placeholder="$gettext('Password')"
                  autofocus
                  name="password"
                  hide-details
                  autocorrect="off"
                  autocapitalize="none"
                  autocomplete="current-password"
                  prepend-inner-icon="mdi-lock"
                  :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
                  class="input-password text-selectable"
                  @click:append-inner="showPassword = !showPassword"
                  @keyup.enter="onSetup"
                ></v-text-field>
              </v-col>
              <v-col cols="12" class="text-body-2">
                {{
                  $gettext(
                    `Enabling two-factor authentication means that you will need a randomly generated verification code to log in, so even if someone gains access to your password, they will not be able to access your account.`
                  )
                }}
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions class="action-buttons">
            <v-btn variant="flat" color="button" class="action-close" @click.stop="close">
              {{ $gettext(`Close`) }}
            </v-btn>
            <v-btn variant="flat" color="highlight" class="action-setup" :disabled="setupDisabled()" @click.stop="onSetup">
              {{ $gettext(`Setup`) }}
            </v-btn>
          </v-card-actions>
        </template>
        <!-- Confirm -->
        <template v-else-if="page === 'confirm'">
          <v-card-text class="dense">
            <v-row dense>
              <v-col cols="12" class="text-body-2 text-center">
                {{ $gettext(`Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:`) }}
              </v-col>
              <v-col cols="12">
                <v-img :src="key.QRCode" :max-height="340" alt="QR Code" class="my-1"></v-img>
              </v-col>
              <v-col cols="12" class="text-body-2 text-center">
                <span class="text-monospace cursor-copy" @click.stop.prevent="$util.copyText(key.Secret)">{{ key.Secret }}</span>
              </v-col>
              <v-col cols="12" class="text-body-2 text-center pb-0">
                {{ $gettext(`Enter the code generated by your authenticator app:`) }}
              </v-col>
              <v-col cols="12">
                <v-otp-input
                  ref="otp"
                  v-model="code"
                  :disabled="busy"
                  :length="6"
                  name="one-time-code"
                  type="number"
                  :label="$gettext('Verification Code')"
                  class="input-code"
                  @keyup.enter="onConfirm"
                ></v-otp-input>
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions class="action-buttons">
            <v-btn variant="flat" color="button" class="action-cancel" @click.stop="close">
              {{ $gettext(`Cancel`) }}
            </v-btn>
            <v-btn variant="flat" color="highlight" class="action-confirm" :disabled="code.length !== 6" @click.stop="onConfirm">
              {{ $gettext(`Confirm`) }}
            </v-btn>
          </v-card-actions>
        </template>
        <!-- Activate -->
        <template v-else-if="page === 'activate'">
          <v-card-text class="dense">
            <v-row align="start" dense>
              <v-col cols="12" class="text-body-2">
                {{
                  $gettext(
                    `Use the following recovery code to access your account when you are unable to generate a valid verification code with your authenticator app:`
                  )
                }}
              </v-col>
              <v-col cols="12">
                <v-text-field
                  :model-value="key.RecoveryCode"
                  type="text"
                  mask="nnn nnn nnn nnn"
                  hide-details
                  readonly
                  autocorrect="off"
                  autocapitalize="none"
                  autocomplete="off"
                  append-inner-icon="mdi-content-copy"
                  class="input-recoverycode cursor-copy text-monospace"
                  @click.stop="onCopyRecoveryCode"
                ></v-text-field>
              </v-col>
              <v-col cols="12" class="text-subtitle-2">
                {{ $gettext(`To avoid being locked out of your account, please download, print or copy this recovery code now and keep it in a safe place.`) }}
                {{ $gettext(`It is a one-time use code that will disable 2FA for your account when you use it.`) }}
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions class="action-buttons">
            <v-btn variant="flat" color="button" class="action-cancel" @click.stop="close">
              {{ $gettext(`Cancel`) }}
            </v-btn>
            <v-btn v-if="recoveryCodeCopied" variant="flat" color="highlight" class="action-activate" @click.stop="onActivate">
              {{ $gettext(`Activate`) }}
            </v-btn>
            <v-btn v-else variant="flat" color="highlight" class="action-copy" @click.stop="onCopyRecoveryCode">
              {{ $gettext(`Copy`) }}
            </v-btn>
          </v-card-actions>
        </template>
        <!-- Deactivate -->
        <template v-else-if="page === 'deactivate'">
          <v-card-text class="dense">
            <v-row align="start" dense>
              <v-col cols="12" class="text-subtitle-2">
                {{ $gettext(`Two-factor authentication has been enabled for your account.`) }}
              </v-col>
              <v-col cols="12" class="text-body-2">
                {{ $gettext(`If you lose access to your authenticator app or device, you can use your recovery code to regain access to your account.`) }}
                {{ $gettext(`It is a one-time use code that will disable 2FA for your account when you use it.`) }}
              </v-col>
              <v-col cols="12" class="text-body-2">
                {{ $gettext(`To switch to a new authenticator app or device, first deactivate two-factor authentication and then reactivate it:`) }}
              </v-col>
              <v-col cols="12">
                <v-text-field
                  v-model="password"
                  :disabled="busy"
                  :type="showPassword ? 'text' : 'password'"
                  :placeholder="$gettext('Password')"
                  autofocus
                  name="password"
                  hide-details
                  autocorrect="off"
                  autocapitalize="none"
                  autocomplete="current-password"
                  class="input-password text-selectable"
                  prepend-inner-icon="mdi-lock"
                  :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
                  @click:append-inner="showPassword = !showPassword"
                  @keyup.enter="onDeactivate"
                ></v-text-field>
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions class="action-buttons">
            <v-btn variant="flat" color="highlight" class="action-deactivate" :disabled="setupDisabled()" @click.stop="onDeactivate">
              {{ $gettext(`Deactivate`) }}
            </v-btn>
            <v-btn variant="flat" color="button" class="action-close" @click.stop="close">
              {{ $gettext(`Close`) }}
            </v-btn>
          </v-card-actions>
        </template>
        <!-- Not Available -->
        <template v-else-if="page === 'not_available'">
          <v-card-text class="dense">
            <v-row align="start" dense>
              <v-col cols="12" class="text-body-2">
                {{ $gettext(`Only locally managed accounts can be set up for authentication with 2FA.`) }}
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions class="action-buttons">
            <v-btn variant="flat" color="button" class="action-close" @click.stop="close">
              {{ $gettext(`Close`) }}
            </v-btn>
          </v-card-actions>
        </template>
      </v-card>
    </v-form>
  </v-dialog>
</template>
<script>
export default {
  name: "PSettingsPasscode",
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    model: {
      type: Object,
      default: () => this.$session.getUser(),
    },
  },
  emits: ["updateUser", "close"],
  data() {
    return {
      busy: false,
      isDemo: this.$config.get("demo"),
      isPublic: this.$config.get("public"),
      code: "",
      recoveryCodeCopied: false,
      password: "",
      showPassword: false,
      session: this.$session,
      minLength: this.$config.get("passwordLength"),
      maxLength: 72,
      rtl: this.$isRtl,
      key: {},
    };
  },
  computed: {
    page() {
      if (!this.session.hasPassword()) {
        return "not_available";
      } else if (this.model?.AuthMethod === "2fa") {
        return "deactivate";
      } else if (this.key?.Type === "totp") {
        if (!this.key?.VerifiedAt) {
          return "confirm";
        } else if (!this.key?.ActivatedAt) {
          return "activate";
        } else {
          return "deactivate";
        }
      }

      return "setup";
    },
  },
  watch: {
    visible: function (show) {
      if (show) {
        this.reset();
      }
    },
  },
  created() {
    if (this.isPublic && !this.isDemo) {
      this.$emit("close");
    }
  },
  methods: {
    afterEnter() {
      this.$view.enter(this, this.$refs.form, ".input-password input");
    },
    afterLeave() {
      this.$view.leave(this);
    },
    reset() {
      this.code = "";
      this.password = "";
      this.showPassword = false;
      this.recoveryCodeCopied = false;
      this.updateUser();
    },
    updateUser() {
      this.$emit("updateUser");
    },
    setupDisabled() {
      return this.isDemo || this.busy || this.password.length < this.minLength;
    },
    onSetup() {
      if (this.busy || this.password === "") {
        return;
      }
      this.busy = true;
      this.model
        .createPasscode(this.password)
        .then((resp) => {
          this.key = resp;
          this.busy = false;
          this.$nextTick(() => {
            this.$view.focus(this.$refs?.otp);
          });
        })
        .catch(() => {
          this.busy = false;
        });
    },
    onConfirm() {
      if (this.busy || this.code === "") {
        return;
      }
      this.busy = true;
      this.model
        .confirmPasscode(this.code)
        .then((resp) => {
          this.key = resp;
          this.$notify.success(this.$gettext("Successfully verified"));
        })
        .finally(() => {
          this.busy = false;
          this.code = "";
          this.password = "";
          this.showPassword = false;
          this.recoveryCodeCopied = false;
        });
    },
    onCopyRecoveryCode() {
      // Use the browser API to copy the recovery code to the clipboard.
      this.$util.copyText(this.key.RecoveryCode);

      // Flag the code as copied to the clipboard even
      // if the copyText() function returns an error.
      this.recoveryCodeCopied = true;
    },
    onActivate() {
      if (this.busy) {
        return;
      }

      this.busy = true;
      this.model
        .activatePasscode()
        .then((resp) => {
          this.key = resp;
          this.$notify.success(this.$gettext("Successfully activated"));
        })
        .finally(() => {
          this.busy = false;
          this.reset();
        });
    },
    onDeactivate() {
      if (this.busy || this.password === "") {
        return;
      }
      this.busy = true;
      this.model
        .deactivatePasscode(this.password)
        .then(() => {
          this.$notify.success(this.$gettext("Settings saved"));
          this.reset();
          this.key = {};
        })
        .finally(() => {
          this.busy = false;
        });
    },
    close() {
      if (this.busy) {
        return;
      }

      this.$emit("close");
    },
  },
};
</script>
