<script setup>

import { useI18n } from 'vue-i18n';
const { t } = useI18n();

import { ref, onMounted, useTemplateRef, inject } from 'vue';
import { Button, SingleSelect, Dialog, InputDialog, TextInput, InputGroup, FormGroup } from '@cloudron/pankow';
import { setLanguage } from '../i18n.js';
import { TOKEN_TYPES } from '../constants.js';
import NotificationSettings from '../components/NotificationSettings.vue';
import AppPasswords from '../components/AppPasswords.vue';
import SettingsItem from '../components/SettingsItem.vue';
import EditableField from '../components/EditableField.vue';
import PrimaryEmailDialog from '../components/dialogs/PrimaryEmailDialog.vue';
import FallbackEmailDialog from '../components/dialogs/FallbackEmailDialog.vue';
import PasswordChangeDialog from '../components/dialogs/PasswordChangeDialog.vue';
import DisableTwoFADialog from '../components/dialogs/DisableTwoFADialog.vue';
import Section from '../components/Section.vue';
import ApiTokens from '../components/ApiTokens.vue';
import ImagePicker from '../components/ImagePicker.vue';

import DashboardModel from '../models/DashboardModel.js';
import ProfileModel from '../models/ProfileModel.js';
import CloudronModel from '../models/CloudronModel.js';
import TokensModel from '../models/TokensModel.js';

const dashboardModel = DashboardModel.create();
const profileModel = ProfileModel.create();
const cloudronModel = CloudronModel.create();
const tokensModel = TokensModel.create();

const profile = inject('profile');
const refreshProfile = inject('refreshProfile');

const savingDisplayName = ref(false);

const config = ref({});
const inputDialog = useTemplateRef('inputDialog');
const primaryEmailDialog = useTemplateRef('primaryEmailDialog');
const fallbackEmailDialog = useTemplateRef('fallbackEmailDialog');
const passwordChangeDialog = useTemplateRef('passwordChangeDialog');
const disableTwoFADialog = useTemplateRef('disableTwoFADialog');

// Language selector
const languages = ref([]);
const language = ref('');
async function onSelectLanguage(lang) {
  const error = await profileModel.setLanguage(lang);
  if (error) return console.error('Failed to set language', error);

  await setLanguage(lang, true);
}

// Profile edits
async function onDisplayNameSave(newName) {
  savingDisplayName.value = true;

  const error = await profileModel.setDisplayName(newName);
  savingDisplayName.value = false;

  if (error) return console.error('Failed to set displayName', error);

  profile.value.displayName = newName;
}

function onChangeEmail(currentEmail) {
  primaryEmailDialog.value.open(currentEmail);
}

async function onChangeFallbackEmail(currentFallbackEmail) {
  fallbackEmailDialog.value.open(currentFallbackEmail);
}

async function onAvatarSubmit(file) {
  await profileModel.setAvatar(file);
  await refreshProfile();
}

async function onAvatarUnset() {
  await profileModel.unsetAvatar();
  await refreshProfile();
}

async function onPasswordChange() {
  passwordChangeDialog.value.open();
}


// Tokens
const webadminTokens = ref([]);
const cliTokens = ref([]);
const revokeTokensBusy = ref(false);

async function onRevokeAllWebAndCliTokens() {
  revokeTokensBusy.value = true;

  // filter current access token to be able to logout still
  const tokens = webadminTokens.value.concat(cliTokens.value).filter(t => t.accessToken !== localStorage.token);
  for (const token of tokens) {
    const [error] = await tokensModel.remove(token.id);
    if (error) console.error(error);
  }

  await profileModel.logout();
}


// 2fa
const mandatory2FAHelp = ref('');
const twoFASecret = ref('');
const twoFATotpToken = ref('');
const twoFAQRCode = ref('');
const twoFAEnableError = ref('');
const twoFADialog = useTemplateRef('twoFADialog');

async function onOpenTwoFASetupDialog() {
  const [error, result] = await profileModel.setTwoFASecret();
  if (error) return console.error(error);

  twoFAEnableError.value = '';
  twoFATotpToken.value = '';
  twoFASecret.value = result.secret;
  twoFAQRCode.value = result.qrcode;

  twoFADialog.value.open();
}

async function onTwoFAEnable() {
  const [error] = await profileModel.enableTwoFA(twoFATotpToken.value);
  if (error) {
    twoFATotpToken.value = '';
    return twoFAEnableError.value = error.body ? error.body.message : 'Internal error';
  }

  await refreshProfile();

  twoFADialog.value.close();
}

async function onTwoFADisable() {
  disableTwoFADialog.value.open();
}

// Init
onMounted(async () => {
  let [error, result] = await dashboardModel.config();
  if (error) return console.error(error);
  config.value = result;

  await refreshProfile();

  [error, result] = await cloudronModel.languages();
  languages.value = result.map(l => {
    return {
      id: l,
      display: t(`lang.${l}`, l /* default fallback */, { locale: 'en' })
    };
  }).sort((a, b) => {
    return a.display.localeCompare(b.display);
  });

  const usedLang = window.localStorage.NG_TRANSLATE_LANG_KEY || 'en';
  language.value = languages.value.find(l => l.id === usedLang).id;

  [error, result] = await tokensModel.list();
  if (error) return console.error(error);

  // dashboard and development clientIds were issued with 7.5.0
  webadminTokens.value = result.filter(function (c) { return c.clientId === TOKEN_TYPES.ID_WEBADMIN || c.clientId === TOKEN_TYPES.ID_DEVELOPMENT || c.clientId === 'dashboard' || c.clientId === 'development'; });
  cliTokens.value = result.filter(function (c) { return c.clientId === TOKEN_TYPES.ID_CLI; });

  // check if we should show the 2fa setup
  if (window.location.hash.indexOf('setup2fa') !== -1) onOpenTwoFASetupDialog();
});

</script>

<template>
  <div class="content">
    <InputDialog ref="inputDialog" />
    <PrimaryEmailDialog ref="primaryEmailDialog" @success="refreshProfile"/>
    <FallbackEmailDialog ref="fallbackEmailDialog" @success="refreshProfile"/>
    <PasswordChangeDialog ref="passwordChangeDialog" @success="refreshProfile"/>
    <DisableTwoFADialog ref="disableTwoFADialog" @success="refreshProfile"/>

    <Dialog ref="twoFADialog" :title="$t('profile.enable2FA.title')">
      <div>
        <p v-show="mandatory2FAHelp">{{ $t('profile.enable2FA.description') }}</p>
        <p v-html="$t('profile.enable2FA.authenticatorAppDescription', { googleAuthenticatorPlayStoreLink: 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2', googleAuthenticatorITunesLink: 'https://itunes.apple.com/us/app/google-authenticator/id388497605', freeOTPPlayStoreLink: 'https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp', freeOTPITunesLink: 'https://itunes.apple.com/us/app/freeotp-authenticator/id872559395'})"></p>
        <div style="text-align: center;">
          <img :src="twoFAQRCode" style="border-radius: 10px; margin-bottom: 10px"/>
          <small>{{ twoFASecret }}</small>
        </div>
        <br/>
        <br/>
        <p class="has-error" v-show="twoFAEnableError">{{ twoFAEnableError }} </p>
        <form @submit.prevent="onTwoFAEnable()">
          <input type="submit" style="display: none;" :disabled="!twoFATotpToken"/>
          <FormGroup style="text-align: left;">
            <label for="totpTokenInput">{{ $t('profile.enable2FA.token') }}</label>
            <InputGroup>
              <TextInput v-model="twoFATotpToken" id="totpTokenInput" style="flex-grow: 1;"/>
              <Button @click="onTwoFAEnable()" :disabled="!twoFATotpToken">{{ $t('profile.enable2FA.enable') }}</Button>
            </InputGroup>
          </FormGroup>
        </form>
      </div>
    </Dialog>

    <Section :title="$t('profile.title')">
      <template #header-buttons>
        <Button @click="profileModel.logout()">{{ $t('main.logout') }}</Button>
      </template>

      <div style="display: flex; flex-wrap: wrap; gap: 20px">
        <div style="width: 128px;">
          <ImagePicker mode="editable" :src="profile.avatarUrl" fallback-src="/img/avatar-default-symbolic.svg" :disabled="config.profileLocked" :size="512" :unset-handler="onAvatarUnset" :save-handler="onAvatarSubmit" display-width="128px"/>
        </div>
        <div style="flex-grow: 1;">
          <SettingsItem>
            <FormGroup>
              <label>{{ $t('main.username') }}</label>
              <div>{{ profile.username }}</div>
            </FormGroup>
          </SettingsItem>

          <SettingsItem>
            <EditableField :label="$t('main.displayName')" :saving="savingDisplayName" :value="profile.displayName" :disabled="config.profileLocked || profile.source || !profile.username" @save="onDisplayNameSave"/>
          </SettingsItem>

          <SettingsItem>
            <FormGroup>
              <label>{{ $t('profile.primaryEmail') }}</label>
              <div>{{ profile.email }}</div>
            </FormGroup>
            <div style="display: flex; align-items: center">
              <Button tool plain @click="onChangeEmail(profile.email)" v-show="profile.username && !profile.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
            </div>
          </SettingsItem>

          <SettingsItem>
            <FormGroup>
              <label>{{ $t('profile.passwordRecoveryEmail') }}</label>
              <div>{{ profile.fallbackEmail || 'No email address set' }}</div>
            </FormGroup>
            <div style="display: flex; align-items: center">
              <Button tool plain @click="onChangeFallbackEmail(profile.fallbackEmail)" v-show="profile.username && !profile.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
            </div>
          </SettingsItem>

          <SettingsItem>
            <div style="display: flex; align-items: center">
              <div style="font-weight: bold">{{ $t('profile.language') }}</div>
            </div>
            <div style="display: flex; align-items: center">
              <SingleSelect v-model="language" :options="languages" option-label="display" option-key="id" @select="onSelectLanguage"/>
            </div>
          </SettingsItem>

          <div style="display: flex; gap: 10px;">
            <Button tool @click="onPasswordChange()">{{ $t('profile.changePasswordAction') }}</Button>
            <Button tool v-show="!profile.source && !config.external2FA" @click="profile.twoFactorAuthenticationEnabled ? onTwoFADisable() : onOpenTwoFASetupDialog()">{{ $t(profile.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction') }}</Button>
          </div>
        </div>
      </div>
    </Section>

    <NotificationSettings v-if="profile.isAtLeastAdmin"/>
    <AppPasswords/>
    <ApiTokens v-if="profile.isAtLeastAdmin"/>

    <Section :title="$t('profile.loginTokens.title')">
      <div>{{ $t('profile.loginTokens.description', { webadminTokenCount: webadminTokens.length, cliTokenCount: cliTokens.length }) }}</div>
      <br/>
      <Button danger :loading="revokeTokensBusy" :disabled="revokeTokensBusy" @click="onRevokeAllWebAndCliTokens()">{{ $t('profile.loginTokens.logoutAll') }}</Button>
    </Section>
  </div>
</template>
