<script setup>

// for restore from archive or clone !

import { ref, useTemplateRef, computed, inject } from 'vue';
import { InputGroup, FormGroup, TextInput, SingleSelect, Dialog } from '@cloudron/pankow';
import { prettyLongDate } from '@cloudron/pankow/utils';
import PortBindings from  '../components/PortBindings.vue';
import AppsModel from '../models/AppsModel.js';
import ArchivesModel from '../models/ArchivesModel.js';
import DomainsModel from '../models/DomainsModel.js';

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

const dashboardDomain = inject('dashboardDomain');
const appId = ref(null);
const dialog = useTemplateRef('dialog');
const restoreArchive = ref({});
const fqdn = ref('');
const restoreManifest = ref({});
const restoreLocation = ref('');
const restoreDomain = ref('');
const needsOverwrite = ref(false);
const busy = ref(false);
const formError = ref({});
const restoreTcpPorts = ref({});
const restoreUdpPorts = ref({});
const domains = ref([]);
const restoreSecondaryDomains = ref([]);
const restoreDomainProvider = computed(() => {
  const tmp = domains.value.find((d) => d.domain === restoreDomain.value);
  return tmp ? tmp.provider : '';
});

async function onSubmit() {
  busy.value = true;
  formError.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: needsOverwrite.value,
  };

  // we could come from restoring an app archive or cloning an app from a backup
  if (appId.value) {
    data.backupId = restoreArchive.value.id;
    const [error] = await appsModel.clone(appId.value, data);
    if (error) {
      if (error.type === 'externally_exists') {
        formError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.';
        needsOverwrite.value = true;
      } else if (error.body) {
        formError.value.generic = error.body.message;
      } else {
        formError.value.generic = 'Internal error';
        console.error(error);
      }
      busy.value = false;
      return;
    }
  } else {
    const [error] = await archivesModel.restore(restoreArchive.value.id, data);
    if (error) {
      if (error.type === 'externally_exists') {
        formError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.';
        needsOverwrite.value = true;
      } else if (error.body) {
        formError.value.generic = error.body.message;
      } else {
        formError.value.generic = 'Internal error';
        console.error(error);
      }
      busy.value = false;
      return;
    }
  }

  busy.value = false;
  dialog.value.close();

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

defineExpose({
  async open(archive, cloneFromAppId = null) {
    appId.value = cloneFromAppId;
    busy.value = false;
    formError.value = {};

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

    domains.value = result;

    const app = archive.appConfig || {
      subdomain: '',
      domain: dashboardDomain.value,
      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 : dashboardDomain.value; // try to pre-select the app's domain
    restoreSecondaryDomains.value = {};
    needsOverwrite.value = false;
    restoreArchive.value = archive;
    fqdn.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;
      }
    }

    dialog.value.open();
  }
});

</script>

<template>
  <Dialog ref="dialog"
    :title="appId ? $t('app.cloneDialog.title') : $t('backups.restoreArchiveDialog.title')"
     reject-style="secondary"
    :reject-label="$t('main.dialog.cancel')"
    :reject-active="!busy"
    :confirm-label="!appId ? $t(needsOverwrite ? 'backups.restoreArchiveDialog.restoreActionOverwrite' : 'backups.restoreArchiveDialog.restoreAction') : $t(needsOverwrite ? 'app.restoreDialog.cloneActionOverwrite' : 'app.restoreDialog.cloneAction')"
    :confirm-busy="busy"
    :confirm-active="!busy"
    @confirm="onSubmit()"
  >
    <div v-if="appId" v-html="$t('app.cloneDialog.description', { creationTime: prettyLongDate(restoreArchive.creationTime), packageVersion: restoreArchive.packageVersion })"></div>
    <div v-else v-html="$t('backups.restoreArchiveDialog.description', { appId: restoreManifest.id, fqdn, creationTime: prettyLongDate(restoreArchive.creationTime) })"></div>

    <div class="error-label" v-show="formError.generic">{{ formError.generic }}</div>
    <div class="error-label" v-show="formError.dnsInUse">{{ formError.dnsInUse }}</div>
    <div class="error-label" v-show="formError.port">{{ formError.port }}</div>

    <form @submit.prevent="onSubmit()" autocomplete="off">
      <input type="submit" style="display: none;"/>

      <fieldset>
        <FormGroup>
          <label for="locationInput">{{ $t('app.cloneDialog.location') }}</label>
          <InputGroup>
            <TextInput id="locationInput" ref="locationInput" v-model="restoreLocation" style="flex-grow: 1;" />
            <SingleSelect v-model="restoreDomain" :options="domains" option-label="domain" option-key="domain" />
          </InputGroup>
          <div class="warning-label" v-show="restoreDomainProvider === 'noop' || restoreDomainProvider === 'manual'" v-html="$t('appstore.installDialog.manualWarning', { restoreLocation: ((restoreLocation ? restoreLocation + '.' : '') + restoreDomain) })"></div>
        </FormGroup>

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

        <PortBindings v-model:tcp="restoreTcpPorts" v-model:udp="restoreUdpPorts" :error="formError" :domain-provider="restoreDomainProvider"/>
      </fieldset>
    </form>
  </Dialog>
</template>
