<template>
  <loader ref="loader">
    <div class="page-editor">
      <validator-group>
        <page-editor-header :page="page" move-rename-buttons></page-editor-header>

        <div class="alert alert-warning" v-if="!hasPublishedVersions">
          Deze pagina heeft geen gepubliceerde versie(s)

          <redirects ref="redirects" :page="page">
            <template slot="button" slot-scope="{redirects, openModal, alertRedirectNeeded}">
               <span v-if="redirects.length">en heeft een <a href="#" @click.prevent="openModal">redirect</a>.</span>
               <span v-else>en geeft een 404.&nbsp;&nbsp;</span>

              <button class="btn btn-primary" v-if="!redirects.length" :class="{alertRedirectNeeded}" @click.prevent="openModal">Maak een redirect aan</button>
            </template>
          </redirects>
        </div>

        <template v-if="version">
          <ul class="nav nav-tabs">
            <li v-for="version in page.versions" class="nav-item">
              <router-link :to="{ name: 'page-editor-version', params: { id: page.id, versionId: version.id }, activeClass: 'active'}" class="nav-link version"
                :class="version == live ? 'text-success' : published(version) ? 'text-warning' : 'text-muted'">
                <span class="icon-calendar" v-if="scheduled(version)"></span>
                <span class="icon-checkmark" v-if="published(version)"></span>
                <span class="icon-cross" v-if="!published(version) && !scheduled(version) && !hasRecurrentSchedule(version)"></span>
                <span class="icon-loop2" v-if="!published(version) && !scheduled(version) && hasRecurrentSchedule(version)"></span>
                {{version.name || '(Naamloze versie)'}}
              </router-link>
            </li>
            <li class="nav-item copy">
              <button type="button" class="btn btn-sm btn-primary" @click.prevent="copyVersion" :disabled="!mayCreate"><span class="icon-copy"></span> Kopieer deze versie</button>
            </li>
            <li class="nav-item text-link-right">
              <redirects ref="redirects" :page="page">
                <template slot="button" slot-scope="{redirects, openModal}">
                  <button class="btn btn-sm btn-secondary redirect-button" @click.prevent="openModal"><span class="icon-new-tab"></span> Redirects ({{redirects.length}})</button>
                </template>
              </redirects>
            </li>
          </ul>
          <version-editor :version="version" :state="state" :publish="publish" :depublish="mayDepublish ? depublish : null" :save="save" :cancel="cancel" :permissions="permissions" ref="version-editor"></version-editor>
        </template>
        <bottom-toolbar>
          <button slot="right" class="btn btn-danger mr-2" @click="cancel" :disabled="versionEditor && !versionEditor.changed" v-if="version">
            <span class="icon-undo"></span>
            Herstellen
          </button>
          <submit slot="right" :action="save" v-if="version" :disabled="!mayUpdate">
            <span class="icon-floppy-disk"></span>
            Wijzigingen opslaan
          </submit>
          <button class="btn btn-warning mr-2" @click="depublish" v-if="version && version == live && mayDepublish" :disabled="versionEditor && versionEditor.hasRecurringSchedules">
            <span class="icon-switch"></span>
            Depubliceren
          </button>
          <button class="btn btn-success mr-2" @click="publish" :disabled="!mayUpdate || versionEditor && (versionEditor.changed || versionEditor.hasRecurringSchedules)" v-if="version && version != live">
            <span class="icon-rocket"></span>
            Live zetten
          </button>
          <button class="btn btn-danger" @click="deleteVersion" :disabled="!mayDelete">
            <span class="icon-cross"></span>
            <span v-if="!version || page.versions.length == 1">Verwijder pagina</span>
            <span v-if="version && page.versions.length != 1">Verwijder versie</span>
          </button>
        </bottom-toolbar>
        <confirm-dialog ref="dialog"></confirm-dialog>
      </validator-group>
    </div>
  </loader>
</template>

<style lang="scss" scoped>
  .nav-item {
    &.copy {
      margin: 5px;
    }
    &.text-link-right {
      margin: 5px 0 5px auto;
    }
  }
</style>

<script>
import { permissionStore } from '../tools/permission-store.js';
import { pageStore } from '../tools/page-store.js';
import loader from '../shared/loader.vue';
import * as formElements from '../shared/form/all.js';
import pageEditorHeader from './page-editor-header.vue';
import versionEditor from './version-editor.vue';
import redirects from '../shared/redirects.vue';
import {getPublishedVersions, isDepublished, isPublished, isScheduled} from './version-utils';

export default {
  name: 'page-editor',
  props: ['versionId'],
  data() {
    return {
      page: null,
      isLoading: true,
      versionEditor: null,
      versionsWithRecurrentSchedules: [],
    };
  },
  // triggered when switching between a page version
  beforeRouteUpdate(to, from, next) {
    this.confirmChangingRoute(next);
  },
  // triggered when going from page-editor to another page (ex. docs)
  beforeRouteLeave(to, from, next) {
    this.confirmChangingRoute(next);
  },
  mounted() {
    this.$watch('isLoading', () => {
      // this.$refs is not reactive, so when loading changes, we record the current version of $refs['version-editor']
      // Note that when isLoading turns to true, $refs.version-editor is undefined so this.versionEditor can be undefined as well.
      this.versionEditor = this.$refs['version-editor'];
    })
  },
  computed: {
    version() {
      return this.page ? this.page.versions.find(version => version.id == this.versionId) : null;
    },
    allPublished() {
      return getPublishedVersions(this.page.versions);
    },
    hasPublishedVersions() {
      return this.page ? this.page.versions.some(isPublished) : null
    },
    live() {
      return this.allPublished[0];
    },
    state(){
      return {
        published: this.published(this.version),
        depublished: this.depublished(this.version),
        scheduled: this.scheduled(this.version),
        live: this.live
      }
    },
    schema() {
      return pageStore.types[this.page.versions[0].$type] || {};
    },
    permissions() {
      return {
        ...permissionStore.getPermissions('CmsPage'),
        canDepublish: this.mayDepublish
      }
    },
    mayCreate() {
      // TODO: use meta.itemName when child classes are returned in the permissions api
      return permissionStore.canCreate('CmsPage');
    },
    mayUpdate() {
      // TODO: use meta.itemName when child classes are returned in the permissions api
      return permissionStore.canUpdate('CmsPage');
    },
    mayDepublish() {
      // TODO: use meta.itemName when child classes are returned in the permissions api
      return permissionStore.canUpdate('CmsPage')
        && (!this.schema.preventDepublish || this.allPublished.length > 1);
    },
    mayDelete() {
      // TODO: use meta.itemName when child classes are returned in the permissions api
      return permissionStore.canDelete('CmsPage')
        && (!this.version
          || (
            (!this.schema.preventDelete || this.page.versions.length > 1)
            && !this.published(this.version)
          )
        );
    }
  },
  watch: {
    // VueRouter docs:
    // ...when the user navigates from /user/foo to /user/bar, the same component instance will be reused.
    // However, this also means that the lifecycle hooks of the component will not be called.
    '$route'() {
      this.fetchData();
    }
  },
  created() {
    this.fetchData();
  },
  methods: {
    fetchData() {
      this.isLoading = true;

      pageStore.getPageById(this.$route.params.id).then(page => {
        this.page = page;

        // nextTick, because 'this.live' can only be computed after 'this.page' is set
        this.$nextTick(() => {
          const version = this.live || this.page.versions[0];

          if (version) {
            this.$router.push({ name: 'page-editor-version',
              params: {
                id: page.id,
                versionId: this.$route.params.versionId || version.id
              }
            });
          }

          this.isLoading = false;
        });

        pageStore.scheduledVersionIds(this.$route.params.id).then(versionsWithRecurrentSchedules => {
          this.versionsWithRecurrentSchedules = versionsWithRecurrentSchedules;
        })
      });
    },
    save() {
      return pageStore.updateVersion(this.version)
        .then(() => {
          this.versionEditor.resetChanged();
          this.$store.dispatch('alerts/addSuccess', 'Pagina is opgeslagen!');
        })
        .catch(e => this.$store.dispatch('alerts/addError', `Pagina opslaan is niet gelukt! <br> ${e.message}`));
    },
    copyVersion() {
      pageStore.copyVersion(this.version).then(version => {
        pageStore.getPageById(this.page.id).then(page => {
          this.page = page;
          this.$nextTick(() => {
            this.versionEditor.changed = false;
            this.$router.push({ name: 'page-editor-version', params: { id: page.id, versionId: version.id } });
          })
        });
      })
    },
    deleteVersion() {
      const deletePage = !this.version || this.page.versions.length == 1;
      this.$refs.dialog.confirm(
        'Verwijderen',
        `Weet je zeker dat je ${ deletePage ? 'de pagina' : 'deze versie' } wilt verwijderen?`
      ).then(() => {
        const action = this.version ? pageStore.deleteVersion(this.version) : pageStore.deletePage(this.page);

        action
          .then(() => {
            if (this.versionEditor) {
              this.versionEditor.changed = false;
            }
            this.$router.push(deletePage ? '/pages' : { name: 'page-editor', params: { id: this.page.id } });
            this.$store.dispatch('alerts/addSuccess', `De ${deletePage ? 'pagina' : 'versie'} is verwijderd`);
          })
          .catch(error => {
            if (error.isReferenceError) {
              if (this.$refs['version-editor']) {
                this.$refs['version-editor'].openReferencesErrorModal();
              }
            }

            this.$store.dispatch('alerts/addError', `Verwijderen is niet gelukt! <br> ${error.message}`);

            throw error;
          });
      });
    },
    confirmChangingRoute(next) {
      // check if version is changed by user
      if (this.versionEditor && this.versionEditor.changed) {
        this.$refs.dialog.confirm(
          'Er zijn wijzigingen niet opgeslagen',
          'Wil je de wijzigingen verwijderen?'
        ).then((isConfirmed) => {
          next(isConfirmed);
        });
      } else {
        next();
      }
    },
    cancel() {
      this.$refs.dialog.confirm(
        'Herstellen',
        'Weet je zeker dat je de wijzigingen ongedaan wilt maken?'
      ).then(() => {
        pageStore.getPageById(this.page.id).then(page => {
          this.page = page;
          this.versionEditor.resetChanged();
        });
      });
    },
    publish() {
      return pageStore.publishVersion(this.version)
        .then(pageVersion => {
          this.handlePublication(pageVersion);
          this.$store.dispatch('alerts/addSuccess', 'Deze versie staat nu live!');
        })
        .catch(e => this.$store.dispatch('alerts/addError', `Versie live zetten is niet gelukt! <br> ${e.message}`));
    },
    depublish() {
      return this.$refs.dialog.confirm(
        'Depubliceren',
        'Weet je zeker dat je deze versie wilt depubliceren?',
        'Ja, depubliceren'
      )
      .then(() => {
        return pageStore.depublishVersion(this.version)
          .then(pageVersion => {
            this.handlePublication(pageVersion);
          })
          .catch(e => this.$store.dispatch('alerts/addError', `Versie depubliceren is niet gelukt! <br> ${e.message}`));
      },
      () => {
        // intentionally empty for removing console error when dismissing dialog
      });
    },
    handlePublication(pageVersion) {
      this.version.publishDate = pageVersion.publishDate;
      this.version.depublishDate = pageVersion.depublishDate;
      this.versionEditor.resetChanged();
    },
    hasRecurrentSchedule(version) {
      return this.versionsWithRecurrentSchedules.includes(version.id);
    },
    scheduled(version) {
      return isScheduled(version);
    },
    published(version) {
      return isPublished(version);
    },
    depublished(version) {
      return isDepublished(version);
    }
  },
  components: {
    pageEditorHeader,
    versionEditor,
    loader,
    redirects,
    ...formElements
  }
};
</script>
