<template>
  <div class="q-gutter-y-md q-pb-md">
    <div class="row items-center">
      <q-select
        v-model="response"
        :options="responses"
        map-options
        option-label="code"
        @input="onChangeCode"
      />
    </div>

    <div class="q-gutter-x-md row items-center">
      <AddFileButton
        :title="$t('fromFile') + '...'"
        @added="readFromFile"
        :remove-after-adding="true"
        extensions=".json"
      />
      <q-input
        placeholder="Название"
        v-model="title"
        v-if="response.id !== CURRENT_CODE"
      />
    </div>
    <div class="column no-wrap">
      <span class="text-h6">{{ $t('base.response') }}:</span>
      <JsonEditor v-model="text" @input="emitChange" />
    </div>

    <div v-if="response.id !== CURRENT_CODE">
      <q-btn
        v-if="response.id === CUSTOM_CODE"
        color="primary"
        filled
        :label="$t('base.save')"
        :disable="isCreateButtonDisabled"
        @click="onCreateResponseClick"
      />
      <q-btn
        v-else
        color="primary"
        filled
        :label="$t('base.update')"
        :disable="isUpdateButtonDisabled"
        @click="onUpdateResponseClick"
      />
    </div>

    <q-inner-loading :showing="loading" />
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import {
  ConstructorMzMockResponse,
  ConstructorMzMockResponseApi,
} from '@openapi/constructor-database';
import JsonEditor from '@/components/common/json-editor-with-errors.vue';
import AddFileButton from '@/components/common/add-file-button.vue';
import cloneDeep from 'lodash/cloneDeep';
import { notifyError, notifySuccess } from '@/utils/notify';

const constructorMzMockResponseApi = new ConstructorMzMockResponseApi();

@Component({
  components: {
    JsonEditor,
    AddFileButton,
  },
})
export default class MockResponsesManager extends Vue {
  @Prop() clazz!: string;
  @Prop() mockResponse: string;

  responses: ConstructorMzMockResponse[] = [];
  response: ConstructorMzMockResponse = {
    code: '',
  };
  title: string = '';
  text: any = {};
  CUSTOM_CODE = 'custom';
  CURRENT_CODE = 'current';
  loading: boolean = false;

  created() {
    this.getResponseByClazz();
  }

  get isCreateButtonDisabled() {
    return !this.title;
  }

  get isUpdateButtonDisabled() {
    const isEqual =
      this.title === this.response.code &&
      JSON.stringify(this.text) === this.response.responseMsg;
    return !this.title || isEqual;
  }

  async getResponseByClazz() {
    try {
      this.loading = true;
      const {
        data,
      } = await constructorMzMockResponseApi.searchConstructorMzMockResponse(
        `clazz==${this.clazz}`
      );
      this.setResponses(data);

      this.setTitleAndJson(this.response);
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.loading = false;
    }
  }

  async readFromFile(file: File) {
    const reader = new FileReader();
    reader.readAsText(file);
    reader.onload = () => {
      try {
        this.text = JSON.parse('' + reader.result);
        this.emitChange();
      } catch (e) {
        if (e instanceof Error) {
          notifyError(e.message);
        }
      }
    };
    reader.onerror = function () {
      notifyError(reader.error?.message || 'не смог прочитать файл');
    };
  }

  async onCreateResponseClick() {
    const createObject: any = {
      clazz: this.clazz,
      code: this.title,
      responseMsg: JSON.stringify(this.text),
    };
    try {
      this.loading = true;
      const {
        data,
      } = await constructorMzMockResponseApi.createConstructorMzMockResponse([
        createObject,
      ]);
      this.loading = false;
      if (data.success) {
        notifySuccess(`Ответ ${this.title} успешно создан`);

        createObject.id = data.objectIds[0];
        this.addNewResponse(createObject);
      }
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    }
  }

  async onUpdateResponseClick() {
    const updateObject = {
      id: this.response.id,
      clazz: this.clazz,
      code: this.title,
      responseMsg: JSON.stringify(this.text),
    };
    try {
      this.loading = true;
      const {
        data,
      } = await constructorMzMockResponseApi.updateConstructorMzMockResponse([
        updateObject,
      ]);
      this.loading = false;
      if (data.success) {
        notifySuccess(`Ответ ${this.title} успешно обновлён`);

        this.updateResponses(updateObject);
      }
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    }
  }

  onChangeCode(v: any) {
    this.setTitleAndJson(v);
    this.emitChange();
  }

  setTitleAndJson(v: any) {
    try {
      this.text = JSON.parse(v.responseMsg);
      this.title = v.id === this.CUSTOM_CODE ? '' : v.code;
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    }
  }

  updateResponses(response: any) {
    const responsesIndex = this.responses.findIndex(
      (r) => r.id === response.id
    );
    if (responsesIndex > -1) {
      this.response = { ...this.response, ...response };
      this.responses[responsesIndex] = cloneDeep(this.response);
    }
  }

  addNewResponse(newResponse: ConstructorMzMockResponse) {
    this.responses.unshift(newResponse);
    this.response = newResponse;
  }

  setResponses(data: any[]) {
    if (this.mockResponse) {
      this.addDefaultOption();
    }
    if (data?.length) {
      this.responses.push(...data);
    }
    this.addCustomOption();
    this.setSelectedResponse();
  }

  setSelectedResponse() {
    this.response = this.responses[0];
  }

  addCustomOption() {
    this.responses.push({
      id: this.CUSTOM_CODE,
      code: this.$tc('newMockResponse'),
      responseMsg: '{}',
    });
  }

  addDefaultOption() {
    this.responses.push({
      id: this.CURRENT_CODE,
      code: this.$tc('current'),
      responseMsg: this.mockResponse,
    });
  }

  emitChange() {
    this.$emit('change', JSON.stringify(this.text));
  }
}
</script>

<style scoped></style>

<i18n>
  {
    "ru": {
      "current": "Текущий",
      "newMockResponse": "Новый мок ответ",
      "fromFile": "Из файла"
    },
    "en": {
      "current": "Current",
      "newMockResponse": "New mock response",
      "fromFile": "From file"
    }
  }
</i18n>