<template>
  <q-dialog v-model="open" @hide="cancelImport">
    <q-card class="full-width no-shadow" style="max-width: 800px">
      <q-card-section class="q-pb-md q-gutter-y-md">
        <div class="text-h6">{{ title }}</div>
      </q-card-section>
      <q-card-section class="q-pb-md q-gutter-y-md">
        <q-checkbox
          dense
          :label="$t('registries.objectCard.all')"
          v-model="selectedAll"
        />
        <q-form ref="form">
          <div
            v-for="(versions, service) in available"
            class="flex column service-el"
          >
            <div class="flex row">
              <q-checkbox
                dense
                v-model="selectedServices[service]"
                :label="service"
                class="col-7"
              />
              <q-select
                dense
                options-dense
                class="col-4"
                :value="selectedServicesVersions[service]"
                @input="(version) => loadService(service, version)"
                :options="versions"
                :disable="
                  versions.length < 2 ||
                  Object.keys(loadingServices).length !== 0
                "
                :rules="[
                  (val) =>
                    !selectedServices[service] || !!val || 'Обязательное поле',
                ]"
              />
              <div
                class="col-1 row justify-center align-center self-center q-mb-sm"
              >
                <q-btn
                  class="releaseInfoToggle"
                  :loading="loadingServices[service]"
                  @click="toggleOpenServiceChangelog(service)"
                  text-color="primary"
                  round
                  :disable="
                    !selectedServicesVersions[service] ||
                    Object.keys(loadingServices).length !== 0
                  "
                >
                  <q-icon name="information" />
                </q-btn>
              </div>
            </div>
            <q-slide-transition>
              <q-input
                :value="getServiceInfo(service)"
                type="textarea"
                readonly
                filled
                class="q-ma-none"
                v-if="openedServiceChangelogs.includes(service)"
              >
              </q-input>
            </q-slide-transition>
          </div>
        </q-form>
      </q-card-section>
      <q-card-actions align="right">
        <q-btn
          type="button"
          color="primary"
          :label="$t('registries.objectCard.cancel')"
          @click="cancelImport"
        />
        <q-btn :label="submitBtnLabel" color="primary" @click="proceedImport" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script lang="ts">
import { Component, Prop, PropSync, Vue } from 'vue-property-decorator';
import { QForm } from 'quasar';
import {
  serviceImportModel,
  ServiceInfoMap,
  ServiceItemsMap,
} from '@components/services/index';
import cloneDeep from 'lodash/cloneDeep';
import { WorkflowServiceApi } from '@openapi/wf';
import { notifyError } from '@utils/notify';

const wfApi = new WorkflowServiceApi();

@Component({})
export default class ServiceImportDialog extends Vue {
  @PropSync('openDialog') open: boolean;
  @Prop() model!: serviceImportModel;
  @Prop() userTask!: string;
  @PropSync('servicesLoading') loadingServices!: { [service: string]: boolean };
  @Prop({ default: 'Импорт услуг' }) submitBtnLabel: string;
  @Prop({ default: 'Доступные услуги' }) title: string;

  private selectedServices: Record<string, boolean> = {};
  private selectedServicesVersions: Record<string, string> = {};
  private importInProgress = false;
  private openedServiceChangelogs: string[] = [];

  $refs!: {
    form: QForm;
  };

  created() {
    this.init();
  }

  private init() {
    const selected = this.model.outdate || [];

    Object.keys(this.available).forEach((code: string) => {
      this.$set(this.selectedServices, code, selected.includes(code));
      if (this.available[code].length === 1) {
        // выбор версии по умолчанию если она одна в массиве
        this.$set(this.selectedServicesVersions, code, this.available[code][0]);
      }
    });
  }

  private get selectedAll(): boolean {
    return Object.values(this.selectedServices).every(
      (isSelected) => !!isSelected
    );
  }

  private set selectedAll(state) {
    Object.keys(this.available).forEach((code: string) => {
      this.$set(this.selectedServices, code, state);
    });
  }

  private get available(): ServiceItemsMap {
    return this.model.available || {};
  }

  private get serviceInfo(): ServiceInfoMap {
    return this.model.info || {};
  }

  private getServiceInfo(service: string): string {
    const displayVersion = this.selectedServicesVersions[service];
    const displayData = this.serviceInfo[service]?.[displayVersion];
    return JSON.stringify(displayData, null, 2);
  }

  private async loadService(service: string, version: string): Promise<void> {
    this.$set(this.selectedServicesVersions, service, version);

    if (
      !this.serviceInfo?.[service]?.[this.selectedServicesVersions[service]]
    ) {
      await this.fetchNewChangelog(service);
    }
  }

  private async fetchNewChangelog(service: string): Promise<void> {
    const logs = cloneDeep(this.loadingServices);
    logs[service] = true;
    this.loadingServices = logs;
    try {
      await wfApi.submitForm(this.userTask, {
        getinfo: {
          name: service,
          version: this.selectedServicesVersions[service],
        },
        action: 'info',
      });
    } catch (e) {
      notifyError(e.message.toString());
      const logs = cloneDeep(this.loadingServices);
      delete logs[service];
      this.loadingServices = logs;
    }
  }

  private async toggleOpenServiceChangelog(service: string) {
    if (
      !this.serviceInfo?.[service]?.[this.selectedServicesVersions[service]]
    ) {
      await this.fetchNewChangelog(service);
    }

    const serviceIdx = this.openedServiceChangelogs.findIndex(
      (value) => value === service
    );
    if (serviceIdx !== -1) {
      this.openedServiceChangelogs.splice(serviceIdx, 1);
    } else {
      this.openedServiceChangelogs.push(service);
    }
  }

  private proceedImport() {
    this.$refs.form.validate().then(async (success) => {
      if (success) {
        const selected = Object.entries(this.selectedServices).reduce(
          (result: Record<string, string>, [code, isSelected]) => {
            const version = this.selectedServicesVersions[code];
            if (isSelected && version) result[code] = version;
            return result;
          },
          {}
        );
        this.importInProgress = true;
        await wfApi.submitForm(this.userTask, {
          selected,
          action: 'update',
        });
        this.open = false;
      }
    });
  }

  private cancelImport() {
    this.open = false;
    if (!this.importInProgress) {
      wfApi.submitForm(this.userTask, {
        action: 'exit',
      });
    }
    this.openedServiceChangelogs = [];
    this.selectedServicesVersions = {};
    this.loadingServices = {};
    this.selectedAll = false;
  }
}
</script>
<style>
.service-el {
  border-bottom: 1px solid transparent;
  transition: border-bottom-color 300ms ease;
}

.service-el:hover,
.service-el:focus-within {
  border-color: #e3e3e3;
}

.releaseInfoToggle {
  max-width: 42px;
  overflow: hidden;
}
</style>
