<i18n>
{
  "en": {
    "title": "Screen forms",
    "forms": {
      "type": "Type",
      "actions": "Actions",
      "tableTitle": "Screen forms",
      "id": "id",
      "noFormsResult": "No Forms result",
      "jsonEditor": "JSON editor",
      "JSONDialog": {
        "cancel": "Cancel",
        "create": "Create",
        "title": "Paste schema in code editor"
      },
      "errors": {
        "loadForms": "There was an error while loading view schemas, try refreshing",
        "loadFile": "There was an error while loading file, try again",
        "createSchema": "There was an error while creating the schema, try again",
        "deleteForm": "There was an error while deleting the schema, try again",
        "downloadForm": "There was an error while downloading the schema, try again",
        "schema": {
          "prefix": "The loaded schema has invalid list of ",
          "attrSuffix": "attributes",
          "groupSuffix": "groups"
        }
      },
      "table": {
        "reception": "Reception",
        "applicant": "Applicant",
        "statementForm": "Statement details",
        "interagencies": "Interdepartmental requests",
        "documents": {
          "mvz": "Documents added by a specialist",
          "applicant": "Applicant",
          "result": "Processes"
        },
        "form": "Schema",
        "formRequest": "Request schema",
        "formResponse": "Response schema",
        "btn": {
          "editForm": "Edit schema",
          "createForm": "Create empty schema",
          "createFormFromFile": "Create schema from file",
          "createFormFromJson": "Create schema from JSON",
          "downloadForm": "Download schema",
          "delete": "Delete",
          "addRow": "Add another form schema"
        },
        "customFormTooltip": "The custom form is used"
      }
    }
  },
  "ru": {
    "title": "Экранные формы",
    "forms": {
      "type": "Тип",
      "actions": "Действия",
      "tableTitle": "Формы заявлений",
      "id": "Идентификатор",
      "noFormsResult": "Нет результата",
      "jsonEditor": "Редактор JSON",
      "JSONDialog": {
        "cancel": "Отмена",
        "create": "Создать",
        "title": "Вставьте json формы"
      },
      "errors": {
        "loadForms": "Ошибка при загрузке экранных форм",
        "loadFile": "Произошла ошибка при загрузке файла, попробуйте снова",
        "createSchema": "Во время создания схемы произошла ошибка, попробуйте снова",
        "deleteForm": "Во время удаления схемы произошла ошибка, попробуйте снова",
        "downloadForm": "Во время скачивания схемы произошла ошибка, попробуйте снова",
        "schema": {
          "prefix": "В загруженной схеме неверно настроен список",
          "attrSuffix": "атрибутов",
          "groupSuffix": "групп"
        }
      },
      "table": {
        "reception": "Очный прием",
        "applicant": "Заявитель",
        "statementForm": "Данные заявления",
        "interagencies": "Межведомственные запросы",
        "documents": {
          "mvz": "Документы добавляемые специалистом",
          "applicant": "Входящие документы",
          "result": "Результирующие документы"
        },
        "form": "Экранная форма",
        "formRequest": "Экранная форма запроса",
        "formResponse": "Экранная форма ответа",
        "btn": {
          "editForm": "Редактировать форму",
          "createForm": "Создать пустую форму",
          "createFormFromFile": "Создать из файла",
          "createFormFromJson": "Создать из JSON",
          "downloadForm": "Скачать форму",
          "delete": "Удалить",
          "addRow": "Добавить еще одну схему формы"
        },
        "customFormTooltip": "Используется форма текущей услуги"
      }
    }
  }
}
</i18n>

<template>
  <div v-if="common.active === 'forms' && !loadingCard" class="q-pb-md">
    <q-card class="no-shadow q-gutter-y-md q-mx-md no-border-radius">
      <div
        class="q-gutter-y-md bg-white q-px-lg q-pb-md q-pt-sm q-mt-md q-mx-md rounded-borders"
      >
        <div v-if="card && tableData">
          <div class="text-h6">
            {{ $t('title') }}
          </div>
          <q-table
            :columns="[]"
            :data="tableData"
            hide-bottom
            hide-top
            row-key="name"
            wrap-cells
            :pagination.sync="pagination"
          >
            <template #body="{ row, key }">
              <q-tr :key="key">
                <q-td
                  colspan="3"
                  class="text-weight-bold"
                  :style="{ fontSize: '14px' }"
                >
                  {{ row.label }}
                </q-td>
              </q-tr>
              <template v-for="entry in row.data">
                <q-tr :key="entry.id" class="text-weight-bold">
                  <q-td :style="{ paddingLeft: '30px' }" colspan="2">
                    <span>
                      {{ entry.label }}
                    </span>
                    <q-btn
                      v-if="canAddAnotherSchema(row, entry)"
                      color="secondary"
                      icon="add"
                      class="q-ml-sm"
                      round
                      dense
                      flat
                      @click="addSchemaEntry(row, entry)"
                    >
                      <q-tooltip :delay="500">
                        {{ $t('forms.table.btn.addRow') }}
                      </q-tooltip>
                    </q-btn>
                  </q-td>
                  <q-td>
                    <span>
                      {{ entry.name }}
                    </span>
                  </q-td>
                </q-tr>
                <q-tr
                  v-for="form in entry.forms"
                  :key="`${entry.id ? entry.id : entry.name}_${form.key}_${
                    form.form ? form.form.id : 'new'
                  }`"
                  class="bg-grey-1"
                >
                  <q-td :style="{ paddingLeft: '60px' }">
                    <div>{{ $t(`forms.table.${form.key}`) }}</div>
                    <q-badge
                      v-if="form.form"
                      outline
                      align="middle"
                      color="grey"
                    >
                      {{ form.form.name }}
                    </q-badge>
                  </q-td>
                  <q-td>
                    <template v-if="form.form">
                      {{ form.form.label }}
                    </template>
                  </q-td>
                  <q-td>
                    <div
                      v-if="form.form"
                      :style="{ minWidth: '160px' }"
                      class="flex full-width items-center"
                    >
                      <q-btn
                        v-if="state === 'inProgress' && canEdit"
                        dense
                        flat
                        color="primary"
                        icon="edit"
                        class="q-mr-sm"
                        @click="edit(form.form.id)"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.editForm') }}
                        </q-tooltip>
                      </q-btn>
                      <q-btn
                        dense
                        flat
                        color="primary"
                        icon="cloud_download"
                        @click="download(form.form)"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.downloadForm') }}
                        </q-tooltip>
                      </q-btn>
                      <q-icon
                        v-if="form.form.parentServiceId"
                        class="self-start"
                        size="16px"
                        name="settings"
                        color="teal-8"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.customFormTooltip') }}
                        </q-tooltip>
                      </q-icon>
                      <q-btn
                        v-if="state === 'inProgress' && canEdit"
                        dense
                        flat
                        color="negative"
                        icon="delete"
                        class="q-ml-auto"
                        @click="
                          deleteFromRegistry(entry, form.form && form.form.id, form.key)
                        "
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.delete') }}
                        </q-tooltip>
                      </q-btn>
                    </div>
                    <div
                      v-if="!form.form && state === 'inProgress' && canEdit"
                      class="flex items-center full-width"
                    >
                      <q-btn
                        dense
                        flat
                        color="secondary"
                        icon="add_box"
                        class="q-mr-sm"
                        @click="create(entry, form.key)"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.createForm') }}
                        </q-tooltip>
                      </q-btn>
                      <q-btn
                        dense
                        flat
                        color="secondary"
                        icon="note_add"
                        class="q-mr-sm"
                        @click="pickFile(entry, form.key)"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.createFormFromFile') }}
                        </q-tooltip>
                      </q-btn>
                      <q-btn
                        dense
                        flat
                        color="secondary"
                        icon="content_paste"
                        class="q-mr-sm"
                        @click="launchJSONEditor(entry, form.key)"
                      >
                        <q-tooltip :delay="500">
                          {{ $t('forms.table.btn.createFormFromJson') }}
                        </q-tooltip>
                      </q-btn>
                    </div>
                  </q-td>
                </q-tr>
              </template>
              <q-tr no-hover :style="{ minHeight: '20px' }">
                <q-td colspan="3"></q-td>
              </q-tr>
            </template>
          </q-table>
          <q-file
            ref="filePicker"
            v-model="file"
            class="hidden"
            accept=".json"
          />
          <q-dialog v-model="pasteJSONDialog">
            <q-card
              class="service-registry-json-dialog"
              :style="{ minWidth: '75vw' }"
            >
              <q-card-section class="text-h6">
                {{ $t('forms.JSONDialog.title') }}
              </q-card-section>
              <q-card-section class="q-pa-none">
                <v-json-editor
                  ref="jsoneditor"
                  v-model="rawJSON"
                  height="500px"
                  :options="{
                    mode: 'code',
                    mainMenuBar: false,
                  }"
                />
              </q-card-section>
              <q-card-actions align="right">
                <q-btn
                  color="negative"
                  :label="$t('forms.JSONDialog.cancel')"
                  @click="pasteJSONDialog = false"
                ></q-btn>
                <q-btn
                  color="primary"
                  :label="$t('forms.JSONDialog.create')"
                  @click="createFromJSON"
                ></q-btn>
              </q-card-actions>
            </q-card>
          </q-dialog>
        </div>
        <q-inner-loading :showing="loading">
          <q-spinner-gears color="primary" size="50px" />
        </q-inner-loading>
      </div>
    </q-card>
  </div>
</template>

<script lang="ts">
// @ts-nocheck
// @ts-ignore
import VJsonEditor from 'v-jsoneditor';
import { exportFile } from 'quasar';
import { COMMON_AUTHOR, COMMON_INNER_PETITION } from '@/constants/schemas';
import {
  DocumentApi,
  FormApi,
  InteragencyApi,
  ModifyResponse,
  ServiceApi,
} from '@openapi/registry-service';
import { updateService } from '@services/registry-service';
import get from 'lodash/get';
import head from 'lodash/head';
import isEmpty from 'lodash/isEmpty';
import { notifyError } from '@utils/notify';

const documentApi = new DocumentApi();
const formApi = new FormApi();
const interagencyApi = new InteragencyApi();
const serviceApi = new ServiceApi();

export default {
  name: 'ServiceForms',
  components: {
    VJsonEditor,
  },
  props: ['common', 'model'],
  data() {
    return {
      tableData: null,
      loading: true,
      file: null,
      current: null,
      pasteJSONDialog: false,
      rawJSON: {},
      cmOptions: {
        mode: {
          name: 'javascript',
          json: true,
        },
        tabSize: 2,
        styleActiveLine: true,
        line: true,
        theme: 'ayu-mirage',
      },
      pagination: {
        rowsPerPage: 0,
      },
    };
  },
  computed: {
    loadingCard() {
      return (
        this.$store.getters['registryService/services/servicesLoading'] || false
      );
    },
    card() {
      return this.$store.getters['registryService/services/servicesCard'];
    },
    interagencies() {
      return get(this.card, 'interagencies');
    },
    formsExtra() {
      return get(this.card, 'formsExtra');
    },
    applicant() {
      return this.formExtraHendler('Applicant');
    },
    reception() {
      return this.formExtraHendler('Reception');
    },
    documents() {
      return get(this.card, 'documents');
    },
    statementForm() {
      return get(this.card, 'form');
    },
    state() {
      return get(this.card, 'state') || 'inProgress';
    },
    documentsMVZ() {
      return this.groupDocuments('MVZ');
    },
    documentsApplicant() {
      return this.groupDocuments('Applicant');
    },
    documentsResult() {
      return this.groupDocuments('result');
    },
    canEdit(): boolean {
      return this.$store.getters['registryService/services/canEdit'];
    },
  },
  watch: {
    state: {
      async handler(state) {
        this.tableData = await this.generateTableData(state);
      },
    },
    file: {
      handler(file) {
        const [entry, formKey] = this.current || [];

        this.current = null;

        if (!file || !entry || !formKey) return;

        this.createFromFile(entry, formKey, file);

        this.file = null;
      },
    },
    card: {
      async handler(card) {
        if (!isEmpty(card)) {
          this.tableData = await this.generateTableData(this.state);
        }
      },
    },
  },
  async mounted() {
    if (!isEmpty(this.card)) {
      this.tableData = await this.generateTableData(this.state);
    }
  },
  methods: {
    isExtraForm(name) {
      return name === COMMON_AUTHOR || name === COMMON_INNER_PETITION;
    },
    formExtraHendler(name) {
      if (this.formsExtra) {
        const arr = this.formsExtra.find((item) => item.name === `${name}Base`);
        return arr
          ? [arr]
          : [
              {
                name,
                label: name,
              },
            ];
      } else {
        return [
          {
            name,
            label: name,
          },
        ];
      }
    },

    canAddAnotherSchema(tableEntry, entry) {
      if (!tableEntry.multiple || !entry.forms) {
        return;
      }
      return entry.forms.every((form) => form.form);
    },
    addSchemaEntry(tableEntry, entry) {
      const isMultiple = tableEntry.multiple;
      const formKeys = entry.formKeys;

      const newFormEntries = formKeys.map((formKey) => ({
        form: void 0,
        key: formKey,
        multiple: isMultiple,
      }));

      const updated =
        entry.forms && Array.isArray(entry.forms)
          ? [...entry.forms, ...newFormEntries]
          : newFormEntries;

      this.$set(entry, 'forms', updated);
    },
    download(form) {
      if (!form) {
        notifyError(this.$t('forms.errors.downloadForm', 'warning'));

        return;
      }
      const schemaBlob = new Blob([JSON.stringify(form, null, 2)], {
        type: 'application/json',
      });

      exportFile(form.label + '.json', schemaBlob, 'application/json');
    },
    // todo refresh by 1 method from index?
    refresh() {
      this.$store.dispatch('registryService/services/getServiceMeta', {
        id: get(this.common, 'id'),
        opts: {
          expand: [
            'interagencies.formRequest',
            'interagencies.formResponse',
            'documents.form',
            'formsExtra',
          ],
        },
      });
    },
    edit(formId) {
      this.$router.push({
        name: 'cards',
        params: {
          id: formId,
          path: 'schemas',
        },
        query: {
          active: 'index',
          components: 'index',
          serviceId: this.common.id,
        },
      });
    },
    generateFormName(entry = {}) {
      return entry.name;
    },
    createEmptySchema(entry, formKey) {
      return {
        name: this.generateFormName(entry, formKey),
        label: entry.label,
        attrList: [],
        groupList: [],
      };
    },
    async create(entry, formKey) {
      const emptySchema = this.createEmptySchema(entry, formKey);

      this.loading = true;

      try {
        if (entry.name === 'Applicant' || entry.name === 'Reception') {
          await this.createInRegistryFromGit(entry, formKey, emptySchema);
        } else {
          await this.createInRegistry(entry, formKey, emptySchema);
        }
      } catch (err) {
        const message = err.message
          ? err.message
          : this.$t('forms.errors.createSchema');

        notifyError(message);

        console.error(
          '[service-constructor][forms][create empty] Could not parse file',
          { formKey, entry },
          err
        );
      } finally {
        this.loading = false;

        this.refresh();
      }
    },
    pickFile(entry, formKey) {
      this.current = [entry, formKey];

      const filePicker = this.$refs.filePicker;

      filePicker.pickFiles();
    },
    async createFromFile(entry, formKey, file) {
      this.loading = true;

      const schemaStr = await file.text();

      await this.createFromString(entry, formKey, schemaStr);

      this.refresh();
    },
    async createFromString(entry, formKey, schemaStr) {
      try {
        const name = this.generateFormName(entry, formKey);
        const schema = JSON.parse(schemaStr);

        const attrListValid = schema.attrList && Array.isArray(schema.attrList);
        const groupListValid =
          schema.groupList && Array.isArray(schema.groupList);

        if (!attrListValid || !groupListValid) {
          const prefix = this.$t('forms.errors.schema.prefix');
          const suffix = !attrListValid
            ? this.$t('forms.errors.schema.attrSuffix')
            : this.$t('forms.errors.schema.groupSuffix');

          const message = `${prefix} ${suffix}`;

          throw new Error(message);
        }

        schema.name = schema.name || name;
        schema.label = entry.label;

        await this.createInRegistry(entry, formKey, schema);
      } catch (err) {
        const message = err.message
          ? err.message
          : this.$t('forms.errors.loadFile');

        notifyError(message);

        console.error(
          '[service-constructor][forms][create from file] Could not parse file',
          { formKey, entry, schemaStr },
          err
        );
      } finally {
        this.loading = false;
        this.pasteJSONDialog = false;
      }
    },
    launchJSONEditor(entry, formKey) {
      this.current = [entry, formKey];
      this.rawJSON = this.createEmptySchema(entry, formKey);
      this.pasteJSONDialog = true;
    },
    async createFromJSON() {
      const [entry, formKey] = this.current;

      await this.createFromString(entry, formKey, JSON.stringify(this.rawJSON));

      this.refresh();
    },
    groupDocuments(source) {
      return this.prepareData(this.documents).filter(
        (document) => document.raw.source === source
      );
    },
    deleteEmptyForms(table: any) {
      const filteredTable = table.filter((row) => {
        return row.data.some((item) => item.forms[0].form);
      });
      filteredTable.forEach((item) => {
        item.data = item.data.filter((el) => el.forms[0].form);
      });
      return filteredTable;
    },
    generateTableData(state) {
      const readyToPublish = state === 'readyToPublish';
      try {
        const table = [
          {
            id: 'applicant',
            label: this.$t('forms.table.applicant'),
            data: this.prepareData(this.applicant, {
              label: 'label',
              name: 'name',
            }),
          },
          {
            id: 'statementForm',
            label: this.$t('forms.table.statementForm'),
            data: this.prepareData([this.card], {
              label: 'shortName',
              name: 'name',
            }),
          },
          {
            id: 'interagencies',
            label: this.$t('forms.table.interagencies'),
            data: this.prepareData(this.interagencies, {
              label: 'requestName',
              name: 'class',
              forms: ['formRequest', 'formResponse'],
              mapFormsToParentServiceId: true
            }),
          },
          {
            id: 'documentsResult',
            multiple: true,
            label: this.$t('forms.table.documents.result'),
            data: this.documentsResult,
          },
          {
            id: 'documentsMVZ',
            multiple: true,
            label: this.$t('forms.table.documents.mvz'),
            data: this.documentsMVZ,
          },
          {
            id: 'documentsApplicant',
            multiple: true,
            label: this.$t('forms.table.documents.applicant'),
            data: this.documentsApplicant,
          },
          {
            id: 'reception',
            label: this.$t('forms.table.reception'),
            data: this.prepareData(this.reception, {
              label: 'label',
              name: 'name',
            }),
          },
          {
            id: 'income',
            label: 'Доходы',
            data: this.prepareData([], {
              label: 'label',
              name: 'name',
            }),
          },
        ];

        return readyToPublish ? this.deleteEmptyForms(table) : table;
      } catch (err) {
        notifyError(this.$t('forms.errors.loadForms'));

        console.error(
          '[service-constructor][forms] Could not generate table data',
          this.card,
          err
        );
      } finally {
        this.loading = false;
      }
    },
    prepareData(raw = [], rawConfig) {
      const defaultConfig = { label: 'name', name: 'code', forms: ['form'] };
      const config = Object.assign(defaultConfig, rawConfig);

      return raw.map((item) => {
        const forms = config.forms.flatMap((formKey) => {
          let form = item[formKey];
          if (this.isExtraForm(item.name)) {
            form = item;
          }
          const multiple = Array.isArray(form);

          if (config.mapFormsToParentServiceId && form && multiple) {
            const serviceId = this.common.id;
            const neededForm = form.find((nestedForm) => nestedForm.parentServiceId === serviceId);
            if (neededForm) {
              return {
                key: formKey,
                form: neededForm
              };
            }
          }
          if (multiple) {
            const defaultForms = form.filter(f => !f.parentServiceId);
            if (defaultForms.length) {
              return defaultForms.map((nestedForm) => ({
                key: formKey,
                form: nestedForm,
                multiple: true,
              }));
            }
            return {
              key: formKey,
              form: undefined
            };
          }

          return {
            key: formKey,
            form: form,
            multiple: false,
          };
        });

        return {
          formKeys: config.forms,
          id: item.id,
          schema: item.schema,
          label: item[config.label],
          name: item[config.name],
          raw: item,
          forms,
        };
      });
    },
    async deleteFromRegistry(entry, formId: string, clickedFormKey) {
      const forms = get(entry, 'forms');
      let form 
      if (entry.schema === 'Interagency') {
        form = forms && forms.find((formIt) => formIt?.form?.id === formId && formIt.form.parentServiceId);

        // При удалении общей формы межведа не удаляем её, а создаём кастомную
        if (!form) {
          const emptySchema = this.createEmptySchema(entry, clickedFormKey);
          await this.createInRegistry(entry, clickedFormKey, emptySchema);
          this.refresh();
          return;
        }
      } else {
        form = forms && forms.find((formIt) => formIt?.form?.id === formId);
      }

      if (!form?.key) {
        notifyError(this.$t('forms.errors.deleteForm'));

        console.error(
          '[service-constructor][forms][delete from registry] Could not get form key, aborting',
          { form }
        );
        return;
      }

      let document;

      if (this.isExtraForm(entry.name)) {
        document = {
          formsExtra: [formId],
          id: this.card.id,
          modified: this.card.modified,
        };
      } else {
        document = {
          [form.key]: formId,
          id: entry.id,
          modified: get(entry, 'raw.modified'),
        };
      }

      let unlinkRequest: Promise<ModifyResponse>;
      switch (entry.schema) {
        case 'ServiceDocument':
          unlinkRequest = documentApi.unlinkDocument.bind(this, entry.schema, [
            document,
          ]);
          break;
        case 'Interagency':
          unlinkRequest = interagencyApi.unlinkInteragency.bind(this, [
            document,
          ]);
          break;
        case 'Service':
          unlinkRequest = serviceApi.unlinkService.bind(this, [document]);
          break;
        case 'Form':
          unlinkRequest = serviceApi.unlinkService.bind(this, [document]);
          break;
      }

      unlinkRequest()
        .then(({ data }) => {
          if (data.success) {
            return formApi.deleteForm([formId]);
          }
        })
        .then(() => {
          this.refresh();
        });
    },

    async createInRegistryFromGit(entry, formKey, schema) {
      schema.name = `${entry.name}Base`;

      const { data: schemaResult } = await formApi.createForm([schema]);

      const schemaId = head(get(schemaResult, 'objectIds'));

      return updateService({
        formsExtra: [schemaId],
        id: this.card.id,
        modified: schemaResult.modified,
      });
    },

    async createInRegistry(entry, formKey, schema) {
      const schemaToSave = entry.schema === 'Interagency' ? { ...schema, parentServiceId: this.common.id } : schema;
      const { data: schemaResult } = await formApi.createForm([schemaToSave]);

      const schemaId = head(get(schemaResult, 'objectIds'));

      const data = {
        [formKey]: schemaId,
        id: entry.id,
        modified: get(entry, 'raw.modified'),
      };

      switch (entry.schema) {
        case 'ServiceDocument':
          return documentApi.updateDocument(entry.schema, [data]);
        case 'Interagency':
          return interagencyApi.updateInteragency([data]);
        case 'Service':
          return serviceApi.updateService([data]);
        case 'Form':
          return serviceApi.updateService([data]);
      }
    },
  },
};
</script>
