<template>
  <q-dialog v-model="preview" auto-close square>
    <q-card class="q-dialog-plugin">
      <q-img :src="image" fit="contain">
        <template #error>
          <div style="width: 100%; height: 100%" class="row justify-center">
            <img style="height: 100%; background: white" src="no-image.svg" />
          </div>
        </template>
      </q-img>
    </q-card>
  </q-dialog>
  <q-form @submit="save" @reset="reset">
    <q-card-section class="fit row justify-start content-center items-center">
      <q-input
        v-model="userModel.firstname"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Vorname"
        :rules="[notEmpty]"
        autocomplete="given-name"
        filled
      />
      <q-input
        v-model="userModel.lastname"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Nachname"
        :rules="[notEmpty]"
        autocomplete="family-name"
        filled
      />
      <q-input
        v-model="userModel.display_name"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Angezeigter Name"
        :rules="[notEmpty]"
        autocomplete="nickname"
        filled
      />
      <q-input
        v-model="userModel.mail"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="E-Mail"
        :rules="[isEmail, notEmpty]"
        autocomplete="email"
        filled
      />
      <q-input
        v-model="userModel.userid"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Benutzername"
        :readonly="!newUser"
        :rules="newUser ? [isFreeUID, notEmpty] : []"
        autocomplete="username"
        filled
      />
      <q-select
        v-model="userModel.roles"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Rollen"
        filled
        multiple
        use-chips
        :readonly="!canSetRoles"
        :options="allRoles"
        option-label="name"
        option-value="name"
      />
      <IsoDateInput
        v-model="userModel.birthday"
        class="col-xs-12 col-sm-6 q-pa-sm"
        label="Geburtstag"
        autocomplete="bday"
      />
      <q-file
        v-model="avatar"
        filled
        clearable
        label="Avatar"
        accept="image/*, .jpg, .png"
        class="col-xs-12 col-sm-6 q-pa-sm"
        @clear="avatar = undefined"
        @rejected="onAvatarRejected"
      >
        <template #before>
          <q-avatar @click="preview = true">
            <q-img
              style="min-width: 100%; min-height: 100%"
              :src="image"
              @error="hasAvatar = false"
            >
              <template #error><img style="width: 100%" src="no-image.svg" /></template
            ></q-img>
          </q-avatar>
        </template>
        <template #append>
          <q-icon
            v-if="!avatar && hasAvatar"
            :name="deleteAvatar ? 'mdi-delete-restore' : 'mdi-delete'"
            class="cursor-pointer"
            @click="deleteAvatar = !deleteAvatar"
          />
        </template>
      </q-file>
    </q-card-section>
    <q-separator v-if="!newUser" />
    <q-card-section
      v-if="!newUser"
      class="fit row justify-start content-center items-center q-gutter-y-md"
    >
      <PasswordInput
        v-if="isCurrentUser"
        v-model="password"
        :rules="[notEmpty]"
        filled
        label="Passwort"
        autocomplete="current-password"
        class="col-xs-12 col-sm-6 q-pa-sm"
        hint="Passwort muss immer eingetragen werden"
      />
      <PasswordInput
        v-model="newPassword"
        filled
        label="Neues Password"
        autocomplete="new-password"
        class="col-xs-12 col-sm-6 q-pa-sm"
      />
    </q-card-section>
    <q-card-actions align="right">
      <q-btn label="Reset" type="reset" />
      <q-btn color="primary" type="submit" label="Speichern" />
    </q-card-actions>
  </q-form>
</template>

<script lang="ts">
import { Notify } from 'quasar';
import { IsoDateInput, PasswordInput } from '@flaschengeist/api/components';
import { defineComponent, computed, ref, onBeforeMount, PropType, watchEffect } from 'vue';
import {
  avatarURL,
  hasPermission,
  notEmpty,
  isEmail,
  useMainStore,
  useUserStore,
  isAxiosError,
} from '@flaschengeist/api';
import { PERMISSIONS } from '../../permissions';

export default defineComponent({
  name: 'MainUserSettings',
  components: { IsoDateInput, PasswordInput },
  props: {
    user: {
      required: true,
      type: Object as PropType<FG.User>,
    },
    newUser: { type: Boolean, default: false },
  },
  emits: {
    'update:user': (payload: FG.User) => !!payload,
  },
  setup(props, { emit }) {
    const userStore = useUserStore();
    const mainStore = useMainStore();

    onBeforeMount(() => {
      void userStore.getRoles(false);
    });

    const preview = ref(false);
    const password = ref('');
    const newPassword = ref('');
    const avatar = ref<File>();
    const userModel = ref(Object.assign({}, props.user));
    const previewURL = ref<string>(); //< Preview of an avatar as data URL

    const allRoles = computed(() => userStore.roles.map((role) => role.name));

    const canSetRoles = computed(() => hasPermission(PERMISSIONS.SET_ROLES));
    const isCurrentUser = computed(() => userModel.value.userid === mainStore.currentUser.userid);

    const deleteAvatar = ref(false);
    const hasAvatar = ref(true);

    /* Reset model if props changed */
    watchEffect(() => {
      if (props.user.userid && props.user.userid !== userModel.value.userid) reset();
    });

    watchEffect(() => {
      if (avatar.value && avatar.value instanceof File) {
        let reader = new FileReader();

        reader.onload = (e) => {
          previewURL.value = <string | undefined>(e.target?.result || undefined);
        };

        reader.readAsDataURL(avatar.value);
      } else previewURL.value = undefined;
    });

    /**
     * Notify user is file does not fulfil the requirements
     */
    function onAvatarRejected() {
      Notify.create({
        group: false,
        type: 'negative',
        message: 'Datei zu groß oder keine gültige Bilddatei.',
        timeout: 10000,
        progress: true,
        actions: [{ icon: 'mdi-close', color: 'white' }],
      });
      avatar.value = new File([], '', {});
    }

    /**
     * Save changes, emit user model, directly uploads / deletes avatar by using the API
     */
    async function save() {
      let changed: FG.User = userModel.value;
      if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday);
      changed = Object.assign(changed, {
        password: password.value,
      });
      if (newPassword.value != '') {
        changed = Object.assign(changed, {
          new_password: newPassword.value,
        });
      }

      emit('update:user', changed);

      if (avatar.value !== undefined && avatar.value.name) {
        await userStore
          .uploadAvatar(changed, avatar.value)
          .catch((response) => isAxiosError(response, 400) && onAvatarRejected());
        hasAvatar.value = true;
      } else if (deleteAvatar.value) {
        await userStore.deleteAvatar(changed);
        hasAvatar.value = false;
      }
      password.value = '';
    }

    function reset() {
      userModel.value = Object.assign({}, props.user);
      password.value = '';
      newPassword.value = '';
      avatar.value = undefined;
      previewURL.value = undefined;
      deleteAvatar.value = false;
    }

    function isFreeUID(val: string) {
      return (
        userStore.users.findIndex((user) => user.userid === val) === -1 ||
        'Benutzername ist schon vergeben'
      );
    }

    const image = computed(() => {
      if (previewURL.value) {
        return previewURL.value;
      }
      if (!deleteAvatar.value) {
        return avatarURL(props.user);
      }
      return 'no-image.svg';
    });

    return {
      allRoles,
      avatar,
      canSetRoles,
      deleteAvatar,
      hasAvatar,
      image,
      isCurrentUser,
      isEmail,
      isFreeUID,
      newPassword,
      notEmpty,
      onAvatarRejected,
      password,
      preview,
      reset,
      save,
      userModel,
    };
  },
});
</script>
