<script>
import { _EDIT } from '@shell/config/query-params';
import { allHash } from '@shell/utils/promise';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import Banner from '@components/Banner/Banner.vue';

import remove from 'lodash/remove';
import { get, set } from '@shell/utils/object';
import { HCI } from '../../../types';
import VGpuDeviceList from './VGpuDeviceList';

export default {
  name:       'VirtualMachinedevices',
  components: {
    Banner,
    LabeledSelect,
    VGpuDeviceList
  },
  props: {
    mode: {
      type:    String,
      default: _EDIT
    },

    value: {
      type:    Object,
      default: () => {}
    },

    vm: {
      type:    Object,
      default: () => {}
    }
  },

  async fetch() {
    const hash = {
      devices: this.$store.dispatch('harvester/findAll', { type: HCI.VGPU_DEVICE }),
      vms:     this.$store.dispatch(`harvester/findAll`, { type: HCI.VM })
    };

    const res = await allHash(hash);

    for (const key in res) {
      this[key] = res[key];
    }

    (this.value?.domain?.devices?.gpus || []).forEach(({ name }) => {
      if (this.enabledDevices.find(device => device?.metadata?.name === name)) {
        this.selectedDevices.push(name);
      }
    });
  },

  data() {
    return {
      deviceSchema:    this.$store.getters['harvester/schemaFor'](HCI.VGPU_DEVICE),
      devices:         [],
      vms:             [],
      selectedDevices: [],
    };
  },

  watch: {
    selectedDevices(neu) {
      const formatted = neu.map((selectedDevice) => {
        const deviceCRD = this.enabledDevices.find(device => device.metadata.name === selectedDevice);
        const deviceName = `nvidia.com/${ deviceCRD?.status?.configureVGPUTypeName?.replace(/\s+/g, '_') }`;

        return {
          deviceName,
          name: deviceCRD?.metadata.name,
        };
      });

      set(this.value.domain.devices, 'gpus', formatted);
    }
  },

  computed: {
    enabledDevices() {
      return this.devices.filter((device) => {
        return device.isEnabled;
      }) || [];
    },

    devicesInUse() {
      const inUse = this.vms.reduce((inUse, vm) => {
        if (vm.metadata.name === this.vm?.metadata?.name) {
          return inUse;
        }
        const devices = get(vm, 'spec.template.spec.domain.devices.hostDevices') || [];

        devices.forEach((device) => {
          inUse[device.name] = { usedBy: [vm.metadata.name] };
        });

        return inUse;
      }, {});

      return inUse;
    },

    devicesByNode() {
      const out = {};

      this.enabledDevices.forEach((deviceCRD) => {
        const nodeName = deviceCRD.spec?.nodeName;

        if (!out[nodeName]) {
          out[nodeName] = [deviceCRD];
        } else {
          out[nodeName].push(deviceCRD);
        }
      });

      return out;
    },

    compatibleNodes() {
      const out = [...Object.keys(this.devicesByNode)];

      this.selectedDevices.forEach((deviceUid) => {
        remove(out, (nodeName) => {
          const device = this.enabledDevices.find(deviceCRD => deviceCRD.metadata.name === deviceUid);

          return device.spec.nodeName !== nodeName;
        });
      });

      return out;
    },

    deviceOpts() {
      const filteredOptions = this.enabledDevices.filter((deviceCRD) => {
        if (this.selectedDevices.length > 0) {
          const selectedDevice = this.enabledDevices.find(device => device.metadata.name === this.selectedDevices[0]);

          return !this.devicesInUse[deviceCRD?.metadata.name] && deviceCRD.spec.nodeName === selectedDevice.spec.nodeName;
        }

        return !this.devicesInUse[deviceCRD?.metadata.name];
      });

      return filteredOptions.map((deviceCRD) => {
        return {
          value: deviceCRD?.metadata.name,
          label: deviceCRD?.metadata.name,
        };
      });
    },
  },
};
</script>

<template>
  <div>
    <div class="row">
      <div class="col span-12">
        <Banner color="info">
          <t k="harvester.vgpu.howToUseDevice" />
        </Banner>
        <Banner v-if="selectedDevices.length > 0" color="info">
          <t k="harvester.vgpu.deviceInTheSameHost" />
        </Banner>
      </div>
    </div>
    <template v-if="enabledDevices.length">
      <div class="row">
        <div class="col span-6">
          <LabeledSelect
            v-model:value="selectedDevices"
            label="Available vGPU Devices"
            searchable
            multiple
            taggable
            :options="deviceOpts"
            :mode="mode"
          >
            <template #option="option">
              <span>{{ option.value }}</span>
            </template>
          </LabeledSelect>
        </div>
      </div>
      <div v-if="compatibleNodes.length && selectedDevices.length" class="row">
        <div class="col span-12 text-muted">
          Compatible hosts:
          <!-- eslint-disable-next-line vue/no-parsing-error -->
          <span v-for="(node, idx) in compatibleNodes" :key="idx">{{ node }}{{ idx < compatibleNodes.length-1 ? ', ' : '' }}</span>
        </div>
      </div>
      <div v-else-if="selectedDevices.length" class="text-error">
        {{ t('harvester.vgpu.impossibleSelection') }}
      </div>
    </template>
    <div class="row mt-20">
      <div class="col span-12">
        <VGpuDeviceList :schema="deviceSchema" :devices="devices" @submit.prevent />
      </div>
    </div>
  </div>
</template>
