<script setup>

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

import { ref, onMounted, useTemplateRef } from 'vue';
import { Button, ButtonGroup, FormGroup, TextInput, Dropdown, TableView, InputDialog, Dialog } from 'pankow';
import { prettyLongDate } from 'pankow/utils';
import { API_ORIGIN, SECRET_PLACEHOLDER } from '../constants.js';
import Section from  '../components/Section.vue';
import PortBindings from  '../components/PortBindings.vue';
import ArchivesModel from '../models/ArchivesModel.js';
import DomainsModel from '../models/DomainsModel.js';
import { download } from '../utils.js';

const props = defineProps({
  config: Object
});

const archivesModel = ArchivesModel.create();
const domainsModel = DomainsModel.create();

const columns = {
  icon: {}, // archived
  location: { label: t('backups.archives.location'), sort: true },
  info: { label: t('backups.archives.info'), sort: false },
  creationTime: { label: t('main.table.date'), sort: true },
  actions: {}
};

const busy = ref(true);
const archives = ref([]);

async function refreshArchives() {
  const [error, result] = await archivesModel.list();
  if (error) return console.error(error);

  // ensure we use the full api oprigin
  result.forEach(a => {
    a.iconUrl = window.cloudronApiOrigin + a.iconUrl;
  });

  archives.value = result;
}

const inputDialog = useTemplateRef('inputDialog');
async function onRemove(archive) {
  const yes = await inputDialog.value.confirm({
    // title: t('backups.deleteArchiveDialog.title',{ appTitle: archive.title, fqdn: archive.fqdn }),
    message: t('backups.deleteArchiveDialog.description'),
    modal: true,
    confirmStyle: 'danger',
    confirmLabel: t('backups.deleteArchive.deleteAction'),
    rejectLabel: t('main.dialog.no')
  });

  if (!yes) return;

  const [error] = await archivesModel.remove(archive.id);
  if (error) return console.error(error);

  await refreshArchives();
}

const restoreDialog = useTemplateRef('restoreDialog');
const restoreArchive = ref({});
const restoreFqdn = ref('');
const restoreManifest = ref({});
const restoreLocation = ref('');
const restoreDomain = ref('');
const restoreNeedsOverwrite = ref(false);
const restoreBusy = ref(false);
const restoreError = ref({});
const restoreTcpPorts = ref({});
const restoreUdpPorts = ref({});
const domains = ref([]);
const restoreSecondaryDomains = ref([]);

async function onRestore(archive) {
  restoreBusy.value = false;
  restoreError.value = {};

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

  domains.value = result;

  const app = archive.appConfig || {
    subdomain: '',
    domain: domains.value[0].domain,
    secondaryDomains: [],
    portBindings: {}
  }; // pre-8.2 backups do not have appConfig

  // appConfig also has a manifest but it has incomplete port objects!
  const manifest = archive.manifest;

  restoreLocation.value = app.subdomain;
  const d = domains.value.find(function (d) { return app.domain === d.domain; });
  restoreDomain.value = d ? d.domain : domains.value[0].domain; // try to pre-select the app's domain
  restoreSecondaryDomains.value = {};
  restoreNeedsOverwrite.value = false;
  restoreArchive.value = archive;
  restoreFqdn.value = archive.appConfig?.fqdn || '-';
  restoreManifest.value = manifest;

  const httpPorts = manifest.httpPorts || {};
  for (const env in httpPorts) {
    restoreSecondaryDomains.value[env] = {
      title: httpPorts[env].title,
      description: httpPorts[env].description,
      subdomain: httpPorts[env].defaultValue || '',
      domain: restoreDomain.value,
    };
  }

  // now fill secondaryDomains with real values, if exists
  if (app.secondaryDomains) {
    app.secondaryDomains.forEach(function (sd) {
      const usedDomain = domains.value.find(function (d) { return sd.domain === d.domain; });

      restoreSecondaryDomains.value[sd.environmentVariable].subdomain = sd.subdomain;
      restoreSecondaryDomains.value[sd.environmentVariable].domain = usedDomain ? usedDomain.domain : restoreDomain.value;
    });
  }

  // Portbinding map only for information we make copies
  restoreTcpPorts.value = manifest.tcpPorts ? JSON.parse(JSON.stringify(manifest.tcpPorts)) : {};
  restoreUdpPorts.value = manifest.udpPorts ? JSON.parse(JSON.stringify(manifest.udpPorts)) : {};

  // set default ports for tcp
  for (const env in restoreTcpPorts.value) {
    if (app.portBindings[env]) { // was enabled in the app
      restoreTcpPorts.value[env].value = app.portBindings[env].hostPort;
      restoreTcpPorts.value[env].enabled = true;
    } else {
      restoreTcpPorts.value[env].value = restoreTcpPorts.value[env].defaultValue || 0;
      restoreTcpPorts.value[env].enabled = false;
    }
  }

  // set default ports for udp
  for (const env in restoreUdpPorts.value) {
    if (app.portBindings[env]) { // was enabled in the app
      restoreUdpPorts.value[env].value = app.portBindings[env].hostPort;
      restoreUdpPorts.value[env].enabled = true;
    } else {
      restoreUdpPorts.value[env].value = restoreUdpPorts.value[env].defaultValue || 0;
      restoreUdpPorts.value[env].enabled = false;
    }
  }

  restoreDialog.value.open();
}

async function onRestoreSubmit() {
  restoreBusy.value = true;
  restoreError.value = {};

  const secondaryDomains = {};
  for (const env in restoreSecondaryDomains.value) {
    secondaryDomains[env] = {
      subdomain: restoreSecondaryDomains.value[env].subdomain,
      domain: restoreSecondaryDomains.value[env].domain
    };
  }

  // only use enabled ports
  const finalPorts = {};
  for (const env in restoreTcpPorts.value) {
    if (restoreTcpPorts.value[env].enabled) {
      finalPorts[env] = restoreTcpPorts.value[env].value;
    }
  }
  for (const env in restoreUdpPorts.value) {
    if (restoreUdpPorts.value[env].enabled) {
      finalPorts[env] = restoreUdpPorts.value[env].value;
    }
  }

  const data = {
    subdomain: restoreLocation.value,
    domain: restoreDomain.value,
    secondaryDomains,
    ports: finalPorts,
    overwriteDns: restoreNeedsOverwrite.value,
  };

  const [error] = await archivesModel.restore(restoreArchive.value.id, data);
  if (error) {
    if (error.type === 'externally_exists') {
      restoreError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.';
      restoreNeedsOverwrite.value = true;
    } else {
      restoreError.value.generic = error.body ? error.body.message : 'Internal error';
      console.error(error);
    }
    restoreBusy.value = false;
    return;
  }

  restoreBusy.value = false;
  restoreDialog.value.close();

  window.location.href = '/#/apps';
}

async function onDownloadConfig(archive) {
  // secrets and tokens already come with placeholder characters we remove them
  const tmp = {
    remotePath: archive.remotePath,
    encrypted: !!props.config.password  // we add this just to help the import UI
  };

  Object.keys(props.config).forEach((k) => {
    if (props.config[k] !== SECRET_PLACEHOLDER) tmp[k] = props.config[k];
  });

  const filename = `${archive.appConfig.fqdn}-archive-config-${(new Date(archive.creationTime)).toISOString().split('T')[0]}.json`;
  download(filename, JSON.stringify(tmp, null, 4));
}

onMounted(async () => {
  await refreshArchives();
  busy.value = false;
});

</script>

<template>
  <InputDialog ref="inputDialog"/>
  <Dialog ref="restoreDialog"
    :title="$t('backups.restoreArchiveDialog.title')"
     reject-style="secondary"
    :reject-label="restoreBusy ? '' : $t('main.dialog.cancel')"
    :confirm-label="$t(restoreNeedsOverwrite ? 'backups.restoreArchiveDialog.restoreActionOverwrite' : 'backups.restoreArchiveDialog.restoreAction')"
    :confirm-busy="restoreBusy"
    :confirm-active="!restoreBusy"
    @confirm="onRestoreSubmit()"
  >
    <p v-html="$t('backups.restoreArchiveDialog.description', { appId: restoreManifest.id, fqdn: restoreFqdn, creationTime: prettyLongDate(restoreArchive.creationTime) })"></p>

    <div class="text-danger" v-show="restoreError.generic">{{ restoreError.generic }}</div>
    <div class="text-danger" v-show="restoreError.dnsInUse">{{ restoreError.dnsInUse }}</div>
    <!-- <div ng-show="archiveRestore.error.location.fqdn === archiveRestore.subdomain + '.' + archiveRestore.domain.domain"><small>{{ archiveRestore.error.location.message }}</small></div> -->

    <form @submit.prevent="onRestoreSubmit()" autocomplete="off">
      <fieldset>
        <FormGroup>
          <label for="locationInput">{{ $t('app.cloneDialog.location') }}</label>
          <div style="display: flex;">
            <TextInput id="locationInput" ref="locationInput" v-model="restoreLocation" style="flex-grow: 1;" />
            <Dropdown v-model="restoreDomain" :options="domains" option-label="domain" option-key="domain" />
          </div>
        </FormGroup>

        <FormGroup v-for="(domain, key) in restoreSecondaryDomains" :key="key">
          <label :for="'secondaryDomainInput-' + key">{{ domain.title }}</label>
          <small>{{ domain.description }}</small>
          <div style="display: flex;">
            <TextInput :id="'secondaryDomainInput-' + key" v-model="domain.subdomain" :placeholder="$t('appstore.installDialog.locationPlaceholder')" style="flex-grow: 1;" />
            <Dropdown v-model="domain.domain" :options="domains" option-label="domain" option-key="domain" />
          </div>
        </FormGroup>

       <!-- <p class="text-small text-warning" ng-show="archiveRestore.domain.provider === 'noop' || archiveRestore.domain.provider === 'manual'" ng-bind-html="'appstore.installDialog.manualWarning' | tr:{ location: ((archiveRestore.subdomain ? archiveRestore.subdomain + '.' : '') + archiveRestore.domain.domain) }"></p> -->
        <!-- <div class="has-error text-center" ng-show="archiveRestore.error.port">{{ archiveRestore.error.port }}</div> -->
        <PortBindings v-model:tcp-ports="restoreTcpPorts" v-model:udp-ports="restoreUdpPorts" :error="restoreError"/>
      </fieldset>
    </form>
  </Dialog>

  <Section :title="$t('backups.archives.title')">
    <p v-html="$t('backups.archive.description')"></p>

    <TableView :columns="columns" :model="archives" :busy="busy" @row-click="onInfo">
      <template #icon="archive">
        <img :src="archive.iconUrl || 'img/appicon_fallback.png'" v-fallback-image="API_ORIGIN + '/img/appicon_fallback.png'" height="24" width="24"/>
      </template>

      <!-- for pre-8.2 backups, appConfig can be null -->
      <template #location="archive">{{ archive.appConfig ? archive.appConfig.fqdn : '-' }}</template>

      <template #info="archive">
        <span v-tooltip="`${archive.manifest.id}@${archive.manifest.version}`">{{ archive.manifest.title }}</span>
      </template>

      <template #creationTime="archive">{{ prettyLongDate(archive.creationTime) }}</template>

      <template #actions="archive">
        <div class="table-actions">
          <ButtonGroup>
            <Button tool secondary small icon="fa-solid fa-history" v-tooltip="'Restore from Archive'" @click.stop="onRestore(archive)"></Button>
            <Button tool secondary small icon="fa-solid fa-file-alt" v-tooltip="$t('backups.listing.tooltipDownloadBackupConfig')" @click.stop="onDownloadConfig(archive)"></Button>
          </ButtonGroup>
          <Button tool danger small icon="fa-solid fa-trash-alt" v-tooltip="'Delete Archive'" @click.stop="onRemove(archive)"></Button>
        </div>
      </template>
    </TableView>
  </Section>
</template>
