<script>
import { throttle, isEmpty } from 'lodash';

import NameNsDescription from '@shell/components/form/NameNsDescription';
import ResourceTabs from '@shell/components/form/ResourceTabs';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import Tab from '@shell/components/Tabbed/Tab';
import CruResource from '@shell/components/CruResource';
import KeyValue from '@shell/components/form/KeyValue';
import Banner from '@components/Banner/Banner';

import CreateEditView from '@shell/mixins/create-edit-view';

import { MANAGEMENT, NAMESPACE } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { matching } from '@shell/utils/selector';
import { HCI } from '@pkg/harvester/types';

import Listeners from './Listeners';
import HealthCheck from './HealthCheck';

const NAMESPACE_SELECTOR = 'loadbalancer.harvesterhci.io/namespace';
const PROJECT_SELECTOR = 'loadbalancer.harvesterhci.io/project';

export default {
  name: 'HarvesterLoadBalancer',

  emits: ['update:value'],

  components: {
    NameNsDescription,
    ResourceTabs,
    LabeledSelect,
    Tab,
    Listeners,
    HealthCheck,
    CruResource,
    KeyValue,
    Banner,
  },

  mixins: [CreateEditView],

  inheritAttrs: false,

  props: {
    value: {
      type:     Object,
      required: true,
    }
  },

  async fetch() {
    const inStore = this.$store.getters['currentProduct'].inStore;

    const hash = {
      ipPools:    this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IP_POOL }),
      namespaces: this.$store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE }),
      vms:        this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VM }),
    };

    if (this.$store.getters['management/schemaFor'](MANAGEMENT.PROJECT)) {
      hash.projects = this.$store.dispatch('management/findAll', { type: MANAGEMENT.PROJECT });
    }

    await allHash(hash);

    this.updateMatchingVMs();
  },

  data() {
    const annotations = this.value.metadata.annotations || [];

    if (!this.value.spec.healthCheck) {
      this.value.spec.healthCheck = {};
    }

    const matchingVMs = {
      matched: 0,
      matches: [],
      none:    true,
      sample:  null,
      total:   0,
    };

    return {
      ipPool:            this.value.spec.ipPool,
      projectSelector:   annotations[PROJECT_SELECTOR] || '',
      namespaceSelector: annotations[NAMESPACE_SELECTOR] || '',
      matchingVMs,
    };
  },

  computed: {
    ipamOption() {
      return [{
        label: this.t('harvester.loadBalancer.ipam.options.dhcp'),
        value: 'dhcp',
      }, {
        label: this.t('harvester.loadBalancer.ipam.options.pool'),
        value: 'pool',
      }];
    },

    ipPoolOptions() {
      const ipPools = this.$store.getters['harvester/all'](HCI.IP_POOL);

      const out = ipPools.map((ipPool) => ipPool.id);

      return [{
        label: this.t('harvester.loadBalancer.ipPool.options.none'),
        value: '',
      }, ...out];
    },

    projectOptions() {
      const projects = this.$store.getters['harvester/all'](MANAGEMENT.PROJECT);

      return projects.map((project) => project.id);
    },

    namespaceOptions() {
      const namespaces = this.$store.getters['harvester/all'](NAMESPACE);

      return namespaces.map((n) => n.id);
    },

    backendServerSelector: {
      get() {
        const out = {};

        Object.keys(this.value.spec?.backendServerSelector || {}).map((key) => {
          out[key] = (this.value.spec.backendServerSelector[key] || []).join(',') || '';
        });

        return out;
      },

      set(value) {
        const backendServerSelector = {};

        Object.keys(value).map((key) => {
          backendServerSelector[key] = (value[key] || '').split(',');
        });

        this.value.spec['backendServerSelector'] = backendServerSelector;
      },
    },

    yamlModifiers() {
      const activelyRemove = [
        'metadata.managedFields',
        'metadata.relationships',
        'metadata.state',
        'links',
        'type',
        'id'
      ];

      if (this.isCreate) {
        activelyRemove.push('status');
      }

      return { activelyRemove };
    },

    healthCheckPortInUseWarning() {
      const healthCheckPort = this.value?.spec?.healthCheck?.port;
      const portInUse = this.value?.spec?.listeners?.find((l) => l.backendPort === healthCheckPort);

      if (healthCheckPort && portInUse) {
        return this.t('harvester.loadBalancer.healthCheck.warning.portInUse', { port: portInUse.backendPort }, true);
      }

      return '';
    }
  },

  methods: {
    update() {
      const { projectSelector, namespaceSelector } = this;

      if (projectSelector) {
        this.value.metadata.annotations[PROJECT_SELECTOR] = projectSelector;
      }

      if (namespaceSelector) {
        this.value.metadata.annotations[NAMESPACE_SELECTOR] = namespaceSelector;
      }
    },

    updateMatchingVMs: throttle(function() {
      const backendServerSelector = this.value.spec.backendServerSelector;
      const allVMs = this.$store.getters['harvester/all'](HCI.VM).filter((vm) => vm.metadata.namespace === this.value.metadata.namespace);

      if (isEmpty(backendServerSelector)) {
        this.matchingVMs = {
          matched: 0,
          total:   allVMs.length,
          none:    true,
          sample:  null,
        };
      } else {
        const match = matching(allVMs, backendServerSelector, 'spec.template.metadata.labels');

        this.matchingVMs = {
          matched: match.length,
          total:   allVMs.length,
          none:    match.length === 0,
          sample:  match[0] ? match[0].nameDisplay : null,
        };
      }
    }, 250, { leading: true }),

    healthCheckEnabled(v) {
      if (!v) {
        this.value.spec.healthCheck = {};
      }
    }
  },

  watch: {
    backendServerSelector:      'updateMatchingVMs',
    'value.metadata.namespace': 'updateMatchingVMs',
  },
};
</script>

<template>
  <CruResource
    :done-route="doneRoute"
    :resource="value"
    :mode="mode"
    :errors="errors"
    :apply-hooks="applyHooks"
    :yaml-modifiers="yamlModifiers"
    @finish="save"
  >
    <NameNsDescription
      :value="value"
      :namespaced="true"
      :mode="mode"
      @update:value="$emit('update:value', $event)"
    />

    <ResourceTabs
      class="mt-15"
      :need-conditions="false"
      :need-related="false"
      :side-tabs="true"
      :mode="mode"
    >
      <Tab
        name="basic"
        :label="t('harvester.loadBalancer.tabs.basic')"
        :weight="99"
        class="bordered-table"
      >
        <div class="row">
          <div class="col span-6">
            <LabeledSelect
              v-model:value="value.spec.ipam"
              :label="t('harvester.loadBalancer.ipam.label')"
              :options="ipamOption"
              :mode="mode"
            />
          </div>
          <div
            v-if="value.spec.ipam === 'pool'"
            class="col span-6"
          >
            <LabeledSelect
              v-model:value="value.spec.ipPool"
              :label="t('harvester.loadBalancer.ipPool.label')"
              :options="ipPoolOptions"
              :mode="mode"
              @update:value="update"
            />
          </div>
        </div>
      </Tab>
      <Tab
        v-if="value.spec.workloadType === 'vm'"
        name="listeners"
        :label="t('harvester.loadBalancer.tabs.listeners')"
        :weight="98"
        class="bordered-table"
      >
        <Banner
          v-if="healthCheckPortInUseWarning"
          color="warning"
        >
          <span v-clean-html="healthCheckPortInUseWarning" />
        </Banner>
        <Listeners
          v-model:value="value.spec.listeners"
          class="col span-12"
          :mode="mode"
        />
      </Tab>
      <Tab
        v-if="value.spec.workloadType === 'vm'"
        name="backendServer"
        :label="t('harvester.loadBalancer.tabs.backendServer')"
        :weight="97"
        class="bordered-table"
      >
        <div class="row">
          <div class="col span-12">
            <Banner :color="(matchingVMs.none ? 'warning' : 'success')">
              <span v-clean-html="t('harvester.loadBalancer.backendServerSelector.matchingVMs.matchesSome', matchingVMs)" />
            </Banner>
          </div>
        </div>
        <KeyValue
          v-model:value="backendServerSelector"
          :mode="mode"
          :read-allowed="false"
          :initial-empty-row="true"
        />
      </Tab>
      <Tab
        v-if="value.spec.workloadType === 'vm'"
        name="healthCheck"
        :label="t('harvester.loadBalancer.tabs.healthCheck')"
        :weight="96"
        class="bordered-table"
      >
        <HealthCheck
          v-model:value="value.spec.healthCheck"
          :mode="mode"
          :model="value"
          @enabled="healthCheckEnabled"
        />
      </Tab>
    </ResourceTabs>
  </CruResource>
</template>

<style lang="scss" scoped>
 $remove: 75;
  $checkbox: 75;

  .title {
    margin-bottom: 10px;

    .read-from-file {
      float: right;
    }
  }

  .ports-headers, .ports-row{
    display: grid;
    grid-column-gap: $column-gutter;
    margin-bottom: 10px;
    align-items: center;

    &.show-protocol{
      grid-template-columns: 23% 23% 10% 15% 15% 10%;
      &:not(.show-node-port){
        grid-template-columns: 31% 31% 10% 15% 10%;
      }
    }
    &.show-node-port:not(.show-protocol){
      grid-template-columns: 28% 28% 15% 15% 10%;
    }
  }

  .ports-headers {
    color: var(--input-label);
  }

  .toggle-host-ports {
    color: var(--primary);
  }

  .remove BUTTON {
    padding: 0px;
  }

  .ports-row {
    > div {
      height: 100%;
    }

    .port-protocol :deep() {
      .unlabeled-select {
        .v-select.inline {
          margin-top: 2px;
        }
      }
    }
  }

  .footer {
    margin-top: 10px;
    margin-left: 5px;

    .protip {
      float: right;
      padding: 5px 0;
    }
  }
</style>
