<template>
  <section>
    <plugin-header>
      <template v-slot:actions-left>
        <div class="asset-archive">
          <a @click.prevent="archiveAsset">{{ archiveButtonText }}</a>
        </div>
      </template>
      <template v-slot:title>
        IMAGE EDITOR
      </template>
      <template v-slot:actions-right>
        <ui-button
          :disabled="showCrops"
          class="asset-header-action"
          type="primary"
          color="default"
          button-type="button"
          @click="toList"
        >
          Done
        </ui-button>
      </template>
    </plugin-header>
    <slider
      v-if="!showCrops"
      :current-index="currentIndex"
      :items="ids"
      @onPreviousClick="getPreviousAsset"
      @onNextClick="getNextAsset"
    />
    <div class="detail-body">
      <div v-if="showCrops">
        <crops
          :asset="asset"
          :asset-id="currentAssetId"
          :cutouts="cutouts"
          :cutout="showCutout"
          :file-name="filename"
          :loaded="loaded"
          :master-crop="showMasterCropper"
          :rendition-details="currentRenditionDetails"
          :master-crop-config="masterCropConfig"
          @alert="emitAlert"
          @closeCropView="closeCropView"
          @saveMasterCrop="saveMasterCropGuide"
        />
      </div>
      <template v-else>
        <div class="asset-configuration">
          <div class="data-container">
            <detail-cropper
              :asset="asset"
              :asset-id="id"
              :formatted-asset-url="formattedAssetUrl"
              :modified-date="modifiedDate"
              :uploaded-by="uploadedBy"
              :uploader-name="uploaderName"
              :path="path"
              :set-crop-guide="setCropGuide"
              :toogle-detail-cropper="toogleDetailCropper"
              :supports-cropping="supportsCropping"
              :upload-date="uploadDate"
              @setShowMasterCropper="showMasterCropper = true"
              @setNullMasterCrop="saveMasterCropGuide(null)"
            />
            <Metadata
              v-if="loaded"
              :key="id"
              :asset-id="currentAssetId"
              :metadata="asset.metadata"
              :asset-types="assetTypes"
              :reference-name="referenceName"
              :tags="tags"
              :validations="validations"
              @saveAssetMeta="saveMetadata"
              @saveReferenceName="saveReferenceName"
            />
          </div>
        </div>
        <template v-if="supportsCropping">
          <preview-container
            v-for="name in previewTypes"
            :id="id"
            :key="`${name}-${path}`"
            :filename="filename"
            :name="name"
            :alternates="alternates"
            :asset="asset"
            :currently-replaced-rendition="currentlyReplacedRendition"
            :cutouts="cutouts"
            :edit-rendition="editRendition"
            :loaded="loaded"
            :reference-name="referenceName"
            :previews="previews"
          />
          <alternate-list
            :alternates="alternates"
            :previews="previews"
            :parent-path="asset.path"
            @alert="emitAlert"
            @removeAlternate="removeAlternate"
            @replaceRendition="replaceRendition"
            @resetAlert="resetAlert"
            @resetReplacedRendition="currentlyReplacedRendition = ''"
            @saveAlternate="saveAlternate"
          />
        </template>
      </template>
    </div>
  </section>
</template>
<script>

import { PREVIEWS_TYPES } from 'Config/constants';
import { getCropUrl, normalizeCropGuideValues, supportsCropping } from 'Helpers/asset';
import { hasStatusError } from 'Helpers/api';
import { fetchSingle, saveSingle } from 'Api';
import Metadata from 'Components/metadata/Metadata.vue';
import Header from 'Components/layout/Header.vue';
import Crops from './crops/Crops.vue';
import AlternateList from './custom/AlternateList.vue';
import PreviewContainer from './preview/PreviewContainer.vue';
import Slider from './Slider.vue';
import DetailCropper from './DetailCropper.vue';

const { UiButton } = window.kiln.utils.components;

export default {
  components: {
    UiButton,
    Crops,
    AlternateList,
    DetailCropper,
    'plugin-header': Header,
    PreviewContainer,
    Slider,
    Metadata
  },
  props: {
    // selected ids join into string
    assetsId: {
      type: String,
      required: true,
      default: ''
    },
    // configuration
    assetTypes: {
      type: Array,
      default: () => []
    },
    masterCropConfig: {
      type: Object,
      default: () => ({})
    },
    previews: {
      type: Object,
      default: () => {}
    },
    tags: {
      type: Object,
      default: () => {}
    },
    validations: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      // asset details
      archived: false,
      asset: {
        renditions: {},
        dimension: {
          height: 0,
          width: 0
        },
        bytes: 0,
        path: '',
        metadata: {
          altText: '',
          caption: '',
          credit: '',
          creditUrl: '',
          copyright: '',
          assetType: '',
          collectionSlug: '',
          tags: [],
          keywords: []
        },
        cropGuide: null,
        masterCrop: null
      },
      id: '',
      uploadDate: null,
      uploadedBy: '',
      uploaderName: '',
      modifiedDate: null,
      referenceName: '',
      filename: '',
      // status of fetch
      loaded: false,
      // references when editing multiple images
      assets: {},
      ids: [],
      currentIndex: 0,
      // crops view toggles
      showMasterCropper: false,
      showCutout: false,
      // cutout clicked/replaced details
      currentlyReplacedRendition: '',
      currentRenditionDetails: {},
      // toggle to refresh detail cropper
      toogleDetailCropper: true
    };
  },
  computed: {
    alternates() {
      return Object.entries(this.asset.renditions)
        .reduce((accum, [rendition, value]) => {
          if (value && value.isAlternate) accum.push(rendition);
          return accum;
        }, []).reverse();
    },
    currentAssetId() {
      return this.ids[this.currentIndex];
    },
    cutouts() {
      return Object.entries(this.asset.renditions)
        .reduce((accum, [rendition, value]) => {
          if (value && value.crop && !value.path) {
            return {
              ...accum,
              [rendition]: value.crop
            };
          }

          return accum;
        }, {});
    },
    formattedAssetUrl() {
      return this.path
        ? getCropUrl(this.path, {
          referenceName: this.referenceName
        })
        : '';
    },
    path() {
      return this.asset.path;
    },
    previewTypes() {
      return Object.values(PREVIEWS_TYPES);
    },
    archiveButtonText() {
      return this.archived ? 'UNARCHIVE IMAGE' : 'ARCHIVE IMAGE';
    },
    showCrops() {
      return (this.showMasterCropper || this.showCutout) && this.supportsCropping;
    },
    supportsCropping() {
      return this.asset && this.asset.mimetype && supportsCropping(this.asset);
    }
  },
  mounted() {
    if (this.assetsId) {
      this.ids = this.assetsId.split('&');

      this.fetchCurrentAsset();
    }
  },
  methods: {
    /**
     * Handles close in Crops
     * @param {Boolean} fetchNeeded Flag for fetch after close
     */
    closeCropView(fetchNeeded = false) {
      this.showCutout = false;
      this.showMasterCropper = false;

      if (fetchNeeded) {
        this.fetchCurrentAsset();
      }
    },
    /**
     * Handles the edit rendition click
     * @param {Object} renditionDetails Details fo rendition clicked
     */
    editRendition(renditionDetails = {}) {
      this.showCutout = true;
      this.showMasterCropper = false;

      this.currentRenditionDetails = renditionDetails;
    },
    /**
     * Emits reset alert
     * @param {string} text Text of alert to remove
     */
    resetAlert(text = '') {
      this.$emit('resetAlert', text);
    },
    /**
     * Emits alert from child components
     * @param {string} text Text of alert to show
     * @param {boolean} dismissible If dismissible or not
     */
    emitAlert(text = '', dismissible = true) {
      this.$emit('alert', { text, dismissible });
    },
    /**
     * Sets the current replaced rendition
     * @param {string} newRendition Name of new rendition replaced
     */
    replaceRendition(newRendition = '') {
      this.currentlyReplacedRendition = newRendition;
    },
    /**
     * Redirects navigation to the list view
     */
    toList() {
      this.$emit('clearAlerts');
      this.$emit('showListView');
    },
    /**
     * Removes alternate from the asset
     * @param {string} alternateKey
     */
    removeAlternate(alternateKey = '') {
      this.$delete(this.asset.renditions, alternateKey);
    },
    /**
     * Saves alternate in the asset
     * @param {string} alternateKey
     */
    saveAlternate(alternateKey = '') {
      this.$set(this.asset.renditions, alternateKey, { isAlternate: true });
    },
    /**
     * Set the crop guide, if changed
     * @param {Object|null} newCropGuide
     */
    setCropGuide(newCropGuide) {
      this.asset.cropGuide = normalizeCropGuideValues(newCropGuide);
      this.save({ asset: { cropGuide: this.asset.cropGuide } });
    },
    /**
     * Sets metadata and saves
     * @param {string} fieldName Field name
     * @param {string} value Value to be set
     */
    saveMetadata(fieldName = '', value = '') {
      this.asset.metadata[fieldName] = value;

      this.save({
        asset: {
          metadata: {
            [fieldName]: value
          }
        }
      });
    },
    /**
     * Sets the reference name and saves
     * @param {string} referenceName New reference name
     */
    saveReferenceName(referenceName = '') {
      this.referenceName = referenceName;
      this.save({ referenceName });
    },
    /**
     * Saves local data to DB
     * @param {Object} objectToSave Object with metadata fields that changed
     * @return {Promise} Save call promise
     */
    save(objectToSave = {}) {
      return saveSingle(this.currentAssetId, objectToSave)
        .then((data) => {
          if (hasStatusError(data)) {
            this.emitAlert(`${data.error}: ${data.message}`);
          }
        });
    },
    /**
     * Toggles archive state and save
     */
    archiveAsset() {
      this.archived = !this.archived;
      this.save({ archived: this.archived });
    },
    /**
     * Fetches current asset data
     * @returns {Promise} Fetch call promise
     */
    fetchCurrentAsset() {
      // populate initial value from data store
      return fetchSingle(this.currentAssetId)
        .then(
          /* eslint complexity: off */
          (data) => {
            if (hasStatusError(data)) {
              const {
                error = 'Unexpected Error',
                message = 'Error fetching single asset'
              } = data;

              this.emitAlert(`${error}: ${message}`);
            } else {
              const {
                  asset = {
                    metadata: {}
                  },
                  uploadedBy = '',
                  filename = '',
                  id = '',
                  modifiedDate = '',
                  referenceName = '',
                  uid = '',
                  uploadDate = '',
                  archived = false,
                  user = null
                } = data,
                // @todo: flatten out this needlessly nested data
                {
                  cropGuide = null,
                  renditions = {},
                  masterCrop = null,
                  dimension = {},
                  bytes = 0,
                  metadata = {},
                  mimetype,
                  path = ''
                } = asset;

              this.asset = {
                renditions,
                dimension,
                bytes,
                path,
                metadata: {
                  altText: metadata.altText || '',
                  caption: metadata.caption || '',
                  collectionSlug: metadata.collectionSlug || '',
                  copyright: metadata.copyright || '',
                  credit: metadata.credit || '',
                  creditUrl: metadata.creditUrl || '',
                  assetType: metadata.assetType || '',
                  keywords: metadata.keywords || [],
                  tags: metadata.tags || []
                },
                mimetype,
                cropGuide,
                masterCrop
              };

              this.archived = archived;
              this.filename = filename;
              this.id = id;
              this.uid = uid;
              this.uploadDate = uploadDate;
              this.referenceName = referenceName;
              this.modifiedDate = modifiedDate;
              this.assets = {
                ...this.assets,
                [id]: {
                  asset: this.asset,
                  archived,
                  filename,
                  id,
                  uid,
                  uploadDate,
                  referenceName,
                  modifiedDate,
                  loaded: true
                }
              };

              this.loaded = true;
              this.timestamp = Date.now();

              if (user && user.name) {
                this.uploaderName = user.name;
              }

              this.uploadedBy = uploadedBy;
            }
          }
        )
        .catch((e) => {
          console.error(e);
          this.toList();
        });
    },

    /**
     * Sets current asset to the state
     */
    setCurrentAsset() {
      if (this.assets[this.currentAssetId]) {
        const currentItem = this.assets[this.currentAssetId];

        this.asset = { ...currentItem.asset };
        this.archived = currentItem.archived;
        this.filename = currentItem.filename;
        this.id = currentItem.id;
        this.uid = currentItem.uid;
        this.uploadDate = currentItem.uploadDate;
        this.referenceName = currentItem.referenceName;
        this.modifiedDate = currentItem.modifiedDate;
        this.loaded = currentItem.loaded;

        this.$nextTick(() => {
          this.fetchCurrentAsset();
        });
      } else {
        this.fetchCurrentAsset();
      }
    },
    /**
     * Gets previous asset in array
     */
    getPreviousAsset() {
      if (this.currentIndex > 0) {
        this.currentIndex--;

        this.setCurrentAsset();
      }
    },

    /**
     * Gets next asset in array
     */
    getNextAsset() {
      if (this.currentIndex < this.ids.length - 1) {
        this.currentIndex++;

        this.setCurrentAsset();
      }
    },
    /**
     * Sets master crop guide in asset and save metadata
     * @param {Object} masterCrop Master crop guide object
     */
    saveMasterCropGuide(masterCrop) {
      if (!this.masterCrop) {
        this.toogleDetailCropper = false;
      }

      this.save({ asset: { cropGuide: null, masterCrop } })
        .then(() => {
          this.asset.masterCrop = masterCrop;
          this.asset.cropGuide = null;
          this.showMasterCropper = false;
          this.toogleDetailCropper = true;

          Object.keys(this.cutouts).map(this.removeAlternate);
        });
    }
  }
};
</script>
<style lang="scss" scoped>
  @import 'Styles/colors.scss';
  @import 'Styles/croppers.scss';
  @import 'Styles/helpers.scss';
  @import 'Styles/fonts.scss';

  .asset-archive {
    color: $light-gray;
    font-size: 14px;
    text-decoration: none;

    a {
      cursor: pointer;
      &:hover {
        text-decoration: underline;
      }
    }
  }

  /* Layout */
  .detail-body {
    display: grid;
    padding: 0 40px 20px;

    > * {
      margin-bottom: 30px;
    }
  }

  .data-container {
    display: flex;
    margin-bottom: 20px;
    width: fit-content;
  }

  .asset-configuration {
    grid-column: 1;
    grid-row-end: 3;
    grid-row-start: 1;
    width: fit-content;
  }
</style>
