<script>
import AsyncButton from '@/components/AsyncButton';
import day from 'dayjs';
import Card from '@/components/Card';
import { exceptionToErrorsArray } from '@/utils/error';
import LabeledSelect from '@/components/form/LabeledSelect';
import Banner from '@/components/Banner';
import { WORKLOAD_TYPES } from '@/config/types';
import { diffFrom } from '@/utils/time';
import { mapGetters } from 'vuex';

export default {
  components: {
    Card,
    AsyncButton,
    LabeledSelect,
    Banner
  },
  props:      {
    resources: {
      type:     Array,
      required: true
    }
  },
  data() {
    return {
      errors:             [],
      selectedRevision:   null,
      currentRevision:    null,
      revisions:          [],
    };
  },
  computed: {
    ...mapGetters({ t: 'i18n/t' }),
    workload() {
      return this.resources[0];
    },
    workloadName() {
      return this.workload.metadata.name;
    },
    workloadNamespace() {
      return this.workload.metadata.namespace;
    },
    currentRevisionNumber() {
      return this.workload.metadata.annotations['deployment.kubernetes.io/revision'];
    },
    rollbackRequestBody() {
      if (!this.selectedRevision) {
        return null;
      }

      // Build the request body in the same format that kubectl
      // uses to call the Kubernetes API to roll back a workload.
      // To see an example request body, run:
      // kubectl rollout undo deployment/[deployment name] --to-revision=[revision number] -v=8
      const body = [
        {
          op:      'replace',
          path:  '/spec/template',
          value: {
            metadata: {
              creationTimestamp: null,
              labels:            { 'workload.user.cattle.io/workloadselector': this.selectedRevision.spec.template.metadata.labels['workload.user.cattle.io/workloadselector'] }
            },
            spec: this.selectedRevision.spec.template.spec
          }
        }, {
          op:    'replace',
          path:  '/metadata/annotations',
          value: { 'deployment.kubernetes.io/revision': this.selectedRevision.metadata.annotations['deployment.kubernetes.io/revision'] }
        }
      ];

      return body;
    }
  },
  fetch() {
    // Fetch revisions of the current workload
    this.$store.dispatch('cluster/findAll', { type: WORKLOAD_TYPES.REPLICA_SET })
      .then(( response ) => {
        const allReplicaSets = response;

        const hasRelationshipWithCurrentWorkload = ( replicaSet ) => {
          const relationshipsOfReplicaSet = replicaSet.metadata.relationships;

          const revisionsOfCurrentWorkload = relationshipsOfReplicaSet.filter(( relationship ) => {
            const isRevisionOfCurrentWorkload = relationship.fromId && relationship.fromId === `${ this.workloadNamespace }/${ this.workloadName }`;

            return isRevisionOfCurrentWorkload;
          });

          return revisionsOfCurrentWorkload.length > 0;
        };

        const workloadRevisions = allReplicaSets.filter(( replicaSet ) => {
          return hasRelationshipWithCurrentWorkload( replicaSet );
        });

        const revisionOptions = workloadRevisions.map( (revision ) => {
          const isCurrentRevision = this.getRevisionNumber(revision) === this.currentRevisionNumber;

          if (isCurrentRevision) {
            this.currentRevision = revision;
          }

          return this.buildRevisionOption( revision );
        });

        this.revisions = revisionOptions;
      })
      .catch(( err ) => {
        this.errors = exceptionToErrorsArray(err);
      });
  },
  methods: {
    close() {
      this.$emit('close');
    },
    async save() {
      try {
        await this.workload.rollBackWorkload(this.workload, this.rollbackRequestBody);
        this.close();
      } catch (err) {
        this.errors = exceptionToErrorsArray(err);
      }
    },
    getRevisionNumber( revision ) {
      return revision.metadata.annotations['deployment.kubernetes.io/revision'];
    },
    buildRevisionOption( revision ) {
      const revisionNumber = this.getRevisionNumber(revision);
      const isCurrentRevision = revisionNumber === this.currentRevisionNumber;
      const now = day();
      const createdDate = day(revision.metadata.creationTimestamp);
      const revisionAge = diffFrom(createdDate, now, this.t);
      const units = this.t(revisionAge.unitsKey, { count: revisionAge.label });
      const currentLabel = this.t('promptRollback.currentLabel');
      const optionLabel = this.t('promptRollback.revisionOption', {
        revisionNumber,
        revisionAge:    revisionAge.label,
        units,
        currentLabel:   isCurrentRevision ? currentLabel : ''
      });

      return {
        label:    optionLabel,
        value:    revision,
        disabled: isCurrentRevision
      };
    },
    getOptionLabel(option) {
      return option.label;
    },
  }
};
</script>

<template>
  <Card
    class="prompt-rollback"
    :show-highlight-border="false"
  >
    <h4 slot="title" class="text-default-text">
      {{ t('promptRollback.modalTitle', { workloadName }, true) }}
    </h4>
    <div slot="body" class="pl-10 pr-10">
      <Banner v-if="revisions.length === 1" color="info" :label="t('promptRollback.singleRevisionBanner')" />
      <form>
        <LabeledSelect
          v-model="selectedRevision"
          class="provider"
          :label="t('promptRollback.dropdownTitle')"
          :placeholder="t('promptRollback.placeholder')"
          :options="revisions"
          :get-option-label="getOptionLabel"
        />
      </form>
      <Banner v-for="(error, i) in errors" :key="i" class="" color="error" :label="error" />
    </div>
    <div slot="actions" class="buttons right-align">
      <button class="btn role-secondary mr-10" @click="close">
        {{ t('generic.cancel') }}
      </button>
      <AsyncButton
        :action-label="t('asyncButton.rollback.action')"
        :disabled="!selectedRevision"
        get-option-label="getOptionLabel"
        :right-align="true"
        @click="save"
      />
    </div>
  </Card>
</template>
<style lang='scss' scoped>
  .prompt-rollback {
    margin: 0;
  }
  .right-align {
    margin-right: 0;
    margin-left: auto;
  }
  .bottom {
    flex-direction: column;
    flex: 1;
    .banner {
      margin-top: 0
    }
    .buttons {
      display: flex;
      justify-content: flex-end;
      width: 100%;
    }
  }
</style>
