<template>
  <DialogFrame
    ref="dialog"
    :header-title="newUser ? $t('addNewUser') : 'Edit ' + localUser.user"
    :height="600"
    :width="500"
  >
    <q-form ref="form">
      <div class="row justify-center q-gutter-md">
        <q-input
          class="col-5 required"
          :label="$t('user')"
          filled
          :lazy-rules="false"
          :rules="[(val) => !!val || $t('userIsRequired')]"
          v-model="localUser.user"
          autofocus
        ></q-input>
        <q-input
          class="col-5 required"
          :label="$t('email')"
          filled
          :lazy-rules="false"
          :rules="[(val) => !!val || $t('emailIsRequired')]"
          v-model="localUser.email"
        ></q-input>
        <div class="col-5">
          <q-input
            class="col-5 required"
            :label="$t('password')"
            filled
            :lazy-rules="false"
            :rules="[validatePassword]"
            v-model="localUser.password"
            @update:model-value="checkStrength"
            :type="showPassword1 ? 'text' : 'password'"
          >
            <template v-slot:append>
              <q-btn
                flat
                dense
                :icon="showPassword1 ? 'visibility_off' : 'visibility'"
                @mousedown.prevent="showPassword1 = true"
                @mouseup.prevent="showPassword1 = false"
                @mouseleave.prevent="showPassword1 = false"
                @touchstart.prevent="showPassword1 = true"
                @touchend.prevent="showPassword1 = false"
                @touchcancel.prevent="showPassword1 = false"
              ></q-btn>
              <q-icon :name="strengthIcon" :color="strengthColor"></q-icon>
            </template>
          </q-input>

          <div class="q-mt-md q-px-xl">
            <q-linear-progress :value="strengthValue" :color="strengthColor" size="8px" rounded />
            <div class="text-caption text-center q-mt-xs">
              {{ strengthLabel }}
            </div>
          </div>
          <q-input
            class="col-5 required"
            :label="$t('password')"
            filled
            :type="showPassword2 ? 'text' : 'password'"
            :rules="[checkSamePassword]"
            v-model="passwordCheck"
          >
            <template v-slot:append>
              <q-btn
                flat
                dense
                :icon="showPassword2 ? 'visibility_off' : 'visibility'"
                @mousedown.prevent="showPassword2 = true"
                @mouseup.prevent="showPassword2 = false"
                @mouseleave.prevent="showPassword2 = false"
                @touchstart.prevent="showPassword2 = true"
                @touchend.prevent="showPassword2 = false"
                @touchcancel.prevent="showPassword2 = false"
              ></q-btn>
            </template>
          </q-input>
        </div>

        <div class="col-5">
          <q-select
            class="col-5 required"
            :label="$t('role')"
            filled
            :options="props.roles"
            :rules="[(val) => !!val || $t('roleIsRequired')]"
            v-model="localUser.role"
          ></q-select>
          <q-input
            class="col-5"
            :label="$t('expires')"
            filled
            v-model="localUser.expires"
          ></q-input>
        </div>
      </div>
    </q-form>
    <div class="row justify-center">
      <q-btn class="q-ma-md" color="primary" no-caps @click="save">{{ $t('save') }}</q-btn>
    </div>
  </DialogFrame>
</template>

<script setup lang="ts">
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
import { ref } from 'vue';
import zxcvbn from 'zxcvbn';
import { appApi } from 'src/boot/axios';
import type { User } from 'src/vueLib/models/users';
import { useNotify } from 'src/vueLib/general/useNotify';
import { validateQForm } from 'src/vueLib/utils/validation';
import { i18n } from 'src/boot/lang';
import { DefaultSettings } from 'src/vueLib/models/settings';

const { NotifyResponse } = useNotify();
const dialog = ref();
const form = ref();
const newUser = ref(false);
const showPassword1 = ref(false);
const showPassword2 = ref(false);
const passwordCheck = ref('');
const strengthValue = ref(0);
const strengthLabel = ref('Enter a password');
const strengthColor = ref('grey');
const strengthIcon = ref('lock');
const localUser = ref<User>({
  user: '',
  email: '',
  role: '',
  expires: '',
});

const props = defineProps({
  roles: {
    type: Array,
    required: true,
  },
});

const emit = defineEmits(['update']);

async function open(user: User | null) {
  if (user === undefined) {
    return;
  }

  if (user !== null) {
    localUser.value = { ...user };
    newUser.value = false;
  } else {
    localUser.value = {
      user: '',
      email: '',
      role: '',
      expires: '',
    };
    newUser.value = true;
  }

  await dialog.value?.open();
  await validateQForm(form.value);
}

function checkStrength() {
  const result = zxcvbn(localUser.value.password || '');
  strengthValue.value = (result.score + 1) / 5;
  const levels = [
    i18n.global.t('veryWeak'),
    i18n.global.t('weak'),
    i18n.global.t('fair'),
    i18n.global.t('good'),
    i18n.global.t('strong'),
  ];
  const colors = ['red', 'orange', 'yellow', 'light-green', 'green'];
  const icon = ['lock', 'warning', 'error_outline', 'check_circle_outline', 'verified_user'];

  strengthLabel.value = levels[result.score] || i18n.global.t('veryWeak');
  strengthColor.value = colors[result.score] || 'grey';
  strengthIcon.value = icon[result.score] || 'lock';
}

function validatePassword(): string | boolean {
  if (!localUser.value.password) return i18n.global.t('passwordIsRequired');

  if (localUser.value.password.length < 8) {
    return i18n.global.t('passwordTooShort');
  } else if (!/[A-Z]/.test(localUser.value.password)) {
    return i18n.global.t('passwordNeedsUppercase');
  } else if (!/[a-z]/.test(localUser.value.password)) {
    return i18n.global.t('passwordNeedsLowercase');
  } else if (!/[0-9]/.test(localUser.value.password)) {
    return i18n.global.t('passwordNeedsNumber');
  } else if (!/[!@#$%^&*(),.?":{}|<>]/.test(localUser.value.password)) {
    return i18n.global.t('passwordNeedsSpecial');
  }

  return true;
}

function checkSamePassword(): string | boolean {
  if (localUser.value.password === passwordCheck.value) return true;
  return i18n.global.t('passwordDoNotMatch');
}

async function save() {
  if (!(await validateQForm(form.value))) {
    NotifyResponse(i18n.global.t('notAllRequiredFieldsFilled'), 'error');
    return;
  }
  let query = 'users/edit?id=' + localUser.value.id;
  if (newUser.value) {
    query = 'users/add';
    localUser.value.settings = DefaultSettings();
  }

  appApi
    .post(query, JSON.stringify(localUser.value))
    .then(() => {
      emit('update');
      dialog.value.close();
    })
    .catch((err) => NotifyResponse(err, 'error'));
}

defineExpose({ open });
</script>

<style>
.required .q-field__label::after {
  content: ' *';
  color: red;
}
</style>
