<script setup>

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

import { ref, useTemplateRef, onMounted } from 'vue';
import { Button, Menu, Checkbox, Dialog, SingleSelect, FormGroup, InputDialog, NumberInput, TableView, TextInput, MaskedInput } from '@cloudron/pankow';
import Section from  '../components/Section.vue';
import StateLED from  '../components/StateLED.vue';
import VolumesModel from '../models/VolumesModel.js';

const volumesModel = VolumesModel.create();

const columns = {
  status: {},
  name: {
    label: 'Name',
    sort: true
  },
  mountType: {
    label: 'Type',
    sort: true ,
    hideMobile: true,
  },
  target: {
    label: 'Target',
    sort: true,
    hideMobile: true,
  },
  actions: {}
};

const actionMenuModel = ref([]);
const actionMenuElement = useTemplateRef('actionMenuElement');
function onActionMenu(volume, event) {
  actionMenuModel.value = [{
    icon: 'fa-solid fa-pencil-alt',
    label: t('main.action.edit'),
    visible: volume.mountType === 'sshfs' || volume.mountType === 'cifs' || volume.mountType === 'nfs',
    action: openVolumeDialog.bind(null, volume),
  }, {
    separator: true,
    visible: volume.mountType === 'sshfs' || volume.mountType === 'cifs' || volume.mountType === 'nfs',
  }, {
    icon: 'fa-solid fa-sync-alt',
    label: t('volumes.remountActionTooltip'),
    visible: volume.mountType === 'sshfs' || volume.mountType === 'cifs' || volume.mountType === 'nfs' || volume.mountType === 'ext4' || volume.mountType === 'xfs',
    action: remount.bind(null, volume),
  }, {
    icon: 'fa-solid fa-folder',
    label: t('volumes.openFileManagerActionTooltip'),
    target: '_blank',
    href: '/filemanager.html#/home/volume/' + volume.id,
  }, {
    separator: true,
  }, {
    icon: 'fa-solid fa-trash-alt',
    label: t('main.action.remove'),
    action: onRemove.bind(null, volume),
  }];

  actionMenuElement.value.open(event, event.currentTarget);
}

const busy = ref(true);
const volumes = ref([]);
const volumeDialogData = ref({
  error: null,
  busy: false,
  mode: '', // edit or new
  name: '',
  // dynamic extra props from openVolumeDialog
});

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

  function checkData() {
    const data = volumeDialogData.value;

    switch (data.mountType) {
      case 'filesystem':
      case 'mountpoint':
        if (!data.hostPath) return false;
        if (!data.hostPath) return false;
        break;
      case 'ext4':
      case 'xfs':
        if (!data.diskPath) return false;
        break;
      case 'nfs':
        if (!data.host) return false;
        if (!data.remoteDir) return false;
        break;
      case 'sshfs':
        if (!data.host) return false;
        if (!data.remoteDir) return false;
        if (!data.port) return false;
        if (!data.user) return false;
        if (!data.privateKey) return false;
        break;
      case 'cifs':
        if (!data.host) return false;
        if (!data.remoteDir) return false;
        if (!data.username) return false;
        if (!data.password) return false;
        break;
      default:
        return false;
    }

    return true;
  }

  if (isFormValid.value) {
    isFormValid.value = checkData();
  }
}

async function refresh() {
  busy.value = true;

  const [error, result] = await volumesModel.list();
  if (error) return console.error(error);
  result.forEach(v => { v.busy = true; });
  volumes.value = result;

  busy.value = false;

  for (const v of volumes.value) {
    const [error, status] = await volumesModel.getStatus(v.id);
    if (error) {
      v.state = 'warning';
      v.message = error.message;
    } else {
      v.state = ledState(status.state);
      v.message = status.message;
    }
    v.busy = false;
  }
};

const volumeDialog = useTemplateRef('volumeDialog');
const inputDialog = useTemplateRef('inputDialog');


async function openVolumeDialog(volume) {
  volumeDialogData.value.error = null;
  volumeDialogData.value.mode = volume ? 'edit' : 'new';
  volumeDialogData.value.id = volume ? volume.id : '';
  volumeDialogData.value.name = volume ? volume.name : '';
  volumeDialogData.value.mountType = volume ? volume.mountType : '';
  volumeDialogData.value.host = volume ? volume.mountOptions.host : '';
  volumeDialogData.value.seal = volume ? volume.mountOptions.seal : true;
  volumeDialogData.value.port = volume ? volume.mountOptions.port : 0;
  volumeDialogData.value.remoteDir = volume ? volume.mountOptions.remoteDir : '';
  volumeDialogData.value.user = volume ? volume.mountOptions.user : '';
  volumeDialogData.value.username = volume ? volume.mountOptions.username : '';
  volumeDialogData.value.password = volume ? volume.mountOptions.password : '';
  volumeDialogData.value.diskPath = volume ? volume.mountOptions.diskPath : '';
  volumeDialogData.value.hostPath = volume ? volume.mountOptions.hostPath : '';
  volumeDialogData.value.privateKey = volume ? volume.mountOptions.privateKey : '';

  const [error, blockDevices] = await volumesModel.getBlockDevices();
  if (error) return console.error(error);

  const ext4BlockDevices = [], xfsBlockDevices = [];
  for (const blockDevice of blockDevices) {
    if (blockDevice.mountpoints.some((mountpoint) => mountpoint === '/' || mountpoint.startsWith('/home') || mountpoint.startsWith('/boot'))) continue;
    blockDevice.label = blockDevice.path; // // amend label for UI
    if (blockDevice.type === 'ext4') ext4BlockDevices.push(blockDevice);
    else if (blockDevice.type === 'xfs') xfsBlockDevices.push(blockDevice);
  }

  volumeDialogData.value.ext4BlockDevices = ext4BlockDevices;
  volumeDialogData.value.xfsBlockDevices = xfsBlockDevices;

  volumeDialog.value.open();

  setTimeout(checkValidity, 100); // update state of the confirm button
}

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

  volumeDialogData.value.busy = true;

  const mountOptions = {
    host: volumeDialogData.value.host,
    seal: volumeDialogData.value.seal,
    port: volumeDialogData.value.port,
    remoteDir: volumeDialogData.value.remoteDir,
    user: volumeDialogData.value.user,
    username: volumeDialogData.value.username,
    diskPath: volumeDialogData.value.diskPath,
    hostPath: volumeDialogData.value.hostPath,
  };
  if (volumeDialogData.value.password) mountOptions.password = volumeDialogData.value.password; // cifs
  if (volumeDialogData.value.privateKey) mountOptions.privateKey = volumeDialogData.value.privateKey; // sshfs

  let error;
  if (volumeDialogData.value.mode === 'new') {
    [error] = await volumesModel.add(volumeDialogData.value.name, volumeDialogData.value.mountType, mountOptions);
  } else {
    [error] = await volumesModel.update(volumeDialogData.value.id, volumeDialogData.value.mountType, mountOptions);
  }

  if (error) {
    volumeDialogData.value.error = error.body ? error.body.message : 'Internal error';
    volumeDialogData.value.busy = false;
    console.error(error);
    return;
  }

  await refresh();

  volumeDialog.value.close();
  volumeDialogData.value.busy = false;
}

async function onRemove(volume) {
  const yes = await inputDialog.value.confirm({
    title: t('volumes.removeVolumeDialog.title'),
    message: t('volumes.removeVolumeDialog.description', { volumeName: volume.name }),
    confirmStyle: 'danger',
    confirmLabel: t('volumes.removeVolumeDialog.removeAction'),
    rejectLabel: t('main.dialog.cancel'),
    rejectStyle: 'secondary'
  });

  if (!yes) return;

  const [error] = await volumesModel.remove(volume.id);
  if (error) {
    if (error.status === 409) window.pankow.notify({ text: error.body.message || `Volume is still in use.`, type: 'danger', persistent: true });
    return console.error(error);
  }

  await refresh();
}

function ledState(state) {
  switch (state) {
  case 'inactive': return 'danger';
  case 'active': return 'success';
  default: return '';
  }
}

async function remount(nonReactiveVolume) {
  const volume = volumes.value.find(v => v.id === nonReactiveVolume.id);

  volume.busy = true;
  await volumesModel.remount(volume.id);

  const status = await volumesModel.getStatus(volume.id);
  volume.state = ledState(status.state);
  volume.message = status.message;
  volume.busy = false;

  window.pankow.notify('Volume remounted');
}

onMounted(async () =>{
  await refresh();
});

</script>

<template>
  <div class="content">
    <Menu ref="actionMenuElement" :model="actionMenuModel" />
    <InputDialog ref="inputDialog" />

    <!-- width is to fix the 70 characters per line for ssh key -->
    <Dialog ref="volumeDialog"
      style="width: 78ch;"
      :title="volumeDialogData.mode === 'edit' ? $t('volumes.editVolumeDialog.title') : $t('volumes.addVolumeDialog.title')"
      :reject-label="$t('main.dialog.cancel')"
       reject-style="secondary"
      :confirm-label="volumeDialogData.mode === 'edit' ? $t('main.dialog.save') : $t('main.action.add')"
      :confirm-active="!volumeDialogData.busy && isFormValid"
      :confirm-busy="volumeDialogData.busy"
      @confirm="onSubmit()"
    >
      <form ref="form" @submit.prevent="onSubmit()" autocomplete="off" @input="checkValidity()">
        <fieldset :disabled="volumeDialogData.busy">
          <input style="display: none;" type="submit" />

          <div class="error-label" v-show="volumeDialogData.error">{{ volumeDialogData.error }}</div>

          <FormGroup>
            <label for="volumeName">{{ $t('volumes.name') }}</label>
            <TextInput id="volumeName" v-model="volumeDialogData.name" :readonly="volumeDialogData.mode === 'edit'" :required="volumeDialogData.mode !== 'edit'"/>
          </FormGroup>

          <FormGroup>
            <label for="volumeMountType">{{ $t('volumes.mountType') }}</label>
            <SingleSelect id="volumeMountType" v-model="volumeDialogData.mountType" :options="VolumesModel.mountTypes" option-label="name" option-key="value" :disabled="volumeDialogData.mode === 'edit'" />
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'filesystem' || volumeDialogData.mountType === 'mountpoint'">
            <label for="volumeHostPath">{{ $t('volumes.localDirectory') }}</label>
            <TextInput id="volumeHostPath" v-model="volumeDialogData.hostPath" :placeholder="volumeDialogData.mountType === 'filesystem' ? '/srv/shared' : '/mnt/data'" required/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'ext4' || volumeDialogData.mountType === 'xfs'">
            <label for="volumeDiskPath">{{ $t('volumes.addVolumeDialog.diskPath') }}</label>
            <SingleSelect id="volumeDiskPath" v-if="volumeDialogData.mountType === 'ext4'" v-model="volumeDialogData.diskPath" :options="volumeDialogData.ext4BlockDevices" option-label="label" option-key="path" :disabled="volumeDialogData.mode === 'edit'"/>
            <SingleSelect id="volumeDiskPath" v-if="volumeDialogData.mountType === 'xfs'" v-model="volumeDialogData.diskPath" :options="volumeDialogData.xfsBlockDevices" option-label="label" option-key="path" :disabled="volumeDialogData.mode === 'edit'"/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'cifs' || volumeDialogData.mountType === 'nfs' || volumeDialogData.mountType === 'sshfs'">
            <label for="volumeHost">{{ $t('volumes.addVolumeDialog.server') }}</label>
            <TextInput v-model="volumeDialogData.host" id="volumeHost" required/>
          </FormGroup>

          <Checkbox v-if="volumeDialogData.mountType === 'cifs'" v-model="volumeDialogData.seal" :label="$t('backups.configureBackupStorage.cifsSealSupport')" />

          <FormGroup v-if="volumeDialogData.mountType === 'sshfs'">
            <label for="volumePort">{{ $t('volumes.addVolumeDialog.port') }}</label>
            <NumberInput v-model="volumeDialogData.port" id="volumePort" min="0"/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'cifs' || volumeDialogData.mountType === 'nfs' || volumeDialogData.mountType === 'sshfs'">
            <label for="volumeRemoteDir">{{ $t('volumes.addVolumeDialog.remoteDirectory') }}</label>
            <TextInput v-model="volumeDialogData.remoteDir" id="volumeRemoteDir" placeholder="/share"/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'cifs'">
            <label for="volumeUsername">{{ $t('volumes.addVolumeDialog.username') }}</label>
            <TextInput v-model="volumeDialogData.username" id="volumeUsername" required/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'cifs'">
            <label for="volumePassword">{{ $t('volumes.addVolumeDialog.password') }}</label>
            <MaskedInput v-model="volumeDialogData.password" id="volumePassword" required/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'sshfs'">
            <label for="volumeUser">{{ $t('volumes.addVolumeDialog.user') }}</label>
            <TextInput v-model="volumeDialogData.user" id="volumeAddUser" required/>
          </FormGroup>

          <FormGroup v-if="volumeDialogData.mountType === 'sshfs'">
            <label for="volumePrivateKey">{{ $t('volumes.addVolumeDialog.privateKey') }}</label>
            <MaskedInput multiline rows="7" style="white-space: nowrap;" v-model="volumeDialogData.privateKey" id="volumePrivateKey" required/>
          </FormGroup>

        </fieldset>
      </form>
    </Dialog>

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

      <div v-html="$t('volumes.description')"></div>
      <br/>
      <TableView :columns="columns" :model="volumes" :busy="busy" :placeholder="$t('volumes.emptyPlaceholder')">
        <template #target="volume">
          {{ (volume.mountType === 'mountpoint' || volume.mountType === 'filesystem') ? volume.hostPath : (volume.mountOptions.host || volume.mountOptions.diskPath || volume.hostPath) + (volume.mountOptions.remoteDir || '') }}
        </template>
        <template #status="volume">
          <div style="text-align: center;" :title="volume.message">
            <StateLED :busy="volume.busy" :state="volume.state"/>
          </div>
        </template>
        <template #actions="volume">
          <div style="text-align: right;">
            <Button tool plain secondary @click.capture="onActionMenu(volume, $event)" icon="fa-solid fa-ellipsis" />
          </div>
        </template>
      </TableView>
    </Section>
  </div>
</template>
