<i18n>
{
    "en": {
        "statements": {
            "SignDialog": {
                "document": "Document",
                "additionalDocuments": "Additional documents",
                "sign": "Sign",
                "fileToSignWasNotFound": "The file to sign was not found",
                "cryptoProIncorrectlyConfigured": "The CryptoPro program is incorrectly configured or the browser plug-in is not installed.",
                "failedGenerate": "Failed to generate the resulting document files.",
                "isNotValid": "Signature generation error. Invalid certificate.",
                "listOfCertificates": "List of certificates",
                "documentSignature": "Document signature",
                "validTo": "Valid to",
                "s3Error": "S3 service error",
                "s3PutError": "Error loading file into S3"
            }
        }
    },
    "ru": {
        "statements": {
            "SignDialog": {
                "document": "Документ",
                "additionalDocuments": "Дополнительные документы",
                "sign": "Подписать",
                "fileToSignWasNotFound": "Файл для подписывания не найден.",
                "cryptoProIncorrectlyConfigured": "Неверно настроена программа \"КриптоПро\" или не установлен плагин для браузера.",
                "failedGenerate": "Не удалось сформировать файлы результирующего документа.",
                "isNotValid": "Ошибка формирования подписи. Невалидный сертификат или лицензия.",
                "listOfCertificates": "Список сертификатов",
                "documentSignature": "Подпись документа",
                "validTo": "Действителен до",
                "s3Error": "Ошибка сервиса s3",
                "s3PutError": "Ошибка при загрузке файла в S3"
            }
        }
    }
}
</i18n>

<script lang="ts">
// @ts-nocheck
import { convertDateFromBackendFormat, isPastDate } from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { notifyError } from '@utils/notify';
import S3UrlSignerFilesService from '@services/s3urlsigner/files';
import get from 'lodash/get';
import { parseCertificatePairs } from '@/utils/crypto-pro/helpers';

const EXTENSIONS_TO_SIGN = ['pdf', 'docx', 'html'];

export default {
  props: [
    'common',
    'disable-all-buttons',
    'user-task',
    'button-settings',
    'model',
  ],
  data() {
    return {
      button: {
        props: {
          color: 'red',
          label: this.$t('statements.SignDialog.sign'),
          loading: false,
        },
      },
      certificate: null,
      certificates: [],
      document: null,
      multipleDocuments: [],
      additionalDocuments: [],
      documentsToSubmit: [],
      isValidSystemSetup: false,
      loading: false,
      value: false,
      positionFromAccount: null,
    };
  },
  computed: {
    recordInfo() {
      const goalsList =
        this.$store.getters['registryService/services/servicesCard']
          .goalsList || [];
      const goalValue = String(get(this.model, 'statement.goal', ''));
      const target =
        goalsList?.find((goal) => goal.value === goalValue)?.label || '';
      const idERN =
        this.model.IDERN ||
        get(this.model, 'statement.recruitsData.mainInfo.[0].systemIdErn', '');

      return {
        buttonCode: 'application_sign',
        extraInfo: {
          id: this.model?.id,
          type: 'application',
          number: this.model?.common?.orderId,
          idERN,
          fio: this.model?.applicant?.fullName,
          date: get(this.model, 'system.startDate', ''),
          target,
        },
      };
    },

    processName() {
      return this.isMultipleStamp
        ? 'StampSignDoubleProcess'
        : 'StampSignProcess';
    },
    isMultipleStamp() {
      return ['accreditationITResult1'].includes(
        get(this.document, 'type.code')
      );
    },
    multipleProcessData() {
      return {
        first: {
          documentFormat: this.signDocumentFormat(this.document?.file.url),
          printFormat: 'DOCX',
          rightText: [this.signerPosition, this.signerName].join('\n'),
          leftText: this.signDate,
          pageNumber: 1,
        },
        second: {
          documentFormat: this.signDocumentFormat(this.document?.file.url),
          printFormat: 'PDF',
          rightText: [this.signerPosition, this.signerName].join('\n'),
          leftText: this.signDate,
          pageNumber: 2,
        },
      };
    },
    currentUser() {
      return this.$store.getters['oidcClient/currentUser'] || null;
    },
    signDate() {
      return convertDateFromBackendFormat(new Date(), DateFormat.date);
    },
    signerName() {
      let fullName;

      if (this.certificate) {
        const certData = parseCertificatePairs(this.certificate.data.subjectName);       
        const familyName = certData.byKey('SN');
        const givenName = certData.byKey('G');
        fullName = familyName && givenName ? `${familyName} ${givenName}` : null;
      }

      if (!fullName) {
        const familyName = get(this.currentUser, 'family_name', '');
        const givenName = get(this.currentUser, 'given_name', '');
        const middleName = get(this.currentUser, 'middle_name', '');

        fullName = this.currentUser
          ? `${familyName} ${givenName} ${middleName}`
          : '';
      }

      return fullName;
    },
    signerPosition() {
      let position;

      if (this.certificate) {
        const certData = parseCertificatePairs(this.certificate.data.subjectName);       
        position = certData.byKey('T');
      }

      if (this.positionFromAccount && !position) {
        position = this.positionFromAccount;
      }

      return position;
    },
  },
  methods: {
    get,
    prepareDocsForProcess(documents) {
      /*
      leftText, rightText - дополнительный текст над печатью
      * */
      return documents.map((doc) => {
        return {
          id: doc.id,
          s3url: doc.file?.url,
          documentFormat: this.signDocumentFormat(doc.file?.url),
          documentName: doc.file?.name,
          printFormat: 'PDF',
          rightText: [this.signerPosition, this.signerName].join('\n'),
          leftText: this.signDate,
        };
      });
    },
    signDocumentFormat(stringUrl) {
      if (typeof stringUrl === 'string') {
        const stringArr = stringUrl.split('.') || [];
        const format = stringArr[stringArr.length - 1] || 'docx';
        return format.toUpperCase();
      }
      return 'DOCX';
    },
    doAction() {
      this.$recordActivity({
        buttonCode: 'application_sign_prepare',
        extraInfo: {
          previewInfo: [
            {
              value: this.model?.common?.orderId,
              label: 'номер',
            },
          ],
        },
      });
      this.multipleDocuments = get(
        this.userTask,
        'userTask.outData.documentToAssign',
        []
      );
      this.additionalDocuments = this.multipleDocuments.filter(
        (doc) => !doc.generated
      );
      this.document = this.multipleDocuments.find((doc) => doc.generated);

      if (this.multipleDocuments.length) {
        this.$store
          .dispatch('utils/cryptoPro/isValidSystemSetup')
          .then((isValid) => {
            this.isValidSystemSetup = isValid;

            if (isValid) {
              this.$store
                .dispatch('utils/cryptoPro/fetchCertificates')
                .then((certificates) => {
                  this.certificates = certificates.map((certificate) => ({
                    label: certificate.subjectName,
                    value: certificate,
                    data: certificate,
                  }));

                  this.value = true;
                });
            } else {
              notifyError(
                this.$t('statements.SignDialog.cryptoProIncorrectlyConfigured')
              );

              this.loading = false;
            }
          })
          .catch((e) => {
            const message = get(e, 'message');

            if (message) {
              notifyError(message);
            } else {
              notifyError(
                this.$t('statements.SignDialog.cryptoProIncorrectlyConfigured')
              );
            }
            this.loading = false;
          });
      } else {
        notifyError(this.$t('statements.SignDialog.fileToSignWasNotFound'));
        this.loading = false;
      }
    },
    async handler() {
      this.loading = true;
      const isValid = await this.$store
        .dispatch(
          'utils/cryptoPro/isValidSystemCertificate',
          this.certificate.value
        )
        .catch((e) => {
          const message = get(e, 'message');
          if (message) {
            notifyError(message);
          } else {
            notifyError(
              this.$t('statements.SignDialog.cryptoProIncorrectlyConfigured')
            );
          }
          this.loading = false;
        });
      if (typeof isValid !== 'undefined' ? isValid : false) {
        const base64 = await this.$store
          .dispatch('utils/cryptoPro/generateBase64ByCertificate', {
            certificate: this.certificate.value,
          })
          .catch((e) => notifyError(e));

        if (base64) {
          let data = {};
          if (this.isMultipleStamp) {
            data = this.multipleProcessData;
          } else {
            data = {
              documents: this.prepareDocsForProcess(
                this.multipleDocuments.filter((doc) => {
                  const filename = get(doc, 'file.name') || '';
                  const ext = filename.split('.').pop()?.toLowerCase();
                  return EXTENSIONS_TO_SIGN.includes(ext);
                })
              ),
            };
          }
          this.$store
            .dispatch('wf/processes/dispatchProcess', {
              processKey: 'MultipleStampSignProcess',
              data: {
                ...data,
                certificateAsBase64: base64,
              },
            })
            .then((response) => {
              const traceId = get(response, 'traceId');
              const signListener = async (e) => {
                const data = JSON.parse(get(e, 'data', '{}'));

                if (traceId === get(data, 'traceId')) {
                  // Удаление слушателя onMessage после прихода подходящего пуша
                  this.common.$websockets.removeListener(signListener);

                  if (data?.body?.length) {
                    const stampedDocuments = data.body;
                    this.signMultipleDocuments(stampedDocuments)
                      .then(() => {
                        this.$emit('submit', {
                          buttonId: 'sign',
                          data: {
                            documents: [
                              ...this.documentsToSubmit,
                              ...this.multipleDocuments.filter((doc) => {
                                const filename = get(doc, 'file.name') || '';
                                const ext = filename.split('.').pop();
                                return !EXTENSIONS_TO_SIGN.includes(ext);
                              }),
                            ],
                          },
                          force: true,
                        });
                      })
                      .finally(() => {
                        this.value = false;
                        this.certificate = undefined;
                        this.certificates = [];
                        this.document = undefined;
                        this.isValidSystemSetup = false;
                        this.loading = false;
                      });
                  } else if (get(data, 'body.type') === 'error') {
                    notifyError(
                      this.$t('statements.SignDialog.failedGenerate')
                    );

                    this.loading = false;
                  }
                }
              };

              // Добавление слушателя onMessage к уже имеющимся
              this.common.$websockets.addListener(signListener);
            })
            .catch((error) => {
              this.loading = false;
              this.value = false;
              if (error instanceof Error) notifyError(error.message);
            });
        } else {
          this.loading = false;
        }
      } else {
        notifyError(this.$t('statements.SignDialog.isNotValid'));
        this.loading = false;
      }
    },
    isExpired(date) {
      return isPastDate(date);
    },
    async getAccountFromPosition() {
      const accId = get(this.currentUser, 'sub');
      const account = await this.$store.dispatch(
        'idm/accounts/dispatchAccount',
        {
          id: accId,
        }
      );

      this.positionFromAccount = get(account, 'position', null);
    },
    convertDateFromBackendFormat,
    signMultipleDocuments(stampedDocuments) {
      return new Promise((resolve) => {
        if (stampedDocuments.length) {
          for (const doc of stampedDocuments) {
            const name = get(doc, 'name', 'file');
            const url = get(doc, 's3url', '');
            this.$store
              .dispatch('utils/cryptoPro/createSignature', {
                hash: doc?.digest,
                certificate: this.certificate.value,
              })
              .then((signature) => {
                const blob = new Blob([signature], {
                  type: 'text/plain',
                });
                const file = new File([blob], `${name}.sig`);
                S3UrlSignerFilesService.uploadFileToS3ByPath({
                  file,
                  path: `Applications/${this.common.id}/sign/${file.name}`,
                }).then((s3Url) => {
                  const fullDocument = this.multipleDocuments.find((el) => {
                    return el.id === doc.id;
                  });
                  const document = {
                    ...doc,
                    type: fullDocument.type,
                    source: fullDocument.source,
                    file: {
                      url,
                      name,
                    },
                    sign: {
                      certificate: {
                        subjectName: get(
                          this.certificate,
                          'value.subjectName',
                          ''
                        ),
                        issuerName: get(
                          this.certificate,
                          'value.issuerName',
                          ''
                        ),
                      },
                      name: file.name,
                      url: s3Url,
                    },
                  };
                  this.documentsToSubmit.push(document);
                  if (
                    this.documentsToSubmit.length === stampedDocuments.length
                  ) {
                    resolve();
                  }
                });
              });
          }
        } else {
          resolve();
        }
      });
    },
  },
  mounted() {
    this.getAccountFromPosition();
  },
};
</script>

<template>
  <div>
    <div
      class="cursor-pointer"
      v-bind="button.props"
      v-if="$slots.button"
      @click="doAction"
    >
      <slot name="button" />
    </div>
    <q-btn
      :disable="disableAllButtons"
      class="q-ml-sm"
      outline
      :label="get(buttonSettings, 'props.label')"
      :color="get(buttonSettings, 'props.color')"
      v-bind="button.props"
      v-else
      @click="doAction"
    />
    <q-dialog v-model="value" :persistent="loading">
      <q-card class="full-width no-shadow" style="max-width: 580px">
        <q-card-section>
          <div class="text-h6">
            {{ $t('statements.SignDialog.documentSignature') }}
          </div>
        </q-card-section>
        <q-card-section class="q-gutter-y-md">
          <table class="text-primary" v-if="document">
            <tr>
              <td>
                <q-item>
                  <q-item-section>
                    <q-item-label class="text-grey-9">
                      {{ $t('statements.DocumentsCard.document') }}
                    </q-item-label>
                  </q-item-section>
                </q-item>
              </td>
              <td>
                <q-item>
                  <q-item-section>
                    <q-item-label>
                      {{ document.file.name }}
                    </q-item-label>
                  </q-item-section>
                </q-item>
              </td>
            </tr>
            <tr v-if="additionalDocuments.length">
              <td style="vertical-align: top">
                <q-item>
                  <q-item-section>
                    <q-item-label class="text-grey-9">
                      {{ $t('statements.SignDialog.additionalDocuments') }}
                    </q-item-label>
                  </q-item-section>
                </q-item>
              </td>
              <td>
                <q-item :key="doc.id" v-for="doc in additionalDocuments">
                  <q-item-section>
                    <q-item-label>
                      {{ doc.file.name }}
                    </q-item-label>
                  </q-item-section>
                </q-item>
              </td>
            </tr>
          </table>
          <q-select
            v-model="certificate"
            :disable="!certificates.length"
            :options="certificates"
            class="full-width"
            filled
            :label="$t('statements.SignDialog.listOfCertificates')"
          >
            <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' }" class="">
                    {{ $t('statements.SignDialog.validTo') }}:
                    {{ convertDateFromBackendFormat(opt.data.validTo) }}
                  </div>
                </div>
              </q-item>
            </template>
          </q-select>
        </q-card-section>
        <q-card-actions align="right">
          <q-btn
            :disable="!isValidSystemSetup || !certificate"
            :loading="loading"
            :label="$t('statements.SignDialog.sign')"
            unelevated
            color="primary"
            v-activity-recorder="recordInfo"
            @click="handler('sign')"
          >
            <template #loading>
              <q-spinner-gears />
            </template>
          </q-btn>
        </q-card-actions>
        <q-inner-loading :showing="loading">
          <q-spinner-gears color="primary" size="50px" />
        </q-inner-loading>
      </q-card>
    </q-dialog>
  </div>
</template>
