<script setup>

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

import { ref, onMounted, useTemplateRef } from 'vue';
import { Button, ClipboardButton, DateTimeInput, Dialog, SingleSelect, FormGroup, TextInput, TableView, InputDialog, InputGroup } from '@cloudron/pankow';
import { prettyLongDate } from '@cloudron/pankow/utils';
import ActionBar from './ActionBar.vue';
import Section from './Section.vue';
import AppPasswordsModel from '../models/AppPasswordsModel.js';
import AppsModel from '../models/AppsModel.js';

const appPasswordsModel = AppPasswordsModel.create();
const appsModel = AppsModel.create();

const newDialog = useTemplateRef('newDialog');
const inputDialog = useTemplateRef('inputDialog');
const passwords = ref([]);
const columns = {
  name: {
    label: t('profile.appPasswords.name'),
    sort: true
  },
  label: {
    label: t('profile.appPasswords.app'),
    sort: true,
    hideMobile: true,
  },
  creationTime: {
    label: t('main.table.created'),
    hideMobile: true,
    sort(a, b) {
      if (!a) return 1;
      if (!b) return -1;
      return new Date(a) - new Date(b);
    }
  },
  expiresAt: {
    label: t('profile.appPasswords.expires'),
    hideMobile: true,
    sort(a, b) {
      if (!a) return 1;
      if (!b) return -1;
      return new Date(a) - new Date(b);
    }
  },
  actions: {}
};

function createActionMenu(appPassword) {
  return [{
    icon: 'fa-solid fa-trash-alt',
    label: t('main.action.remove'),
    action: onRemove.bind(null, appPassword),
  }];
}

// new dialog props
const addedPassword = ref('');
const passwordName = ref('');
const identifiers = ref([]);
const identifier = ref('');
const expiresAtDate = ref('');
const minExpiresAt = new Date().toISOString().slice(0, 16);
const addError = ref('');
const busy = ref(false);

const appsById = {};
async function refresh() {
  const [error, result] = await appPasswordsModel.list();
  if (error) return console.error(error);

  for (const password of result) {
    if (password.identifier === 'mail') {
      password.label = password.identifier;
    } else {
      const app = appsById[password.identifier];
      if (!app) return password.label = password.identifier + ' (App not found)';

      const ftp = app.manifest.addons && app.manifest.addons.localstorage && app.manifest.addons.localstorage.ftp;
      const labelSuffix = ftp ? ' - SFTP' : '';
      password.label = app.label ? app.label + ' (' + app.fqdn + ')' + labelSuffix : app.fqdn + labelSuffix;
    }

    password.expired = password.expiresAt && new Date(password.expiresAt) < new Date();
  }

  passwords.value = result;
}

const form = useTemplateRef('form');
const isFormValid = ref(false);
function checkValidity() {
  isFormValid.value = form.value ? form.value.checkValidity() : false;
}

function onReset() {
  setTimeout(() => {
    passwordName.value = '';
    identifier.value = '';
    expiresAtDate.value = '';
    addedPassword.value = '';
    addError.value = '';
    busy.value = false;
    setTimeout(checkValidity, 100); // update state of the confirm button
  }, 500);
}

async function onSubmit() {
  if (!form.value.reportValidity()) return;

  busy.value = true;
  addError.value = '';
  addedPassword.value = '';

  const expiresAt = expiresAtDate.value ? new Date(expiresAtDate.value).toISOString() : null;
  const [error, result] = await appPasswordsModel.add(identifier.value, passwordName.value, expiresAt);
  if (error) {
    busy.value = false;
    addError.value = error.body ? error.body.message : 'Internal error';
    return;
  }

  addedPassword.value = result.password;
  passwordName.value = '';
  identifier.value = '';
  expiresAtDate.value = '';

  await refresh();

  busy.value = false;
}

async function onRemove(appPassword) {
  const yes = await inputDialog.value.confirm({
    title: t('profile.removeAppPassword.title'),
    message: t('profile.removeAppPassword.description', { name: appPassword.name }),
    confirmLabel: t('main.action.remove'),
    confirmStyle: 'danger',
    rejectLabel: t('main.dialog.cancel'),
    rejectStyle: 'secondary'
  });

  if (!yes) return;

  const [error] = await appPasswordsModel.remove(appPassword.id);
  if (error) return console.error(error);

  await refresh();
}

onMounted(async () => {
  // build the password identifier selection model
  identifiers.value = [{ id: 'mail', label: 'Mail client' }];

  const [error, apps] = await appsModel.list();
  if (error) return console.error(error);

  apps.forEach(function (app) {
    if (!app.manifest.addons) return;
    if (app.manifest.addons.email) return;

    const ftp = app.manifest.addons.localstorage && app.manifest.addons.localstorage.ftp;
    const sso = app.sso && (app.manifest.addons.ldap || app.manifest.addons.proxyAuth);

    if (!ftp && !sso) return;

    let labelSuffix = '';
    if (ftp && sso) labelSuffix = ' - SFTP & App Login';
    else if (ftp) labelSuffix = ' - SFTP Only';

    const label = app.label ? app.label + ' (' + app.fqdn + ')' + labelSuffix : app.fqdn + labelSuffix;
    identifiers.value.push({ id: app.id, label: label });

    // stash for later use in table labels
    appsById[app.id] = app;
  });

  await refresh();
});

</script>

<template>
  <div>
    <InputDialog ref="inputDialog" />

    <Dialog ref="newDialog"
      :title="$t('profile.createAppPassword.title')"
      :confirm-busy="busy"
      :confirm-active="addedPassword || (!busy && isFormValid)"
      :confirm-label="addedPassword ? '' : $t('main.action.add')"
       confirm-style="primary"
      :reject-label="addedPassword ? $t('main.dialog.close') : $t('main.dialog.cancel')"
       reject-style="secondary"
      @confirm="onSubmit()"
      @close="onReset()"
    >
      <div>
        <div class="error-label" v-show="addError">{{ addError }}</div>
        <Transition name="slide-left" mode="out-in">
          <div v-if="!addedPassword">
            <form @submit.prevent="onSubmit()" autocomplete="off" ref="form" @input="checkValidity()">
              <fieldset :disabled="busy">
                <input style="display: none" type="submit"/>
                <FormGroup>
                  <label for="passwordName">{{ $t('profile.createAppPassword.name') }}</label>
                  <TextInput id="passwordName" v-model="passwordName" required/>
                </FormGroup>

                <FormGroup>
                  <label>{{ $t('profile.createAppPassword.app') }}</label>
                  <SingleSelect outline v-model="identifier" :options="identifiers" option-label="label" option-key="id" required/>
                </FormGroup>

                <FormGroup>
                  <label for="expiresAt">{{ $t('profile.createAppPassword.expiresAt') }} (optional)</label>
                  <DateTimeInput id="expiresAt" v-model="expiresAtDate" :min="minExpiresAt"/>
                </FormGroup>
              </fieldset>
            </form>
          </div>
          <div v-else>
            <p>{{ $t('profile.createAppPassword.description') }}</p>
            <InputGroup>
              <TextInput v-model="addedPassword" readonly style="flex-grow: 1;"/>
              <ClipboardButton :value="addedPassword" />
            </InputGroup>
            <p>{{ $t('profile.createAppPassword.copyNow') }}</p>
          </div>
        </Transition>
      </div>
    </Dialog>

    <Section :title="$t('profile.appPasswords.title')">
      <template #header-buttons>
        <Button @click="newDialog.open()">{{ $t('main.action.add') }}</Button>
      </template>

      <div>{{ $t('profile.appPasswords.description') }}</div>
      <br/>

      <TableView :columns="columns" :model="passwords" :placeholder="$t('profile.appPasswords.noPasswordsPlaceholder')">
        <template #name="{ item:password }"><span :class="{ 'text-muted': password.expired }">{{ password.name }}</span></template>
        <template #label="{ item:password }"><span :class="{ 'text-muted': password.expired }">{{ password.label }}</span></template>
        <template #creationTime="{ item:password }"><span :class="{ 'text-muted': password.expired }">{{ prettyLongDate(password.creationTime) }}</span></template>
        <template #expiresAt="{ item:password }">
          <span :class="{ 'text-muted': password.expired }" v-if="!password.expiresAt">-</span>
          <span :class="{ 'text-muted': password.expired }" v-else>{{ prettyLongDate(password.expiresAt) }}</span>
        </template>
        <template #actions="{ item:password }">
          <ActionBar :actions="createActionMenu(password)" />
        </template>
      </TableView>
    </Section>
  </div>
</template>
