<i18n>
{
  "en": {
    "document": "Document",
    "signature": "Sign",
    "typeFile": "File type",
    "nameFile": "File name",
   "signatureDocuments": "To sign document",
   "certificate": "Сertificate",
   "certificateValidTo": "Действителен до",
   "cryptoProIncorrectlyConfigured": "The CryptoPro program is incorrectly configured or the browser plug-in is not installed.",
   "isNotValid": "Signature generation error. Invalid certificate.",
   "cryptoProSuccess": "Success",
   "sendSuccess": "Success",
   "notSelectedDocumentType": "Not selected document type",
   "labelInputFile": "Select a document. *.pdf, *.tiff up to 10 Mb",
   "errorSizeFile": "Maximum file size exceeded - 10 MB"
  },
  "ru": {
   "document": "Документ",
   "signature": "Подпись",
   "typeFile": "Тип файла",
   "nameFile": "Название файла",
   "signatureDocuments": "Подписать документ",
   "certificate": "Сертификат",
   "certificateValidTo": "Действителен до",
   "cryptoProIncorrectlyConfigured": "Неверно настроена программа \"КриптоПро\" или не установлен плагин для браузера.",
   "isNotValid": "Ошибка формирования подписи. Невалидный сертификат или лицензия.",
   "cryptoProSuccess": "Документы подписаны",
   "sendSuccess": "Электронный дубликат отправлен!",
   "notSelectedDocumentType": "Не выбран тип документа",
   "labelInputFile": "Выберите документ. *.pdf, *.tiff до 10 Мб",
   "errorSizeFile": "Превышен максимальный объем файла - 10 Мб"
  }
}
</i18n>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import { v4 } from 'uuid';

import { QSelectOption } from '@/models/models-ui';
import { notifyError, notifySuccess } from '@utils/notify';
import { convertDateFromBackendFormat, isPastDate } from '@utils/format-date';
import S3UrlSignerFilesService from '@services/s3urlsigner/files';
import { CollapsedProfileApi } from '@openapi/idm';

import FormGeneratorWrapper from '@components/common/node-generator/FormGeneratorWrapper.vue';

import schemeJson from './schema.json';
import { getServicesOfOrganization } from '@/services/registry-service';

type ProcessStep = {
  title: string;
  name: string;
  icon: string;
};

type FileModel = {
  id: string;
  type: 'document' | 'sign';
  file: File;
  typeDocument: QSelectOption;
};

type Certificate = {
  label: string;
  value: any;
  data: any;
};

const PROCESS_NAME = 'BaseMFCProccess';
const CLASS_PROVIDER =
  'ElkOrderRequest@urn://epgu.gosuslugi.ru/elk/status/1.0.2';
const ACCESS_SERVICE = '93493fd9-81ea-419e-b14c-e2142e9d8a17';
const CODE_STORAGE = 'electronic-duplicate';

const collapsedProfileApi = new CollapsedProfileApi();

@Component({
  components: {
    FormGeneratorWrapper,
  },
})
export default class StatementsElectronicDuplicate extends Vue {
  loading = false;

  processCurrentStep = '';

  modelForm: any = {
    employee: {
      officeName: '',
      officeAdress: '',
      fio: '',
      position: '',
    },
  };

  fileModel: File | null = null;
  filesModel: FileModel[] = [];

  // @TODO временное решение, пока не тянем схему из сервиса
  schema: typeof schemeJson | null = null;

  $refs!: {
    formGeneratorWrapper: any;
  };

  certificates: Certificate[] = [];
  certificate: Certificate | null = null;
  certificateLoading = false;

  openDialog = false;

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

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

  get processCurrentStepIndex() {
    return this.processSteps.findIndex(
      (step) => step.name === this.processCurrentStep
    );
  }

  get processNextStep() {
    return this.processSteps[this.processCurrentStepIndex + 1];
  }

  get processPrevStep() {
    return this.processSteps[this.processCurrentStepIndex - 1];
  }

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

    const schema = cloneDeep(this.schema);
    let { groupList, attrList } = schema;

    groupList = groupList.filter(
      (group) =>
        (group.name === this.processCurrentStep || group?.parent) &&
        !group?.props?.external
    );

    attrList = attrList.filter((attr) => {
      for (let index = 0; index < groupList.length; index++) {
        const { attrList } = groupList[index];

        if (attrList.includes(attr.name)) {
          return true;
        }
      }

      return false;
    });

    if (!attrList.length) return null;

    schema.groupList = groupList;
    schema.attrList = attrList;

    return schema;
  }

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

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

  get validUploadFile() {
    return !!this.fileModel && !!this.modelForm?.documents?.type?.label;
  }

  get filesModalByDocuments() {
    return this.filesModel.filter((v) => v.type === 'document');
  }

  get filesModalBySign() {
    return this.filesModel.filter((v) => v.type === 'sign');
  }

  async mounted() {
    this.loading = true;

    try {
      const serviceIds = await getServicesOfOrganization(
        this.currentUser.domain_id
      );

      const successfulAccess = !!serviceIds.find((id) => id === ACCESS_SERVICE);

      if (!successfulAccess) {
        this.$router.push({ name: 'statements' });
      }

      await this.$store.dispatch('idm/accounts/dispatchAccount', {
        id: this.currentUser.sub,
        data: {
          expand: ['user-domain', 'person'],
        },
      });

      // @TODO async get form schema
      this.schema = schemeJson;
    } catch (error: any) {
      notifyError(error);
    } finally {
      this.loading = false;
    }

    if (this.processSteps.length) {
      this.processCurrentStep = this.processSteps[0].name;
    }

    if (this.userAccount) {
      const { address, fullname, inn, kpp, ogrn } = this.userAccount[
        'user-domain'
      ];
      const { fio } = this.userAccount['person'];
      const { position } = this.userAccount;
      const { officeName, officeAdress } =
        JSON.parse(localStorage.getItem(CODE_STORAGE) || '{}') || {};

      this.modelForm.employee.officeName = officeName || fullname || '';
      this.modelForm.employee.officeAdress = officeAdress || address || '';
      this.modelForm.employee.fio = fio || '';
      this.modelForm.employee.position = position || '';
      this.modelForm.employee.officeINN = inn || '';
      this.modelForm.employee.officeKPP = kpp || '';
      this.modelForm.employee.officeOGRN = ogrn || '';
    }
  }

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

  onProcessStepBack() {
    this.processCurrentStep = this.processPrevStep.name;
  }

  onProcessStepForward() {
    const [form] = this.$refs.formGeneratorWrapper;

    if (!form.triggerValidate()) {
      return;
    }

    this.processCurrentStep = this.processNextStep.name;
  }

  handleFile(file: File) {
    const { size } = file;

    if (!size || size / 1024 / 1024 > 10) {
      return notifyError(this.$t('errorSizeFile').toString());
    }

    this.fileModel = file;
  }

  addFileDocument() {
    if (!this.validUploadFile) {
      notifyError(this.$t('notSelectedDocumentType').toString());

      return;
    }

    const file = this.fileModel as File;
    const id = v4();

    this.filesModel.push({
      id,
      type: 'document',
      file,
      typeDocument: this.modelForm.documents.type as QSelectOption,
    });

    this.fileModel = null;
  }

  removeFile(file: FileModel) {
    this.filesModel = this.filesModel.filter((v) => v.id !== file.id);

    if (file.type === 'document' && !this.filesModalByDocuments.length) {
      this.filesModel = [];
    }
  }

  convertDateFromBackendFormat(date: any, dateFormat?: string) {
    return convertDateFromBackendFormat(date, dateFormat);
  }

  isExpired(date: string): boolean {
    return isPastDate(date);
  }

  async openDialogSignatureDocuments() {
    this.certificate = null;

    try {
      const isValid = await this.$store.dispatch(
        'utils/cryptoPro/isValidSystemSetup'
      );

      if (!isValid) {
        throw new Error(this.$t('cryptoProIncorrectlyConfigured').toString());
      }

      const certificates = await this.$store.dispatch(
        'utils/cryptoPro/fetchCertificates'
      );

      this.certificates = certificates.map((certificate: any) => ({
        label: certificate.subjectName,
        value: certificate,
        data: certificate,
      }));

      this.openDialog = true;
    } catch (error: any) {
      const message = get(error, 'message');

      if (message) {
        notifyError(message);
        return;
      }

      notifyError(error);
    }
  }

  async onSignatureDocument(context: FileModel) {
    if (!this.certificate?.value) return;

    try {
      const hash = await this.$store.dispatch('utils/cryptoPro/generateHash', {
        file: context.file,
      });

      const signature = await this.$store.dispatch(
        'utils/cryptoPro/createSignature',
        {
          hash,
          certificate: this.certificate.value,
        }
      );

      const blob = new Blob([signature], {
        type: 'text/plain',
      });

      const file = new File([blob], `${context.file.name}.sig`);
      const id = v4();

      const fileModel: FileModel = {
        id,
        type: 'sign',
        file,
        typeDocument: context.typeDocument,
      };

      return fileModel;
    } catch (error: any) {
      const message = get(error, 'message');

      if (message) {
        notifyError(message);
        return;
      }

      notifyError(error);
    }
  }

  async onSignatureDocuments() {
    if (!this.certificate?.value) return;

    this.certificateLoading = true;

    try {
      const isValid = await this.$store.dispatch(
        'utils/cryptoPro/isValidSystemCertificate',
        this.certificate.value
      );

      if (!isValid) {
        throw new Error(this.$t('isNotValid').toString());
      }

      const files = await Promise.all(
        this.filesModalByDocuments.map((context) =>
          this.onSignatureDocument(context)
        )
      );

      (files || []).forEach((file) => {
        if (!file) return;
        this.filesModel.push(file);
      });

      notifySuccess(this.$t('cryptoProSuccess').toString());
      this.openDialog = false;
    } catch (error: any) {
      const message = get(error, 'message');

      if (message) {
        notifyError(message);
        return;
      }

      notifyError(error);
    } finally {
      this.certificateLoading = false;
    }
  }

  async onSendModelForm() {
    const [form] = this.$refs.formGeneratorWrapper;

    if (!form.triggerValidate()) {
      return;
    }

    this.loading = true;

    try {
      const { data: profile } = await collapsedProfileApi.getCollapsedProfile();

      const [document, sign] = await Promise.all(
        this.filesModel.map(async (context) => {
          const dir = context.type === 'sign' ? 'sign' : 'file';

          const s3Url = await S3UrlSignerFilesService.uploadFileToS3ByPath({
            file: context.file,
            path: `Applications/${CODE_STORAGE}/${dir}/${context.file.name}`,
          });

          return { ...context, s3Url };
        })
      );

      const data = {
        class: CLASS_PROVIDER,
        dataRequest: {
          ...this.modelForm,
          documents: {
            document: {
              codeDocument: document.type,
              id: document.id,
              type: document.file.type,
              name: document.file.name,
              url: document.s3Url,
              typeDocument: document.typeDocument,
            },
            sign: {
              codeDocument: sign.type,
              id: sign.id,
              name: sign.file.name,
              url: sign.s3Url,
            },
          },
        },
      };

      const payload = {
        processKey: PROCESS_NAME,
        userId: profile.id,
        data,
      };

      await this.$store.dispatch('wf/processes/dispatchProcess', payload);

      notifySuccess(this.$t('sendSuccess').toString());

      // @TODO временное решение от Сергея Самойленко - потому это заменится на хранение данных в профиле
      localStorage.setItem(
        CODE_STORAGE,
        JSON.stringify(this.modelForm?.employee || {})
      );

      this.modelForm = {
        employee: this.modelForm.employee,
      };

      this.fileModel = null;
      this.filesModel = [];

      this.processCurrentStep = this.processSteps[0].name;
    } catch (error: any) {
      const message = get(error, 'message');

      if (message) {
        notifyError(message);
        return;
      }

      notifyError(error);
    } finally {
      this.loading = false;
    }
  }
}
</script>

<style lang="scss">
.file-cell {
  white-space: pre-line;
}

.document-card {
  width: 100%;
  max-width: 350px;
}

.grid-item-value {
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>

<template>
  <div class="manual">
    <div
      class="flex justify-start items-center bg-white q-pa-md q-mt-md q-mx-md rounded-borders"
    >
      <q-btn
        padding="xs"
        class="text-grey-7 q-mr-md"
        color="grey-3"
        icon="arrow_back"
        @click="goToPageStatements"
      />
      <span class="text-h5">{{ $t('statements.manualInput') }}</span>
    </div>

    <div
      class="q-gutter-y-md row bg-white q-px-lg q-pb-md q-pt-sm q-mt-md q-mx-md rounded-borders"
    >
      <div class="col text-primary q-pa-sm">
        <div class="q-gutter-y-md">
          <q-card class="no-border no-shadow">
            <q-card-section>
              <q-stepper v-model="processCurrentStep" vertical animated flat>
                <q-step
                  v-for="step in processSteps"
                  :key="step.name"
                  :active-icon="step.icon"
                  :icon="step.icon"
                  :name="step.name"
                  :title="step.title"
                >
                  <div v-if="step.name === 'docCopy'" class="documents">
                    <FormGeneratorWrapper
                      ref="formGeneratorWrapper"
                      mode="create"
                      :model="modelForm"
                      :schema="formGeneratorSchema"
                    />

                    <div
                      class="q-mb-lg row items-start q-gutter-md"
                      v-if="filesModel && filesModel.length"
                    >
                      <q-card
                        bordered
                        class="document-card"
                        v-for="file in filesModel || []"
                        :key="file.id"
                      >
                        <q-card-section class="entry header text-h6">
                          {{
                            file.type === 'document'
                              ? $t('document')
                              : $t('signature')
                          }}
                        </q-card-section>

                        <q-separator inset />

                        <q-card-section class="entry">
                          <div
                            class="grid-item-label text-weight-medium text-grey-9"
                          >
                            {{ $t('typeFile') }}
                          </div>
                          <div class="grid-item-value">
                            {{ file.typeDocument.label }}
                          </div>
                        </q-card-section>

                        <q-card-section class="entry">
                          <div
                            class="grid-item-label text-weight-medium text-grey-9"
                          >
                            {{ $t('nameFile') }}
                          </div>
                          <div class="grid-item-value">
                            {{ file.file.name.slice(0, 30) }}
                            <span v-show="file.file.name.length > 30">...</span>
                            <q-tooltip :content-style="{ 'font-size': '12px' }">
                              {{ file.file.name }}
                            </q-tooltip>
                          </div>
                        </q-card-section>

                        <q-card-actions align="right">
                          <q-btn
                            @click="removeFile(file)"
                            text-color="negative"
                          >
                            {{ $t('base.delete') }}
                          </q-btn>
                        </q-card-actions>
                      </q-card>
                    </div>

                    <q-file
                      v-else
                      class="q-mb-md"
                      :label="$t('labelInputFile')"
                      outlined
                      accept=".pdf, .tiff"
                      :value="fileModel"
                      @input="handleFile"
                    />

                    <div>
                      <q-btn
                        v-if="
                          filesModalByDocuments.length &&
                          !filesModalBySign.length
                        "
                        unelevated
                        @click="openDialogSignatureDocuments"
                        color="secondary"
                        :label="$t('signatureDocuments')"
                      />

                      <q-btn
                        v-if="!filesModalByDocuments.length"
                        unelevated
                        @click="addFileDocument"
                        color="secondary"
                        :label="$t('base.document.addFile')"
                        :disable="!fileModel"
                      />
                    </div>
                  </div>
                  <FormGeneratorWrapper
                    v-else
                    ref="formGeneratorWrapper"
                    mode="create"
                    :model="modelForm"
                    :schema="formGeneratorSchema"
                  />

                  <q-stepper-navigation>
                    <q-btn
                      v-show="processPrevStep"
                      @click="onProcessStepBack"
                      class="on-left"
                      color="secondary"
                      :label="$t('base.back')"
                    />
                    <q-btn
                      v-show="processNextStep"
                      @click="onProcessStepForward"
                      color="primary"
                      :label="$t('base.forward')"
                    />
                  </q-stepper-navigation>
                </q-step>
              </q-stepper>
            </q-card-section>
          </q-card>

          <q-card-section v-if="modelForm && schema">
            <div class="flex justify-end q-gutter-sm">
              <q-btn
                :label="$t('base.send')"
                color="primary"
                :disabled="!filesModalBySign.length"
                @click="onSendModelForm"
              />
            </div>
          </q-card-section>

          <q-inner-loading :showing="loading">
            <q-spinner-gears color="primary" size="50px" />
          </q-inner-loading>
        </div>
      </div>
    </div>

    <q-dialog v-model="openDialog" :persistent="certificateLoading">
      <q-card class="full-width no-shadow" style="max-width: 580px">
        <q-card-section>
          <div class="text-h6">
            {{ $t('signatureDocuments') }}
          </div>
        </q-card-section>

        <q-card-section class="q-gutter-y-md">
          <q-select
            v-model="certificate"
            :disable="!certificates.length"
            :options="certificates"
            filled
            :label="$t('certificate')"
          >
            <template #option="{ opt, toggleOption, selected }">
              <q-item
                @click="toggleOption(opt)"
                :class="{
                  'bg-red-1': isExpired(opt.data.validTo),
                  'bg-blue-1': selected,
                }"
                clickable
              >
                <div>
                  <div class="text-grey-8">{{ opt.label }}</div>
                  <div :style="{ fontSize: '0.75rem' }">
                    {{ $t('certificateValidTo') }}:
                    {{ convertDateFromBackendFormat(opt.data.validTo) }}
                  </div>
                </div>
              </q-item>
            </template>
          </q-select>
        </q-card-section>

        <q-card-actions align="right">
          <q-btn
            :disable="!certificate"
            :loading="certificateLoading"
            :label="$t('signature')"
            unelevated
            color="primary"
            @click="onSignatureDocuments"
          >
            <template #loading>
              <q-spinner-gears />
            </template>
          </q-btn>
        </q-card-actions>

        <q-inner-loading :showing="certificateLoading">
          <q-spinner-gears color="primary" size="50px" />
        </q-inner-loading>
      </q-card>
    </q-dialog>
  </div>
</template>
