<script>
import BrandImage from '@shell/components/BrandImage';
import ClusterProviderIcon from '@shell/components/ClusterProviderIcon';
import { mapGetters } from 'vuex';
import $ from 'jquery';
import { CAPI, MANAGEMENT } from '@shell/config/types';
import { mapPref, MENU_MAX_CLUSTERS } from '@shell/store/prefs';
import { sortBy } from '@shell/utils/sort';
import { ucFirst } from '@shell/utils/string';
import { KEY } from '@shell/utils/platform';
import { getVersionInfo } from '@shell/utils/version';
import { LEGACY } from '@shell/store/features';
import { SETTING } from '@shell/config/settings';
import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
import { isRancherPrime } from '@shell/config/version';

const UNKNOWN = 'unknown';
const UI_VERSION = process.env.VERSION || UNKNOWN;
const UI_COMMIT = process.env.COMMIT || UNKNOWN;

export default {

  components: { BrandImage, ClusterProviderIcon },

  data() {
    const { displayVersion, fullVersion } = getVersionInfo(this.$store);
    const hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);

    return {
      shown:         false,
      displayVersion,
      fullVersion,
      uiCommit:      UI_COMMIT,
      uiVersion:     UI_VERSION,
      clusterFilter: '',
      hasProvCluster,
    };
  },

  fetch() {
    if (this.hasProvCluster) {
      this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
    }
  },

  computed: {
    ...mapGetters(['clusterId']),
    ...mapGetters(['clusterReady', 'isRancher', 'currentCluster', 'currentProduct']),
    ...mapGetters('type-map', ['activeProducts']),
    ...mapGetters({ features: 'features/get' }),

    value: {
      get() {
        return this.$store.getters['productId'];
      },
    },

    legacyEnabled() {
      return this.features(LEGACY);
    },

    showClusterSearch() {
      return this.clusters.length > this.maxClustersToShow;
    },

    clusters() {
      const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
      let kubeClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all), this.$store);
      let pClusters = null;

      if (this.hasProvCluster) {
        pClusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
        const available = pClusters.reduce((p, c) => {
          p[c.mgmt] = true;

          return p;
        }, {});

        // Filter to only show mgmt clusters that exist for the available provisionning clusters
        // Addresses issue where a mgmt cluster can take some time to get cleaned up after the corresponding
        // provisionning cluster has been deleted
        kubeClusters = kubeClusters.filter(c => !!available[c]);
      }

      return kubeClusters.map((x) => {
        const pCluster = pClusters?.find(c => c.mgmt.id === x.id);

        return {
          id:              x.id,
          label:           x.nameDisplay,
          ready:           x.isReady && !pCluster?.hasError,
          osLogo:          x.providerOsLogo,
          providerNavLogo: x.providerMenuLogo,
          badge:           x.badge,
          isLocal:         x.isLocal
        };
      });
    },

    clustersFiltered() {
      const search = (this.clusterFilter || '').toLowerCase();

      const out = search ? this.clusters.filter(item => item.label.toLowerCase().includes(search)) : this.clusters;

      const sorted = sortBy(out, ['name:desc', 'label']);

      return sorted;
    },

    maxClustersToShow: mapPref(MENU_MAX_CLUSTERS),

    multiClusterApps() {
      const options = this.options;

      return options.filter(opt => (opt.inStore === 'management' || opt.isMultiClusterApp) && opt.category !== 'configuration' && opt.category !== 'legacy');
    },

    legacyApps() {
      const options = this.options;

      return options.filter(opt => opt.inStore === 'management' && opt.category === 'legacy');
    },

    configurationApps() {
      const options = this.options;

      return options.filter(opt => opt.category === 'configuration');
    },

    options() {
      const cluster = this.clusterId || this.$store.getters['defaultClusterId'];
      // TODO plugin routes
      const entries = this.activeProducts.map((p) => {
        // Try product-specific index first
        const to = p.to || {
          name:   `c-cluster-${ p.name }`,
          params: { cluster }
        };

        if ( !this.$router.getMatchedComponents(to).length ) {
          to.name = 'c-cluster-product';
          to.params.product = p.name;
        }

        return {
          label:             this.$store.getters['i18n/withFallback'](`product."${ p.name }"`, null, ucFirst(p.name)),
          icon:              `icon-${ p.icon || 'copy' }`,
          value:             p.name,
          removable:         p.removable !== false,
          inStore:           p.inStore || 'cluster',
          weight:            p.weight || 1,
          category:          p.category || 'none',
          to,
          isMultiClusterApp: p.isMultiClusterApp,
        };
      });

      return sortBy(entries, ['weight']);
    },

    canEditSettings() {
      return (this.$store.getters['management/schemaFor'](MANAGEMENT.SETTING)?.resourceMethods || []).includes('PUT');
    },

    hasSupport() {
      return isRancherPrime() || this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.SUPPORTED )?.value === 'true';
    },
  },

  watch: {
    $route() {
      this.shown = false;
    }
  },

  mounted() {
    document.addEventListener('keyup', this.handler);
  },

  beforeDestroy() {
    document.removeEventListener('keyup', this.handler);
  },

  methods: {
    // Cluster list number of items shown is configurable via user preference
    setClusterListHeight(maxToShow) {
      const el = this.$refs.clusterList;
      const max = Math.min(maxToShow, this.clusters.length);

      if (el) {
        const $el = $(el);
        const h = 33 * max;

        $el.css('min-height', `${ h }px`);
        $el.css('max-height', `${ h }px`);
      }
    },
    handler(e) {
      if (e.keyCode === KEY.ESCAPE ) {
        this.hide();
      }
    },

    hide() {
      this.shown = false;
    },

    toggle() {
      this.shown = !this.shown;
      this.$nextTick(() => {
        this.setClusterListHeight(this.maxClustersToShow);
      });
    },
  }
};
</script>
<template>
  <div>
    <div
      data-testid="top-level-menu"
      class="menu"
      :class="{'raised': shown, 'unraised':!shown}"
      @click="toggle()"
    >
      <svg
        class="menu-icon"
        xmlns="http://www.w3.org/2000/svg"
        height="24"
        viewBox="0 0 24 24"
        width="24"
      ><path
        d="M0 0h24v24H0z"
        fill="none"
      /><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" /></svg>
    </div>
    <div
      v-if="shown"
      class="side-menu-glass"
      @click="hide()"
    />
    <transition name="fade">
      <div
        v-if="shown"
        data-testid="side-menu"
        class="side-menu"
        tabindex="-1"
      >
        <div class="title">
          <div class="menu-spacer" />
          <div class="side-menu-logo">
            <BrandImage file-name="rancher-logo.svg" />
          </div>
        </div>
        <div class="body">
          <div @click="hide()">
            <nuxt-link
              class="option cluster selector home"
              :to="{ name: 'home' }"
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                height="24"
                viewBox="0 0 24 24"
                width="24"
              ><path
                d="M0 0h24v24H0z"
                fill="none"
              /><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" /></svg>
              <div>
                {{ t('nav.home') }}
              </div>
            </nuxt-link>
          </div>
          <template v-if="clusters && !!clusters.length">
            <div class="category">
              {{ t('nav.categories.explore') }}
            </div>
            <div
              v-if="showClusterSearch"
              class="search"
            >
              <input
                ref="clusterFilter"
                v-model="clusterFilter"
                :placeholder="t('nav.search.placeholder')"
              >
              <i
                v-if="clusterFilter"
                class="icon icon-close"
                @click="clusterFilter=''"
              />
            </div>
            <div
              ref="clusterList"
              class="clusters"
            >
              <div
                v-for="c in clustersFiltered"
                :key="c.id"
                @click="hide()"
              >
                <nuxt-link
                  v-if="c.ready"
                  class="cluster selector option"
                  :to="{ name: 'c-cluster', params: { cluster: c.id } }"
                >
                  <ClusterProviderIcon
                    :small="true"
                    :cluster="c"
                    class="rancher-provider-icon mr-10"
                  />
                  <div class="cluster-name">
                    {{ c.label }}
                  </div>
                </nuxt-link>
                <span
                  v-else
                  class="option-disabled cluster selector disabled"
                >
                  <ClusterProviderIcon
                    :small="true"
                    :cluster="c"
                    class="rancher-provider-icon mr-10"
                  />
                  <div class="cluster-name">{{ c.label }}</div>
                </span>
              </div>
              <div
                v-if="clustersFiltered.length === 0"
                class="none-matching"
              >
                {{ t('nav.search.noResults') }}
              </div>
            </div>
          </template>

          <template v-if="multiClusterApps.length">
            <div class="category">
              {{ t('nav.categories.multiCluster') }}
            </div>
            <div
              v-for="a in multiClusterApps"
              :key="a.label"
              @click="hide()"
            >
              <nuxt-link
                class="option"
                :to="a.to"
              >
                <i
                  class="icon group-icon"
                  :class="a.icon"
                />
                <div>{{ a.label }}</div>
              </nuxt-link>
            </div>
          </template>
          <template v-if="legacyEnabled">
            <div class="category">
              {{ t('nav.categories.legacy') }}
            </div>
            <div
              v-for="a in legacyApps"
              :key="a.label"
              @click="hide()"
            >
              <nuxt-link
                class="option"
                :to="a.to"
              >
                <i
                  class="icon group-icon"
                  :class="a.icon"
                />
                <div>{{ a.label }}</div>
              </nuxt-link>
            </div>
          </template>
          <template v-if="configurationApps.length">
            <div class="category">
              {{ t('nav.categories.configuration') }}
            </div>
            <div
              v-for="a in configurationApps"
              :key="a.label"
              @click="hide()"
            >
              <nuxt-link
                class="option"
                :to="a.to"
              >
                <i
                  class="icon group-icon"
                  :class="a.icon"
                />
                <div>{{ a.label }}</div>
              </nuxt-link>
            </div>
          </template>
          <div class="pad" />
        </div>
        <div class="footer">
          <div
            v-if="canEditSettings"
            @click="hide()"
          >
            <nuxt-link :to="{name: 'support'}">
              {{ t('nav.support', {hasSupport}) }}
            </nuxt-link>
          </div>
          <div @click="hide()">
            <nuxt-link
              v-tooltip="{ content: fullVersion, classes: 'footer-tooltip' }"
              :to="{ name: 'about' }"
              class="version"
              v-html="displayVersion"
            />
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<style scoped>
  .cluster.disabled > * {
    cursor: not-allowed;
    filter: grayscale(1);
    color: var(--muted);
  }
</style>

<style lang="scss">
  .localeSelector, .footer-tooltip {
    z-index: 1000;
  }

  .cluster {
    &.selector:not(.disabled):hover {
      color: var(--primary-hover-text);
      background: var(--primary-hover-bg);
      border-radius: 5px;
      text-decoration: none;

      .rancher-provider-icon {
        .rancher-icon-fill {
          fill: var(--primary-hover-text);;
        }
      }
    }

    .rancher-provider-icon {
      .rancher-icon-fill {
        // Should match .option color
        fill: var(--link);
      }
    }
  }

  .localeSelector {
    .popover-inner {
      padding: 10px 0;
    }

    .popover-arrow {
      display: none;
    }

    .popover:focus {
      outline: 0;
    }
  }

</style>

<style lang="scss" scoped>
  $clear-search-size: 20px;
  $icon-size: 25px;
  $option-padding: 4px;
  $option-height: $icon-size + $option-padding + $option-padding;

  .option {
    align-items: center;
    cursor: pointer;
    display: flex;
    color: var(--link);

    &:hover {
      text-decoration: none;
    }

    &:focus {
      outline: 0;
      > div {
        text-decoration: underline;
      }
    }

    > i {
      width: $icon-size;
      font-size: $icon-size;
      margin-right: 8px;
    }
    svg {
      margin-right: 8px;
      fill: var(--link);
    }

    > div {
      color: var(--link);
    }

    &:hover {
      color: var(--primary-hover-text);
      background: var(--primary-hover-bg);
      border-radius: 5px;
      > div {
        color: var(--primary-hover-text);
      }
      svg {
        fill: var(--primary-hover-text);
      }
      div {
        color: var(--primary-hover-text);
      }
    }
  }

  .option, .option-disabled {
    padding: $option-padding 0 $option-padding 10px;
  }

  .menu {
    position: absolute;
    left: 0;
    width: 55px;
    height: 54px;
    top: 0;
    grid-area: menu;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    &:hover {
      background-color: var(--topmost-light-hover);
    }
    .menu-icon {
      width: 24px;
      height: 24px;
      fill: var(--header-btn-text);
    }
    &.raised {
      z-index: 200;
    }
  }

  .side-menu {
    position: absolute;
    top: 0;
    left: 0px;
    bottom: 0;
    width: 280px;
    background-color: var(--topmenu-bg);
    z-index: 100;
    border-right: 1px solid var(--topmost-border);
    box-shadow: 0 0 15px 4px var(--topmost-shadow);
    display: flex;
    flex-direction: column;
    padding: 0;

    &:focus {
      outline: 0;
    }

    .title {
      display: flex;
      height: 55px;
      flex: 0 0 55px;
      width: 100%;
      border-bottom: 1px solid var(--nav-border);
      justify-content: flex-start;
      align-items: center;
      .menu {
        display: flex;
        justify-content: center;
        width: 55px;
        margin-right: 10px;
      }
      .menu-icon {
        width: 24px;
        height: 24px;
      }
      .menu-spacer {
        width: 55px;
      }
    }
    .body {
      flex: 1;
      display: flex;
      flex-direction: column;
      margin: 10px 20px;
      overflow-y: auto;

      .category {
        padding: 10px 0;
        text-transform: uppercase;
        opacity: 0.8;
        margin-top: 10px;
      }

      .home {
        color: var(--link);
      }

      .home:focus {
        outline: 0;
      }

      .cluster {
        align-items: center;
        display: flex;
        height: $option-height;

        white-space: nowrap;
        &:focus {
          outline: 0;
        }
        .cluster-name {
          text-overflow: ellipsis;
          overflow: hidden;
        }
        > img {
          max-height: $icon-size;
          max-width: $icon-size;
          margin-right: 8px;
        }
      }

      .pad {
        flex: 1;
      }

      .search {
        position: relative;
        > input {
          background-color: transparent;
          margin-bottom: 8px;
          padding-right: 34px;
        }
        > i {
          position: absolute;
          font-size: $clear-search-size;
          top: 9px;
          right: 8px;
          opacity: 0.7;
          cursor: pointer;
          &:hover {
            color: var(--disabled-bg);
          }
        }
      }

      .clusters {
        overflow-y: auto;
        overflow-x: hidden;
      }

      .none-matching {
        padding: 8px
      }
    }
    .footer {
      margin: 20px;

      display: flex;
      flex: 0;
      flex-direction: row;
      > * {
        flex: 1;
        color: var(--link);

        &:first-child {
          text-align: left;
        }
        &:last-child {
          text-align: right;
        }
        text-align: center;
      }

      .version {
        cursor: pointer;
      }
    }
  }

  .side-menu-glass {
    background-color: transparent;
    position: absolute;
    top: 0;
    left: 0px;
    bottom: 0;
    width: 100vw;
    z-index: 99;
    opacity: 1;
  }

  .side-menu-logo {
    align-items: center;
    display: flex;
    margin-left: 10px;
    opacity: 1;
    transition: opacity 1.2s;
    transition-delay: 0s;
    height: 55px;
    max-width: 200px;
    & IMG {
      object-fit: contain;
      height: 21px;
      max-width: 200px;
    }
  }

  .fade-enter-active {
    .side-menu-logo {
      opacity: 0;
    }
  }

  .fade-enter-active, .fade-leave-active {
    transition: all 0.2s;
    transition-timing-function: ease;
  }

  .fade-leave-active {
    transition: all 0.4s;
  }

  .fade-leave-to {
    left: -300px;
  }

  .fade-enter {
    left: -300px;

    .side-menu-logo {
      opacity: 0;
    }
  }

  .locale-chooser {
    cursor: pointer;
  }

  .localeSelector {
    ::v-deep .popover-inner {
      padding: 50px 0;
    }

    ::v-deep .popover-arrow {
      display: none;
    }

    ::v-deep .popover:focus {
      outline: 0;
    }

    li {
      padding: 8px 20px;

      &:hover {
        background-color: var(--primary-hover-bg);
        color: var(--primary-hover-text);
        text-decoration: none;
      }
    }
  }
</style>
