import { Component, Watch } from 'vue-property-decorator';
import Vue from 'vue';
import FormGeneratorWrapper from '@components/common/node-generator/FormGeneratorWrapper.vue';
import OperatorStatementSign from './components/OperatorStatementSign.vue';
import axios, { AxiosResponse } from 'axios';
import { QSelectOption } from '@/models/models-ui';
import { MetaClassSchema } from '@devsun/form-generator/dist';
import { v4 } from 'uuid';
import { convertDateFormat } from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { COMMON_INNER_PETITION } from '@/constants/schemas';
import { ServiceApi } from '@openapi/registry-service';
import { updateAttachment } from '@services/registry-service';
import { CollapsedProfileApi } from '@openapi/idm';
import { notifyError, notifyInfo, notifySuccess } from '@utils/notify';
import OrganizationService from '@services/entities/OrganizationService';
import get from 'lodash/get';
import set from 'lodash/set';
import cloneDeep from 'lodash/cloneDeep';
import isEmpty from 'lodash/isEmpty';
import { AttachmentApi, DocumentApi } from '@openapi/document-registry';
import { fetchAttachment } from '@services/document-registry';
import { Organization } from '@interfaces/entities/Organization';
import { downloadStatement, downloadAgreement } from '@/utils/statement-print';
import SelectBase from '@components/common/select-base.vue';
import { createDictionaryMapFromSchema, setValuesFromDictionaries } from '@/utils/dict-map-utils';
import DepartmentService, { DepartmentWithServices } from '@/services/entities/DepartmentService';
import { ProcessStep, getPathsToClear, clearValue } from '../utils/create';
import isEqual from 'lodash/isEqual';
import { Field } from '@devsun/eva-models';
import { isPastDate } from '@utils/format-date';
import { ReservedTicketDTO, TicketControllerApi } from '@openapi/preorder-pgs';

const attachmentApi = new AttachmentApi();
const documentApi = new DocumentApi();
const ticketsApi = new TicketControllerApi();

// TODO DELETE
interface Service {
  authority: { ogrn: string; fullName: string };
  credentialTransferred: boolean;
  epguImport: boolean;
  fullName: string;
  healthStatus: Array<any>;
  hidden: boolean;
  id: string;
  name: string;
  kcrImport: boolean;
  legalActs: Array<any>;
  modified: number;
  multiAuthority: boolean;
  rgyApplied: boolean;
  schema: string;
  scope: Array<any>;
  servicePresentation: string;
  shortName: string;
  state: string;
  documents: ServiceDocument[];
  formsExtra?: Array<any>;
  includeReception?: boolean;
}

interface ServiceDocument {
  source: string;
  code: string;
  name: string;
}

export interface FileData {
  id: string;
  file: File;
  name: string;
  type: QSelectOption;
}

export interface ExternalFile {
  id: string;
  type: QSelectOption;
  comment: string;
  quantity: number;
}

interface Draft {
  id?: string;
  orgId?: string;
  schema: string;
  userId: string;
  model: any;
  statementType?: string | boolean;
  files: any[];
}

const serviceApi = new ServiceApi();
const collapsedProfileApi = new CollapsedProfileApi();

const FAMILY_CHILD_NEW = 'familychildsub-2';
const EMERGENCIES103 = 'Emergencies103';
const PREGNANCY = 'pregnancysub';
const SINGLEPARENT817 = 'singleparent817sub';
/* empty dev merge commit */

@Component({
  components: {
    FormGeneratorWrapper,
    OperatorStatementSign,
    SelectBase,
  },
  methods: {
    get,
    isEmpty,
  },
})
export default class StatementsCreate extends Vue {
  statementType: QSelectOption | null = null;
  fullService: Service | null = null;
  uploadFormComponent = 'ReviewDocumentsHiddenForm';
  model: any = {};
  schema: MetaClassSchema | null = null;
  loading = false;
  // todo typings - Service OPENAPI: documents, includeReception, formsExtra -
  services: any[] | null = null; // Service[]
  sendingStatement = false;
  files: FileData[] = [];
  fileData: FileData = {} as FileData;
  externalFiles: ExternalFile[] = [];
  externalFileData: ExternalFile = {} as ExternalFile;
  servicesLoading = false;
  isStepValid = false;
  isExternalStepValid = false;
  draftFiles: string[] = [];
  profileId = '';
  documentExtensions = '.pdf, .doc, .docx, .xls, .xlsx, .txt, image/*, .sig, .zip, .rar';
  step = '';
  isExternalFile = false;

  includeServiceNames = ['familychildsub-2', '11'];

  documentStep: ProcessStep = {
    name: 'documents',
    label: 'Документы',
    icon: 'description',
  };

  unSaveData = false;
  unSaveDataLabel = '';
  unSaveDataDialog = false;
  action = 'next';

  isDropDataDialogVisible = false;
  clearedData = false;
  hasStatementBeenDownloaded = false;

  $refs!: {
    formGeneratorWrapper: any;
    externalFormGeneratorWrapper: any;
  };

  get steps(): ProcessStep[] {
    if (!this.schema) return [];
    console.log(
      '*************************************************************************'
    );
    console.log(this.schema);
    let steps = [
      ...this.schema.groupList
        .map((group) => ({
          label: group.label,
          name: group.name,
          icon: group.icon ?? '',
          parent: group.parent,
          external: !!group.props?.external,
        }))
        .filter((group) => !group.parent && !group.external),
      ...(this?.model?.s_KP_OA_02 === '1' || this.statementType?.value
        ? [this.documentStep]
        : []), // TODO: нужно обязательно что то сделать с документами, т.к. такое решение очень уж кривое
    ];

    // FIXME: Дополнение кривости шага документов
    if (
      this.schema.hasOwnProperty('skipDocumentStep') &&
      // @ts-ignore
      this.schema?.skipDocumentStep
    ) {
      steps = steps.filter((element) => element.name !== 'documents');
    }
    console.log(steps);

    // подписание заявления всегда в конце
    const applicationForPrintName = 'applicationForPrint';
    const aplicationForPrint = steps.find(
      (step) => step.name === applicationForPrintName
    );
    if (aplicationForPrint) {
      steps = steps.filter((item) => item.name !== applicationForPrintName);
      steps = [...steps, aplicationForPrint];
    }
    return steps;
  }

  get externalSteps(): ProcessStep[] {
    if (!this.schema) return [];

    return this.schema.groupList
      .map((group) => ({
        label: group.label,
        name: group.name,
        icon: group.icon ?? '',
        parent: group.parent,
        external: !!group.props?.external,
      }))
      .filter((group) => !group.parent && group.external);
  }

  get currentStepIndex() {
    return this.steps.findIndex((step) => step.name === this.step);
  }

  async setProfileId() {
    await collapsedProfileApi
      .getCollapsedProfile()
      .then((response) => {
        this.profileId = response.data.id || '';
      })
      .catch((e: any) => {
        notifyError(e.message?.toString());
      });
  }

  get lastStepIndex() {
    return this.steps.length - 1;
  }

  get nextStep() {
    return this.steps[this.currentStepIndex + 1];
  }

  get prevStep() {
    return this.steps[this.currentStepIndex - 1];
  }

  get adoptedSchema() {
    if (!this.schema) return null;

    const schema = cloneDeep(this.schema);
    const group = schema.groupList.filter(
      (group) =>
        (group.name === this.step || group.parent) && !group.props?.external
    ); // todo recusrsive check for parent === this.step

    if (!group) return null;

    const fields: any = [];
    schema.attrList.forEach((attr) => {
      group.forEach((item) => {
        if (item.attrList?.includes(attr.name)) {
          fields.push(attr);
        }
      });
    });

    schema.groupList = group;
    schema.attrList = fields;

    return schema;
  }

  get receptionBase() {
    return this.fullService?.formsExtra
      ? this.fullService?.formsExtra.find(
          (item) => item.name === COMMON_INNER_PETITION
        )
      : {};
  }

  get commonBase() {
    return this.fullService?.formsExtra
      ? this.fullService?.formsExtra.find(
          (item) => item.name === COMMON_INNER_PETITION
        )
      : {};
  }

  get adoptedExternalSchema() {
    if (!this.schema) return null;

    const schema = cloneDeep(this.schema);
    const group = schema.groupList.filter(
      (group) => !!group.props?.external || group.parent
    );

    if (!group) return null;

    const fields: any = [];
    schema.attrList.forEach((attr) => {
      group.forEach((item) => {
        if (item.attrList?.includes(attr.name)) {
          fields.push(attr);
        }
      });
    });

    schema.groupList = group;
    schema.attrList = fields;

    return schema;
  }

  get statementsModels(): QSelectOption[] {
    if (!this.services) return [];

    return this.services
      .filter(
        (service) =>
          !!service.includeReception ||
          this.includeServiceNames.includes(service.name)
      )
      .map((service) => ({
        label: service.shortName,
        value: service.name,
        data: service,
      }));
  }

  get attractionCode(): string | null {
    return this.statementType ? `manual_${this.statementType.value}` : null;
  }

  get fileDataOptions(): QSelectOption[] {
    return this.fullService?.documents
      ? this.fullService.documents
          .filter((doc) =>
            this.statementType?.value === FAMILY_CHILD_NEW ||
            this.statementType?.value === PREGNANCY ||
            this.statementType?.value === SINGLEPARENT817
              ? doc.source === 'MVZ'
              : doc.source === 'Applicant'
          )
          .map((doc) => ({
            label: doc.name,
            value: doc.code,
            data: doc,
          }))
      : [];
  }

  get allowStepForward(): boolean {
    return this.isStepValid && this.isExternalStepValid;
  }

  get allowJumpForward(): boolean {
    return this.allowStepForward && !this.clearedData;
  }

  get isFromDraft(): boolean {
    return this.$router.currentRoute.name === 'from-draft';
  }

  get draftId(): string {
    return this.$router.currentRoute.params.id;
  }

  get currentUser(): any {
    return this.$store.getters['oidcClient/currentUser'];
  }

  get userAccount(): any {
    return this.$store.getters['idm/accounts/accountsCard'];
  }

  get loggedAsDepartment(): boolean {
    return this.userAccount['user-domain'].schema === 'Department';
  }

  get authorSchema(): MetaClassSchema {
    return this.$store.getters['utils/cards/getSchemaByPath']('authorSchema');
  }

  // Если атрибут резервации присутствует, значит она обязательная
  get reservationAttr(): Field.Base | undefined {
    return this.schema?.attrList.find(attr => attr.template === 'SlotSelector');
  }

  get disableSaveDraft(): boolean {
    return !!(this.reservationAttr?.name && get(this.model, this.reservationAttr.name, null));
  }

  get disableSendStatement() {
    const isOperatorStatementSign = this.steps.find(
      (f) => f.name === 'applicationForPrint'
    );
    return (
      this.currentStepIndex !== this.lastStepIndex
      || (isOperatorStatementSign && !this.lastStepDocsAreReady)
      || !!(this.reservationAttr?.name && !this.disableSaveDraft)
    );
  }

  get lastStepDocsAreReady(): boolean {
    return this.statementDocuments.length >= 2;
  }

  setStepValid(valid: boolean): void {
    this.isStepValid = valid;
  }

  setExternalStepValid(valid: boolean) {
    this.isExternalStepValid = valid;
  }

  onStatementTypeChange(statementType: QSelectOption): void {
    this.$store.commit('formGenerator/data', { id: statementType.data.id });
    this.changeStatementType(statementType);

    this.$store.commit('formGenerator/setSteppedBack', false);

    this.clearStatementDocuments();
  }

  async changeStatementType(statementType: QSelectOption): Promise<void> {
    this.statementType = statementType;
    if (statementType) {
      const service = await this.$store.dispatch(
        'registryService/services/getServiceMeta',
        { id: statementType.data.id }
      );
      this.fullService = service;

      this.$store.commit('formGenerator/data', { service });

      this.getFormSchema();
    }
  }

  @Watch('fileData')
  fieldsUpdate(fields: FileData): void {
    this.unSaveData = !isEmpty(fields);
  }

  get isClearModalOpen() {
    return this.$store.getters['formGenerator/isClearModalOpen'];
  }

  @Watch('currentStepIndex')
  onCurrentStepIndexChange(curr: number, prev: number) {
    this.$store.commit('formGenerator/setSteppedBack', curr < prev);

    if (curr === this.lastStepIndex) {
      this.clearedData = false;
    }
  }

  get documentStepIndex(): number {
    return this.steps.findIndex((s: ProcessStep) => s.name === 'documents');
  }

  get foundPerson() {
    return this.$store.getters['formGenerator/foundPerson'];
  }

  @Watch('foundPerson')
  onFoundDocRus(foundPerson: any, prevFoundPerson: any) {
    if (foundPerson && !isEqual(foundPerson, prevFoundPerson)) {
      this.$set(this.model, foundPerson.path, foundPerson.obj);
    }
  }

  get statementDocuments() {
    return this.$store.getters['formGenerator/statementDocuments'];
  }

  async created(): Promise<void> {
    await this.$store.dispatch('idm/accounts/dispatchAccount', {
      id: this.currentUser.sub,
      data: {
        expand: 'user-domain',
      },
    });
    await this.setServices();
    const route = this.$route;
    if (route.name === 'from-draft') {
      await this.fromDraft(route.params.id);
      this.clearedData = false;
    } else {
      this.clearedData = true;
    }
    this.clearStatementDocuments();
    await this.setProfileId();
    await this.declineReservation();
  }

  async getFormSchema() {
    this.loading = true;

    let schema: MetaClassSchema;

    try {
      schema = this.receptionBase;

      if (!schema) {
        throw new Error(this.$t('statements.Create.noSchema') as string);
      }

      this.schema = schema;
      this.step =
        this.schema.groupList.find((group) => !group.props?.external)?.name ||
        '';
      // this.step = schema.groupList[1].name || '';
    } catch (error) {
      notifyError(error.message.toString());
    } finally {
      this.setBaseModel();

      this.loading = false;
    }
  }

  async setServices(): Promise<void> {
    this.servicesLoading = true;

    const query: string[] = [];
    this.includeServiceNames.forEach((name: string) => {
      query.push(`name==${name}`);
    });
    query.push('includeReception==true');

    const services = await serviceApi
      .searchService(query.join(' or '))
      .then(({ data }) => {
        return data || [];
      })
      .catch(() => []);

    let orgDepServices: Organization | DepartmentWithServices;

    try {
      orgDepServices = this.loggedAsDepartment ? 
        await new DepartmentService().get(
          this.currentUser.domain_id,
          'services'
        ) :
        await new OrganizationService().get(
          this.currentUser.domain_id,
          'services'
        );

      if (orgDepServices?.services) {
        this.services = services.filter((attr) => {
          return orgDepServices.services.some((item) => attr.id === item.id);
        });
      }
    } catch (e) {
      console.log(e);
    }

    this.servicesLoading = false;
  }

  async saveOrUpdate() {
    try {
      if (this.isFromDraft) {
        await this.updateDraft();
      } else {
        await this.saveToDrafts();
      }
    } catch (error) {
      console.error(error);
      notifyError(error.message.toString());
    }
  }

  async updateDraft() {
    const files = this.files || [];
    const draft = this.createDraft();
    draft.id = this.draftId;
    const {
      data: stored,
      // TODO: разобраться с совместимость интерфейса Draft & Document
    } = await documentApi.updateDocument('ApplicationDrafts', [draft] as any);
    const id = stored.objectIds?.[0] || '';

    await this.deleteOldFiles(id, files);
    await this.updateAttachments(id, files);
    this.draftFiles = files.map((f) => f.name);

    notifySuccess(this.$t('statements.updateDraftSuccess') as string, '');
  }

  async saveToDrafts() {
    const files = this.files || [];
    const draft = this.createDraft();
    const {
      data: stored,
      // TODO: разобраться с совместимость интерфейса Draft & Document
    } = await documentApi.createDocument('ApplicationDrafts', [draft] as any);
    const id = stored.objectIds?.[0] || '';
    await this.updateAttachments(id, files);
    this.draftFiles = files.map((f) => f.name);

    this.$router.replace({
      name: 'from-draft',
      params: {
        id,
      },
    });
    notifySuccess(this.$t('statements.saveToDraftSuccess') as string, '');
  }

  createDraft(): Draft {
    const files = this.files || [];
    return {
      schema: 'ApplicationDrafts',
      orgId: this.userAccount?.['user-domain']?.id,
      model: this.model,
      userId: this.currentUser?.sub,
      statementType: this.statementType?.value,
      files: files.map((f) => {
        const copy: any = Object.assign({}, f);
        copy.name = f.file.name;
        copy.fileType = f.file.type;
        copy.file = undefined;
        return copy;
      }),
    };
  }

  async updateAttachments(id: string, files: FileData[]) {
    if (files.length) {
      await updateAttachment(
        'ApplicationDrafts',
        id,
        '',
        files.map((f) => f.file)
      );
    }
  }

  async deleteOldFiles(id: string, files: FileData[]): Promise<void> {
    const filesToDelete = this.draftFiles.filter(
      (draftFile) => !files.find((f) => f.name === draftFile)
    );

    await Promise.all(
      filesToDelete.map((f: string) =>
        attachmentApi.deleteDocumentFile('ApplicationDrafts', id, f)
      )
    ).catch((error) => console.error(error));
  }

  async deleteCurrentDraft(): Promise<AxiosResponse> {
    const id = this.$route.params.id;
    return await documentApi.deleteDocument('ApplicationDrafts', [id]);
  }

  async fromDraft(id: string) {
    try {
      const { data: res } = await documentApi.getDocument(
        'ApplicationDrafts',
        id
      );
      // TODO: разобраться с совместимость интерфейса Draft & Document
      await this.openDraft(res as any);
    } catch (error) {
      const err = error as Error;
      console.error(error);
      notifyError(err.message.toString());
    }
  }

  async openDraft(draft: Draft) {
    const statementType = this.statementsModels.find(
      (m) => m.value === draft.statementType
    );
    this.$store.commit('formGenerator/data', { id: statementType?.data?.id });

    if (statementType) {
      await this.changeStatementType(statementType);
      this.model = draft.model;

      this.draftFiles = (draft.files || []).map((f) => f.name);

      const loads = (draft.files || []).map((f) =>
        fetchAttachment(
          'ApplicationDrafts',
          draft.id || '',
          f.name,
          false
        ).then((res: string) => {
          f.file = new File([res], f.name, { type: f.fileType });
          return f;
        })
      );

      await Promise.all(loads);
      this.files = draft.files || [];
    }
  }

  // Mutates formData and model
  addFilesToStatement(formData: FormData, model: any, files: any[]) {
    if (files.length) {
      files.forEach((file: FileData) => {
        formData.append('attachments', file.file);

        const docCode = get(file, 'type.data.code');
        const fileType = get(file, 'file.type');
        const fileName = get(file, 'file.name');

        const newDoc = {
          CodeDocument: docCode,
          type: fileType,
          URL: fileName,
          Name: fileName,
        };
        const docArray = get(model, `Documents.[${docCode}]`) || [];
        docArray.push(newDoc);
        set(model, `Documents.${docCode}`, docArray);
      });
    }
  }

  getCurrentTicket(notify = true): ReservedTicketDTO | undefined {
    const lsname = `${this.currentUser.sub}-reception-booking`;
    const item = localStorage.getItem(lsname);
    if (!item) {
      if (notify) {
        notifyError('Бронь не найдена');
      }
      return;
    }
    const obj = JSON.parse(item);
    const isPast = isPastDate(new Date(obj.reservedUntil))
    if (isPast) {
      localStorage.removeItem(lsname);
      if (notify) {
        notifyError('Бронь истекла');
      }
      return;
    } else {
      localStorage.removeItem(lsname);
      return obj;
    }
  }

  async sendStatement(): Promise<void> {
    this.sendingStatement = true;
    const formData = new FormData();
    let model = { ...this.model };

    this.addFilesToStatement(formData, model, this.files);
    this.addFilesToStatement(formData, model, this.statementDocuments);
    console.log(model, formData)

    model = this.fillDocInfoType(model);
    if (this.externalFiles?.length) {
      model.externalFiles = this.externalFiles;
    }

    // EPGU hack for 'new 3-7'
    if (
      this.statementType?.value === FAMILY_CHILD_NEW ||
      this.statementType?.value === PREGNANCY ||
      this.statementType?.value === SINGLEPARENT817
    ) {
      model = {
        dataRequest: model,
        class: model.class,
      };
    }
    model.OrderPersonInfo = {
      id: this.$store.state.oidcClient.currentUser.sub,
      fio: this.$store.state.oidcClient.currentUser.name,
      tnsOrgId: this.$store.state.formGenerator.data.organization.dns,
      organization: this.$store.state.formGenerator.data.organization.fullname,
      roles: this.$store.state.oidcClient.currentUser.roles,
    };

    const modelBlob = new Blob([JSON.stringify(model)]);
    const file = new File([modelBlob], 'file');

    formData.append('body', file);

    const path = process.env.VUE_APP_PROJECT_PATH;

    const url = location.origin.includes('platform.apps.dev')
      ? `${path}/${path}/@mz-inner-petitions` // :D
      : '/mz/inner-petitions';

    console.log('MZ_URL', url);

    if (!!this.reservationAttr?.name && !this.getCurrentTicket()) {
      set(this.model, this.reservationAttr.name, null);
      this.sendingStatement = false;
      return;
    }

    return axios
      .request({
        url,
        method: 'POST',
        data: formData,
        headers: {
          'Content-Type': 'multipart/form-data',
          userId: this.profileId,
        },
      })
      .then(({ data }) => data)
      .then(() => {
        notifySuccess('Заявление отправлено', 'check');

        this.setBaseModel();
        if (!this.steps[0]?.name) {
          document.location.reload();
        }
        this.step = this.steps[0].name;
      })
      .catch((err) => console.error(err))
      .finally(() => {
        this.sendingStatement = false;
        this.clearStatementDocuments();
        this.goToStatements();
      });
  }

  fillDocInfoType(model: any) {
    const applicantTypeField = 'ApplicantInfo.IdentityDocInfo.Type';

    const applicantType = get(model, applicantTypeField);

    set(model, applicantTypeField, { code: applicantType });

    let relatives = get(model, 'FamilyInfo.RelativeInfo');
    // todo DEL if not necessary
    if (this.statementType?.value === FAMILY_CHILD_NEW) {
      relatives = get(model, 'familyInfo.relativeInfo');
    }

    if (relatives && relatives.length) {
      relatives.forEach((relative: any) => {
        const typeField = 'IdentityDocInfo.Type';

        const type = get(relative, typeField);

        set(relative, typeField, { code: type });
      });
    }

    /* if (model['emergency'] && model['emergency'].id) {
      set(model, 'emergency', model['emergency'].id);
    } */

    return model;
  }

  setBaseModel(): void {
    this.model = {
      class: this.statementType?.data?.petitionType,
      userAccepts: false,
      currentDate: convertDateFormat(new Date(), '', DateFormat.invertedDate),
      chanel: 'Очный прием',
      ...(this.statementType?.value !== FAMILY_CHILD_NEW && {
        ApplicantInfo: {
          Citizenship: {
            code: 643,
            content: 'Россия',
          },
        },
      }),
      // todo DEL if not necessary
      ...(this.statementType?.value === FAMILY_CHILD_NEW && {
        applicantInfo: {
          citizenship: {
            code: 643,
            content: 'Россия',
          },
        },
      }),
      // todo DEL if not necessary
      ...(this.statementType?.value === EMERGENCIES103 && {
        RecipientPersonalData: {
          citizenship: 'Россия',
        },
      }),
    };
  }

  goToStatements() {
    this.$router.back();
  }

  goToStatementDrafts() {
    this.$router.push({
      path: '/statement-drafts',
    });
  }

  addFile(): void {
    this.fileData.id = v4();
    this.files.push(this.fileData);
    this.fileData = {} as FileData;
  }

  addExternalFile(): void {
    this.externalFileData.id = v4();
    this.externalFiles.push(this.externalFileData);
    this.externalFileData = {} as ExternalFile;
  }

  deleteFile(delFile: FileData): void {
    this.files = this.files.filter((file) => {
      console.log(delFile?.id, file?.id);
      return file?.id !== delFile?.id;
    });
  }

  deleteExternalFile(delFile: ExternalFile): void {
    this.externalFiles = this.externalFiles.filter((file) => {
      console.log(delFile?.id, file?.id);
      return file?.id !== delFile?.id;
    });
  }

  stepForward(): void {
    this.step = this.nextStep.name;
  }

  goAction() {
    switch (this.action) {
      case 'next':
        this.stepForward();
        break;

      case 'send':
        this.sendStatement();
        break;
    }

    this.unSaveDataDialog = false;
    this.unSaveData = false;
    this.unSaveDataLabel = '';
  }

  checkUnSaveData(action: string, label = ''): void {
    if (label) {
      this.unSaveDataLabel = label;
    }
    if (!this.allowStepForward) {
      this.$refs.formGeneratorWrapper[0].triggerValidate();
      this.$refs.externalFormGeneratorWrapper.triggerValidate();
      return;
    }
    this.action = action;
    if (this.unSaveData) {
      this.unSaveDataDialog = true;
    } else {
      this.goAction();
    }
  }

  goBack(): void {
    this.step = this.prevStep.name;
  }

  addDocumentsToPrintedStatement(model: any) {
    const newModel = cloneDeep(model);

    newModel.documents = this.files.map((f: FileData) => ({
      type: {
        name: f.type.label || f.type.data.name
      },
      file: {
        name: f.file.name
      }
    }));

    if (this.externalFiles.length) {
      newModel.externalDocuments = [];
    }
    this.externalFiles.forEach((pf: ExternalFile) => 
      newModel.externalDocuments.push({
        type: {
          name: pf.type.label || pf.type.data.name
        },
        file: {
          name: `${pf.quantity} шт.${pf.comment ? (', ' + pf.comment) : ''}` 
        }
      })
    )

    return newModel;
  }

  async setDictionaryValues(model: any) {
    // @ts-ignore
    const dictionaryMap = createDictionaryMapFromSchema(this.schema);
    return await setValuesFromDictionaries(model, dictionaryMap);
  }

  async downloadStatement() {
    let model = this.addDocumentsToPrintedStatement(this.model);
    model = await this.setDictionaryValues(model);

    await downloadAgreement(model);

    await downloadStatement(
      this.schema,
      this.authorSchema,
      model,
      this.fullService,
      false,
    );

    this.hasStatementBeenDownloaded = true;
  }

  unionSignedFiles(signedFiles: FileData[]) {
    let filesToPush = cloneDeep(signedFiles);
    this.files = this.files.map((f: FileData) => {
      const newFile = signedFiles
        .find((sf: FileData) => f.type.data.code === sf.type.data.code);
      if (newFile) {
        filesToPush = filesToPush
          .filter((f: FileData) => f.type.data.code !== newFile.type.data.code);
        return newFile;
      }
      return f;
    });
    filesToPush.forEach((f: FileData) => this.files.push(f));
  }

  mounted() {
    this.$root.$on(
      'TableFillUnSaveData',
      ({ label, unSaveData }: { label: string; unSaveData: boolean }) => {
        this.unSaveData = unSaveData;
        this.unSaveDataLabel = label;
      }
    );
  }

  clearData(): void {
    const pathsToClear = getPathsToClear(this.steps, this.currentStepIndex, this.schema, this.model);
    pathsToClear.forEach((d: string) => {
      set(this.model, d, clearValue(d));
    });
    if (pathsToClear.length) {
      console.debug('[create] cleared paths', pathsToClear);
    }

    if (this.currentStepIndex < this.documentStepIndex) {
      if (this.files.length) {
        console.debug('[create] cleared documents', this.files);
        this.files = [];
      }
    }

    if (this.statementDocuments?.length) {
      this.clearStatementDocuments();
    }

    this.closeClearModal();
    this.$store.commit('formGenerator/setSteppedBack', false);
    this.clearedData = true;
  }

  allowJump(stepIndex: number): boolean {
    const allowJumpBackwards = this.currentStepIndex > stepIndex;
    return allowJumpBackwards || this.allowJumpForward;
  }

  closeClearModal(): void {
    this.$store.commit('formGenerator/setClearModal', false);
  }

  clearStatementDocuments(): void {
    this.hasStatementBeenDownloaded = false;
    this.$store.commit('formGenerator/setStatementDocuments', []);
  }

  async declineReservation(): Promise<void> {
    if (!this.reservationAttr) {
      return;
    }
    const reservation = this.getCurrentTicket(false);
    if (reservation?.bookId) {
      this.loading = true;
      try {
        const { data } = await ticketsApi.declineTicketsByBookId([reservation.bookId]);
        notifyInfo('Предыдущая резервация автоматически отменена');
      } catch (e) {
        if (e.response?.data?.errorMessage) {
          notifyError(e.response.data.errorMessage);
        } else if (e instanceof Error) {
          notifyError(e.message);
        }
      } finally {
        this.loading = false;
      }
    }
  }

  async destroyed(): Promise<void> {
    this.closeClearModal();
    this.$store.commit('formGenerator/setSteppedBack', false);
  }
}
