<i18n>
{
  "en": {
    "IncomeCard": {
      "buttons": {
        "previewSchema": "Preview",
        "editSchema": "Edit",
        "saveSchema": "Save schema",
        "addForm": "Add a screen form"
      },
      "notifications": {
        "formSaveSuccess": "Income form saved successfully",
        "formSaveError": "Failed to save income form"
      }
    }
  },
  "ru": {
    "IncomeCard": {
      "buttons": {
        "previewSchema": "Предпросмотр",
        "editSchema": "Редактирование",
        "saveSchema": "Сохранить схему",
        "addForm": "Создать экранную форму доходов"
      },
      "notifications": {
        "formSaveSuccess": "Экранная форма доходов успешно сохранена",
        "formSaveError": "Не удалось сохранить экранную форму доходов"
      }
    }
  }
}
</i18n>

<template>
  <div v-if="common.active === 'income' && !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">
        <IncomeDirectorylinks 
          :dictionaries="dictionaries" 
          :directory-links="incomeDirectorylinks" 
          :canEdit="canEdit"
          class="q-mb-lg"
          @update="updateIncomeDirectorylinks"
        />

        <div>
          <q-btn
            v-if="canEdit"
            @click="() => settingsPreviewMode = !settingsPreviewMode"
            unelevated
            color="primary"
            :icon="settingsPreviewMode ? 'edit' : 'visibility'"
            class="q-mr-md"
          >
            <q-tooltip :delay="500">
              {{
                settingsPreviewMode
                  ? $t('IncomeCard.buttons.editSchema')
                  : $t('IncomeCard.buttons.previewSchema')
              }}
            </q-tooltip>
          </q-btn>

          <SddConstructor
            v-if="card.hasSDD"
            :model="editModel"
            :edit-mod="!settingsPreviewMode"
          />
        </div>
        <div>
          <div class="text-h6">Форма доходов</div>
          <template v-if="!incomeForm">
            <q-btn
              icon="create"
              color="primary"
              @click="createEmptyForm"
            >
              {{ $t('IncomeCard.buttons.addForm') }}
            </q-btn>
          </template>

          <template v-else>
            <div class="q-mb-md">
              <q-btn
                @click="changeMode"
                unelevated
                color="primary"
                :icon="previewMode ? 'edit' : 'visibility'"
                class="q-mr-md"
                :disable="isAttrListEmpty || !!liveSchemaErrors.length"
              >
                <q-tooltip :delay="500">
                  {{
                    previewMode
                      ? $t('IncomeCard.buttons.editSchema')
                      : $t('IncomeCard.buttons.previewSchema')
                  }}
                </q-tooltip>
              </q-btn>
              <q-btn
                v-if="canEdit"
                @click="saveSchema"
                unelevated
                :disable="!!liveSchemaErrors.length || !unsavedChanges"
                :label="$t('IncomeCard.buttons.saveSchema')"
                color="primary"
                icon="save"
              >
                <q-tooltip :delay="500">{{ $t('IncomeCard.buttons.saveSchema') }}</q-tooltip>
              </q-btn>
            </div>

            <div>
              <FormGeneratorWrapper
                v-if="previewMode && ready"
                :model="previewModel"
                :schema="schema"
              />
              <template v-else>
                <transition-group appear name="fade">
                  <q-banner
                    v-for="(error, idx) of lazySchemaErrors"
                    :key="''+error+idx"
                    inline-actions
                    rounded
                    class="bg-red-7 text-white q-mb-md"
                  >
                    <div
                      :key="lineIndex"
                      v-for="(line, lineIndex) of error.message.split('<br>')"
                    >
                      {{ line }}
                    </div>
                  </q-banner>
                </transition-group>
                <v-json-editor
                  v-model="schema"
                  ref="jsoneditor"
                  height="calc(100vh - 51px - 72px - 80px)"
                  :options="{
                    mode: 'code',
                    mainMenuBar: false,
                    onValidationError: setSchemaErrors,
                    onBlur: setLazySchemaErrors,
                    onEditable: () => canEdit ? true : false,
                  }"
                />
              </template>
            </div>
          </template>
        </div>
      </div>
    </q-card>
  </div>
</template>

<script lang="ts">
// @ts-ignore
import VJsonEditor from 'v-jsoneditor';
import cloneDeep from 'lodash/cloneDeep';
import { Vue } from 'vue-property-decorator';
import { BaseCardCommon } from '@/interfaces';
import FormGeneratorWrapper from '@components/common/node-generator/FormGeneratorWrapper.vue';
import isEmpty from 'lodash/isEmpty';
import SddConstructor from '@components/cards/services/components/sdd-constructor/SddConstructor.vue';
import IncomeDirectorylinks, {  Directorylinks } from './components/IncomeDirectorylinks.vue'
import type { ExtendedService } from './types/services-types';
import {
  FormApi,
  ServiceApi,
} from '@openapi/registry-service';
import get from 'lodash/get';
import head from 'lodash/head';
import { notifyError, notifySuccess } from '@utils/notify';
import has from 'lodash/has';
import isEqual from 'lodash/isEqual';
import set from 'lodash/set';

const serviceApi = new ServiceApi();
const formApi = new FormApi();

// TODO: Переписать на классовый компонент
interface IData {
  schema: any,
  previewModel: object,
  previewMode: boolean,
  liveSchemaErrors: any[],
  lazySchemaErrors: any[],
  settingsPreviewMode: boolean,
  editModel: ExtendedService | null,
  schemaKeys: any[],
  schemaDump: any,
  unsavedChanges: boolean,
  schemaMeta: any,
  currentServiceId: string | null
}

interface IMethods {
  getKeysSchema: (schema: any) => any[],
  updateStoreModel: (value: any) => void, 
  createEmptySchema: () => any,
  setSchema: () => void,
  removeOldProperty: (schema: any) => any,
  changeMode: () => void,
  getMockModel: () => void,
  saveSchema: () => void,
  setSchemaErrors: (errors: any[]) => void,
  setLazySchemaErrors: () => void,
  createEmptyForm: () => void,
  getService: () => void,
  updateCard<T>(key: string, data: T): void,
  updateIncomeDirectorylinks(data: Directorylinks[]): void
}

interface IComputed {
  card: any,
  incomeForm: any,
  loadingCard: boolean,
  isAttrListEmpty: boolean,
  ready: boolean,
  dictionaries: [],
  incomeDirectorylinks: Directorylinks[],
  canEdit: boolean,
}

interface IProps {}

export default Vue.extend<IData, IMethods, IComputed, IProps>({
  components: {
    FormGeneratorWrapper,
    VJsonEditor,
    SddConstructor,
    IncomeDirectorylinks
  },
  props: {
    common: Object as () => BaseCardCommon
  },
  data() {
    return {
      schema: null,
      previewModel: {},
      previewMode: false,
      liveSchemaErrors: [],
      lazySchemaErrors: [],
      settingsPreviewMode: true,
      editModel: null,
      schemaKeys: [],
      schemaDump: null,
      unsavedChanges: false,
      schemaMeta: null,
      currentServiceId: null,
    }
  },
  computed: {
    loadingCard(): boolean {
      return (
        this.$store.getters['registryService/services/servicesLoading'] || false
      );
    },
    card() {
      return cloneDeep(this.$store.getters['registryService/services/servicesCard']);
    },
    isAttrListEmpty() {
      return (
        this.schema?.attrList === undefined || isEmpty(this.schema.attrList)
      );
    },
    incomeForm() {
      return this.card?.formsExtra?.find((f: any) => f.name === 'Income');
    },
    ready() {
      return !isEmpty(this.previewModel) && !isEmpty(this.schema);
    },
    dictionaries() {
      return (this.card?.dictionaries || []).map((v: any) => ({
        value: v.id,
        label: v.name
      }))
    },
    incomeDirectorylinks() {
      return this.card?.incomeDirectorylinks || []
    },
    canEdit(): boolean {
      return this.$store.getters['registryService/services/canEdit'];
    },
  },
  watch: {
    card: {
      immediate: true,
      handler(value) {
        if (!this.editModel && !isEmpty(value) ||
          this.editModel && value.id !== this.editModel.id) {
          this.editModel = cloneDeep(value);
        }
      }
    },
    editModel: {
      deep: true,
      handler(value) {
        this.updateStoreModel({
          ...this.card,
          ...value && { livingWage: value.livingWage }
        });
      }
    },
    incomeForm(value) {
      if (value && isEmpty(this.schema)) {
        this.setSchema();
      }
    },
    schema: {
      handler(schema) {
        this.unsavedChanges =
          schema !== null && !isEqual(schema, this.schemaDump);
        this.$root.$emit('unsavedChanges', this.unsavedChanges);
      },
    },
  },
  methods: {
    changeMode() {
      this.previewMode = !this.previewMode;
      if (this.previewMode) {
        this.getMockModel();
      }
    },
    removeOldProperty(schema) {
      let removeKey = null;

      const newSchema = cloneDeep(schema);

      for (const key in this.schemaKeys) {
        if (this.schemaKeys[key].includes(removeKey)) continue;

        const hasKey = has(newSchema, this.schemaKeys[key]);

        if (!hasKey) {
          set(newSchema, this.schemaKeys[key], '');
          removeKey = this.schemaKeys[key];
        }
      }
      return newSchema;
    },
    async saveSchema() {
      let schema = this.schema;

      if (!schema) return;

      schema = this.removeOldProperty(schema);

      const { data: result } = await formApi
        .updateForm([{ ...schema, ...this.schemaMeta }])
        .catch((error) => {
          console.error(
            '[schemas index] could not save schema',
            { schema },
            error
          );

          notifyError(this.$tc('IncomeCard.notifications.formSaveError'));

          return false;
        }) as any;

      if (result) {
        this.unsavedChanges = false;

        notifySuccess(this.$tc('IncomeCard.notifications.formSaveSuccess'));
        return this.setSchema();
      }
    },
    setSchemaErrors(errors) {
      this.liveSchemaErrors = errors;
      if (this.lazySchemaErrors.length) {
        this.lazySchemaErrors = this.liveSchemaErrors;
      }
    },
    setLazySchemaErrors() {
      this.lazySchemaErrors = this.liveSchemaErrors;
    },
    getMockModel() {
      this.$store
        .dispatch('utils/components/fetchMockDataForSchema', this.schema)
        .then((data) => {
          this.previewModel = data.model;

          // Если на бфф есть не определенные типы в схеме, то выводим их
          if (Object.keys(data.warning).length > 0) {
            console.log(
              '[FormGenerator][Preview schema] неизвестные типы схемы: ',
              data.warning
            );
          }
        });
    },
    updateStoreModel(value) {
      this.$store.dispatch(
        'registryService/services/setCardForJson',
        value
      );
    },
    createEmptySchema() {
      return {
        name: 'Income',
        label: 'Доходы',
        attrList: [],
        groupList: [],
      };
    },
    async createEmptyForm() {
      const schema = this.createEmptySchema();
      
      const { data: schemaResult } = await formApi.createForm([schema]);

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

      const data = {
        formsExtra: [schemaId],
        id: this.editModel?.id,
        modified: this.editModel?.modified,
      };

      // @ts-ignore
      await serviceApi.updateService([data]);
      this.getService();
    },
    async setSchema() {
      const response = await formApi.getForm(this.incomeForm.id);
      const schema = response.data as any;

      if (!schema.attrList) schema.attrList = [];
      if (!schema.groupList) schema.groupList = [];

      this.schemaMeta = {
        hidden: schema.hidden,
        modified: schema.modified,
        schema: schema.schema,
        id: schema.id,
        version: schema.version,
        _service_id: schema._service_id,
      };

      this.schema = schema;
      this.schemaDump = schema;
      this.schemaKeys = this.getKeysSchema(schema);
    },
    getKeysSchema(schema: any): any[] {
      let keys: any[] = [];
      for (const key in schema) {
        keys.push(key);

        if (Array.isArray(schema[key])) continue;

        if (typeof schema[key] === 'object') {
          const subkeys = this.getKeysSchema(schema[key]);
          keys = keys.concat(
            subkeys.map((subkey: any) => {
              return `${key}.${subkey}`;
            })
          );
        }
      }
      return keys;
    },
    getService() {
      this.$store.dispatch('registryService/services/getServiceMeta', {
        id: this.$route.params.id,
      });
    },
    updateCard<T>(key: string, data: T) {
      const card = cloneDeep(this.card);

      card[key] = data;
      this.$store.dispatch('registryService/services/setCardForJson', card);
    },
    updateIncomeDirectorylinks(data: Directorylinks[]) {
      this.updateCard('incomeDirectorylinks', data)
    }
  },
});
</script>
