<i18n>
{
  "en": {
    "ProcessEdit": {
      "editProcess": "Edit process",
      "createProcess": "Create process",
      "editTable": "Edit table",
      "createTable": "Create table",
      "name": "Name",
      "type": "Type",
      "value": "Value",
      "fileSuccess": "File success",
      "fileError": "File error",
      "AddDocumentEmptyError": "Fill in all the fields",
      "AddDocumentCoreError": "There is already an entry with this code",
      "import": "Import",
      "dataPrepare": "Data prepare",
      "routing": "Routing",
      "interdepartmental": "Interdepartmental interaction",
      "baseProcess": "Base process",
      "custom": "Custom",
      "list": "List",
      "interagencyRequestTemplate": "Interagency request template"
    }
  },
  "ru": {
    "ProcessEdit": {
      "editProcess": "Редактирование процесса",
      "createProcess": "Создание процесса",
      "editTable": "Редактирование таблицы",
      "createTable": "Создание таблицы",
      "name": "Имя",
      "type": "Тип",
      "value": "Значение",
      "inputHint": "Заполняется автоматически, в зависимости от имени загружаемого файла",
      "fileSuccess": "Документ успешно загружен",
      "fileError": "Ошибка при загрузке процесса",
      "AddDocumentEmptyError": "Заполните все поля",
      "AddDocumentCoreError": "Запись с таким кодом уже есть",
      "import": "Импорт",
      "dataPrepare": "Предподготовка данных",
      "routing": "Маршрутизация",
      "interdepartmental": "Межведомственное взаимодействие",
      "baseProcess": "Базовый процесс",
      "custom": "Свой",
      "list": "Список",
      "interagencyRequestTemplate": "Шаблон межведомственного запроса"
    }
  }
}
</i18n>

<template>
  <q-dialog v-model="dialogOpen" @hide="clearModel">
    <q-card class="full-width no-shadow" style="max-width: 800px">
      <q-card-section>
        <div class="text-h6">
          {{ title }}
        </div>
      </q-card-section>

      <div v-if="dialogData">
        <q-card-section class="q-pb-md q-gutter-y-md">
          <q-input
            filled
            ref="processDialogName"
            :rules="[validateFileName]"
            :error-message="$t('processes.validationErrors.errorProcessId')"
            v-model="dialogData.name"
            :label="$t('ProcessEdit.name')"
          />
          <div
            v-if="schemaName === 'Process'"
            class="flex justify-between items-start no-wrap"
          >
            <q-input
              v-if="isCustomProcessType"
              filled
              class="full-width"
              v-model="dialogData.type"
              :label="$t('ProcessEdit.type')"
            />
            <q-select
              v-else
              filled
              emit-value
              map-options
              class="full-width"
              v-model="dialogData.type"
              :disable="isTypeSelectDisabled"
              :options="processTypes"
              :label="$t('ProcessEdit.type')"
            />
            <q-btn
              v-if="!isTypeSelectDisabled"
              color="primary"
              class="q-ml-sm flex-item-button"
              filled
              :label="
                isCustomProcessType
                  ? $t('ProcessEdit.list')
                  : $t('ProcessEdit.custom')
              "
              @click="toggleType"
            />
          </div>

          <q-select
            filled
            v-else
            v-model="dialogData.serviceCode"
            :options="goalsList"
            :label="this.$t('services.ProcessesCard.serviceCode')"
            use-chips
            stack-label
          />

          <codemirror
            v-if="isEditForm"
            :value="xml"
            :options="cmOptions"
            @input="onCmCodeChange"
          />

          <div v-else class="flex justify-between items-start no-wrap">
            <q-file
              :accept="schemaName === 'Process' ? '.bpmn' : '.dmn'"
              clearable
              filled
              counter
              label="Загрузить"
              ref="processDialogFile"
              :rules="[validateFileName]"
              :error-message="
                upload.file
                  ? $t('processes.validationErrors.errorFileName')
                  : $t('processes.validationErrors.errorFileRequired')
              "
              v-model="upload.file"
            />
            <q-btn
              v-if="schemaName === 'Process'"
              color="primary"
              class="q-ml-sm flex-item-button"
              filled
              :label="this.$t('ProcessEdit.import')"
              @click="isBpmnCloudLoader = true"
            />
            <BaseBpmnCloudLoader
              :show.sync="isBpmnCloudLoader"
              value-mode="file"
              @getBpmn="bpmnSchemaUploadHandler"
            />
          </div>
        </q-card-section>
        <q-card-actions align="right">
          <q-btn
            @click="edit"
            label="Cохранить"
            v-if="isEditForm"
            unelevated
            :loading="loading"
            color="primary"
          />

          <q-btn
            @click="create"
            label="Создать"
            unelevated
            v-else
            :loading="loading"
            color="primary"
          />

          <q-btn
            @click="dialogOpen = false"
            label="Закрыть"
            color="white"
            :loading="loading"
            text-color="primary"
            outline
          />
        </q-card-actions>
      </div>
      <div v-else>{{ $t('ProcessEdit.noResult') }}</div>
    </q-card>
  </q-dialog>
</template>

<script lang="ts">
// @ts-nocheck
import { codemirror } from 'vue-codemirror';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/selection/active-line.js';
import BaseBpmnCloudLoader from '@components/common/base-bpmn-cloud-loader/index.vue';
import { multipleUseValidator } from '@/utils/field-validation/multipleUse';
import { fileNameValidator } from '@/utils/field-validation/fileName';
import { ValidatorResponse } from '@interfaces/base/ValidatorRespose';
import {
  createDocument,
  fetchAttachment,
  updateAttachment,
  updateDocument,
} from '@services/registry-service';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';

export default {
  props: ['common', 'model', 'lockedType'],
  name: 'process_dialog',
  components: {
    codemirror,
    BaseBpmnCloudLoader,
  },
  data() {
    return {
      mode: 'edit',
      schemaName: 'Process',
      xml: '',
      dialogData: null,
      loading: false,
      newDocumentsModel: null,
      addDocuments: false,
      dialogOpen: false,
      isBpmnCloudLoader: false,
      isCustomProcessType: false,
      cmOptions: {
        tabSize: 4,
        mode: 'text/html',
        styleActiveLine: true,
        lineNumbers: true,
        line: true,
      },

      defaultProcessModel: {
        type: '',
        name: '',
      },
      defaultTableModel: {
        type: '',
        serviceCode: '',
      },

      processTypes: [
        {
          label: String(this.$t('ProcessEdit.import')),
          value: 'import',
        },
        {
          label: String(this.$t('ProcessEdit.dataPrepare')),
          value: 'dataPrepare',
        },
        {
          label: String(this.$t('ProcessEdit.routing')),
          value: 'route',
        },
        {
          label: String(this.$t('ProcessEdit.interdepartmental')),
          value: 'interagencies',
        },
        {
          label: String(this.$t('ProcessEdit.baseProcess')),
          value: 'baseProcess',
        },
        {
          label: String(this.$t('ProcessEdit.interagencyRequestTemplate')),
          value: 'MzBPMNTemplate',
        },
      ],

      upload: {
        file: null,
        loading: false,
      },
    };
  },

  computed: {
    isEditForm() {
      return this.mode === 'edit';
    },
    title() {
      let title = '';
      if (this.schemaName === 'Process') {
        title = this.isEditForm
          ? this.$t('ProcessEdit.editProcess')
          : this.$t('ProcessEdit.createProcess');
      } else {
        title = this.isEditForm
          ? this.$t('ProcessEdit.editTable')
          : this.$t('ProcessEdit.createTable');
      }
      return title;
    },
    goalsList() {
      const card = cloneDeep(
        this.$store.getters['registryService/services/servicesCard']
      );
      return (card.goalsList || []).map((i) => i.value);
    },
    serviceId() {
      return this.$route.params.id;
    },
    isTypeSelectDisabled() {
      return !!this.lockedType;
    },
  },

  methods: {
    validateFileName(value: string | File): boolean {
      if (typeof value === 'string') {
        return value === value.match(/^[а-яёЁА-Я\w.]+$/)?.[0];
      } else if (value instanceof File) {
        return value.name === value.name.match(/^[а-яёЁА-Я\w.]+$/)?.[0];
      }
      return true;
    },
    validateCodeMirrorProcessId(): boolean {
      const processIdx = this.xml.indexOf('<bpmn:process');
      const processIdIdx = this.xml.indexOf('id="', processIdx) + 4;
      const processIdEndIdx = this.xml.indexOf('"', processIdIdx);
      const processId = this.xml.slice(processIdIdx, processIdEndIdx);
      return processId === processId?.match(/^[а-яёЁА-Я\w.]+$/)?.[0];
    },
    toggleType() {
      this.isCustomProcessType = !this.isCustomProcessType;
      this.dialogData.type = '';
    },

    bpmnSchemaUploadHandler(val) {
      this.dialogData.name = val.code;
      this.upload.file = val.value;
    },

    onCmCodeChange(newCode) {
      this.xml = newCode;
    },
    validateEdit(): ValidatorResponse {
      try {
        Object.entries(this.$refs).forEach(([ref, refObj]) => {
          if (ref.indexOf('processDialog') !== -1) {
            refObj.validate();
          }
        });
      } catch (e) {
        console.error('process dialog validation fail: ', e.message);
      }
      if (!get(this.dialogData, 'name')) {
        return {
          isValid: false,
          message: this.$t('ProcessEdit.AddDocumentEmptyError'),
        };
      }
      if (!this.validateFileName(this.dialogData.name)) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.fileError'),
        };
      }
      if (this.xml && !this.validateCodeMirrorProcessId()) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.errorProcessId'),
        };
      }
      return {
        isValid: true,
        message: '',
      };
    },
    async validate(): Promise<ValidatorResponse> {
      try {
        Object.entries(this.$refs).forEach(([ref, refObj]) => {
          if (ref.indexOf('processDialog') !== -1) {
            refObj.validate();
          }
        });
      } catch (e) {
        console.error('process dialog validation fail: ', e.message);
      }
      if (!get(this.dialogData, 'name')) {
        return {
          isValid: false,
          message: this.$t('ProcessEdit.AddDocumentEmptyError'),
        };
      }
      if (!this.validateFileName(this.dialogData.name)) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.fileError'),
        };
      }
      if (!this.upload?.file) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.errorFileRequired'),
        };
      }
      if (!this.validateFileName(this.upload.file)) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.fileError'),
        };
      }
      const fileNameValidation = await fileNameValidator(this.upload.file);
      if (!fileNameValidation.isValid) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.fileNameMatchId', {
            processId: fileNameValidation.message ?? '',
          }),
        };
      }
      const multipleUseValidation = await multipleUseValidator(
        this.upload.file,
        this.serviceId
      );
      if (!multipleUseValidation.isValid) {
        return {
          isValid: false,
          message: this.$t('processes.validationErrors.idAlreadyInUse'),
        };
      }
      if (!get(this.dialogData, 'name')) {
        return {
          isValid: false,
          message: this.$t('ProcessEdit.AddDocumentEmptyError'),
        };
      }
      return {
        isValid: true,
        message: '',
      };
    },

    async create() {
      const validation = await this.validate();
      if (!validation.isValid) {
        this.$q.notify({
          color: 'negative',
          icon: 'error',
          message: validation.message,
        });
        return;
      }

      try {
        this.loading = true;
        const { data: docResult } = await createDocument(this.schemaName, [
          this.dialogData,
        ]);

        this.dialogData.id = get(docResult, 'objectIds[0]');
        this.dialogData.schema = this.schemaName;

        const extension = this.schemaName === 'Process' ? 'bpmn' : 'dmn';

        if (get(this.upload, 'file')) {
          const file = new File(
            [this.upload.file],
            `${get(this.dialogData, 'name')}.${extension}`
          );

          const { data: attachResult } = await updateAttachment(
            this.schemaName,
            get(this.dialogData, 'id'),
            undefined,
            [file]
          );

          this.dialogData.modified = attachResult.modified;

          this.dialogData.attachment = true;

          const { data: docResult } = await updateDocument(this.schemaName, [
            this.dialogData,
          ]);
          this.dialogData.modified = docResult.modified;
        }
        const card = cloneDeep(
          this.$store.getters['registryService/services/servicesCard']
        );

        if (this.schemaName === 'Process') {
          const processes = card.processes || [];

          processes.push(this.dialogData);

          card.processes = processes;
        } else {
          const tables = card.tables || [];

          tables.push(this.dialogData);

          card.tables = tables;
        }

        this.$store.dispatch('registryService/services/setCardForJson', card);
        const newProcessData = {
          ...this.dialogData,
          ...this.upload,
          processTypes: this.processTypes,
        };
        this.$emit('created', newProcessData);
      } catch (e) {
        this.$q.notify({
          color: 'negative',
          icon: 'error',
          message: e.message,
        });
      } finally {
        this.loading = false;
        this.dialogOpen = false;
      }
    },

    async edit() {
      const validation = this.validateEdit();
      if (!validation.isValid) {
        this.$q.notify({
          color: 'negative',
          icon: 'error',
          message: validation.message,
        });
        return;
      }

      try {
        this.loading = true;
        const { id, name } = this.dialogData;
        if (this.xml) {
          const blob = new Blob([this.xml], { type: 'multipart/form-data' });
          const extension = this.schemaName === 'Process' ? 'bpmn' : 'dmn';
          const file = new File([blob], `${name}.${extension}`);

          const { data: attachmentResult } = await updateAttachment(
            this.schemaName,
            id,
            undefined,
            [file]
          );

          this.dialogData.modified = attachmentResult.modified;
          this.dialogData.attachment = true;
        }
        const { data: result } = await updateDocument(this.schemaName, [
          this.dialogData,
        ]);

        this.dialogData.modified = result.modified;

        const card = cloneDeep(
          this.$store.getters['registryService/services/servicesCard']
        );

        if (this.schemaName === 'Process') {
          const process = card.processes || [];

          process.map((it) =>
            it.id === this.dialogData.id ? this.dialogData : it
          );

          card.processes = process;
        } else {
          const tables = card.tables || [];

          tables.map((it) =>
            it.id === this.dialogData.id ? this.dialogData : it
          );

          card.tables = tables;
        }

        this.$store.dispatch('registryService/services/setCardForJson', card);

        this.$q.notify({
          color: 'positive',
          icon: 'check_circle_outline',
          message: 'Успешно',
        });
      } catch (e) {
        this.$q.notify({
          color: 'negative',
          icon: 'error',
          message: e.message,
        });
      } finally {
        this.loading = false;
        this.dialogOpen = false;
      }
    },
    setLockedType() {
      const foundType = this.processTypes.find(
        (t) => t.value === this.lockedType
      );
      if (foundType) {
        this.dialogData = {
          ...this.dialogData,
          type: foundType.value,
        };
      }
    },

    clearModel() {
      this.upload.file = null;
      this.xml = null;
      this.dialogData = cloneDeep(this.defaultProcessModel);
      this.$emit('commonModels', {
        ProcessDialog: {
          data: null,
        },
      });
    },
  },

  destroyed() {
    this.clearModel();
  },

  watch: {
    'common.models.ProcessDialog.data': {
      immediate: true,
      handler: function (val) {
        if (val) {
          this.dialogOpen = true;
          if (val && val.mode === 'createMode') {
            this.mode = 'create';
            this.dialogData =
              this.schemaName === 'Process'
                ? cloneDeep(this.defaultProcessModel)
                : cloneDeep(this.defaultTableModel);
            this.schemaName = val.schema;
            return;
          }

          this.dialogData = val;
          this.mode = 'edit';
          if (!val.name || !val.id) return;
          this.schemaName = val.schema;
          const extension = this.schemaName === 'Process' ? 'bpmn' : 'dmn';
          fetchAttachment(
            this.schemaName,
            val.id,
            `${val.name}.${extension}`,
            false
          ).then((response) => {
            this.xml = response;
          });
        }
      },
    },
    dialogOpen: {
      immediate: true,
      handler(open) {
        if (open) {
          if (this.lockedType) {
            this.setLockedType();
          }
        }
      },
    },
  },
};
</script>

<style scoped lang="scss">
.flex-item-button {
  min-width: 90px;
  height: 56px;
}
</style>