<i18n>
{
    "en": {
        "upload": {
            "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": {
        "upload": {
            "SignDialog": {
                "document": "Документ",
                "additionalDocuments": "Дополнительные документы",
                "sign": "Подписать",
                "fileToSignWasNotFound": "Файлы для подписания не найдены.",
                "cryptoProIncorrectlyConfigured": "Неверно настроена программа \"КриптоПро\" или не установлен плагин для браузера.",
                "failedGenerate": "Не удалось сформировать файлы результирующего документа.",
                "isNotValid": "Ошибка формирования подписи. Невалидный сертификат или лицензия.",
                "listOfCertificates": "Список сертификатов",
                "documentSignature": "Подпись документа",
                "validTo": "Действителен до",
                "s3Error": "Ошибка сервиса s3",
                "s3PutError": "Ошибка при загрузке файла в S3"
            }
        }
    }
}
</i18n>

<template>
  <div>
    <q-dialog v-model="open" @hide="hideModal">
      <q-card class="full-width no-shadow" style="max-width: 580px">
        <q-card-section>
          <div class="text-h6">
            {{ $t('upload.SignDialog.documentSignature') }}
          </div>
        </q-card-section>
        <q-card-section class="q-gutter-y-md">
          <table class="text-primary" v-if="model">
            <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>
                      {{ model.title }}
                    </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('upload.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('upload.SignDialog.validTo') }}:
                    {{ convertDateFromBackendFormat(opt.data.validTo) }}
                  </div>
                </div>
              </q-item>
            </template>
          </q-select>
        </q-card-section>
        <q-card-actions align="right">
          <q-btn
            v-if="model && model.reportLink"
            :disable="!isValidSystemSetup || !certificate"
            :loading="loading"
            :label="$t('upload.SignDialog.sign')"
            unelevated
            color="primary"
            @click="handler('sign')"
          >
            <template #loading>
              <q-spinner-gears />
            </template>
          </q-btn>
          <q-btn
            v-else
            :disable="!isValidSystemSetup || !certificate"
            :loading="loading"
            :label="$t('upload.SignDialog.sign')"
            unelevated
            color="primary"
            @click="newObjSign"
          >
            <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>

<script lang="ts">
// @ts-nocheck
import { convertDateFromBackendFormat, isPastDate } from '@utils/format-date';
import { notifyError, notifySuccess } from '@utils/notify';
import S3UrlSignerFilesService from '@services/s3urlsigner/files';
import { Component, Prop, PropSync, Watch, Vue } from 'vue-property-decorator';
import { RegistryApi } from '@openapi/registry-data';
import get from 'lodash/get';
import { WorkflowServiceApi } from '@openapi/wf';

const wfApi = new WorkflowServiceApi();

const PDF_FORMAT = 'PDF';
const XML_FORMAT = 'XML';
const CREATE_SIGN_PROCESS_CODE = 'RegistryKompensDectskOtdMIRCreateSign';

@Component({
  methods: {
    convertDateFromBackendFormat,
  },
  components: {},
})
export default class RegistriesUploadSignDialog extends Vue {
  @PropSync('openDialog') open?: boolean;
  @Prop() schema!: string;
  @Prop() model!: any;
  @Prop() processInfo!: any;

  private certificate = null;
  private certificates = [];
  private isValidSystemSetup = false;
  private loading = false;
  private positionFromAccount = null;
  private signatureHash = '';

  private get currentUser() {
    return this.$store.getters['oidcClient/currentUser'] || null;
  }

  @Watch('model', { immediate: true })
  onDialogDataChange(val: any): void {
    if (val && this.open) {
      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,
                }));
              });
          } else {
            notifyError(
              this.$t('upload.SignDialog.cryptoProIncorrectlyConfigured')
            );

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

          if (message) {
            notifyError(message);
          } else {
            notifyError(
              this.$t('upload.SignDialog.cryptoProIncorrectlyConfigured')
            );
          }
          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('upload.SignDialog.cryptoProIncorrectlyConfigured')
          );
        }
        this.loading = false;
      });
    if (typeof isValid !== 'undefined' ? isValid : false) {
      try {
        const certificate = this.certificate.value;
        const base64 = await this.$store
          .dispatch('utils/cryptoPro/generateBase64ByCertificate', {
            certificate,
          })
          .catch((e) => notifyError(e));
        this.$store
          .dispatch('wf/processes/dispatchProcess', {
            processKey: CREATE_SIGN_PROCESS_CODE,
            data: {
              exportId: this.model.id,
              dataFileLink: this.model.dataFileLink,
              reportLink: this.model.reportLink,
              cert: base64,
            },
          })
          .then(({ traceId }) => {
            const signListener = async (e: any) => {
              const data = JSON.parse(get(e, 'data', '{}'));
              if (traceId === get(data, 'traceId')) {
                this.$websockets.removeListener(signListener);
                if (data?.body) {
                  const signedList = data.body.signedList;
                  const dataFile = signedList.find(
                    (i) => i.format === XML_FORMAT
                  );
                  const report = signedList.find(
                    (i) => i.format === PDF_FORMAT
                  );
                  const dataFileSig = await this.S3UrlSigner(
                    dataFile.s3url,
                    dataFile.digest,
                    dataFile.format,
                    certificate
                  );
                  const reportSig = await this.S3UrlSigner(
                    report.s3url,
                    report.digest,
                    report.format,
                    certificate
                  );
                  const registryApi = new RegistryApi();
                  await registryApi.updateRegistry([
                    {
                      ...this.model,
                      signed: true,
                      state: this.$t('registries.unloadingList.signed'),
                      sigHash: this.signatureHash,
                      reportLink: report.s3url,
                      dataFileSig,
                      reportSig,
                      certificate,
                    },
                  ]);
                  this.hideModal();
                  this.$emit('listRefresh');
                  notifySuccess('Документы успешно подписаны');
                }
              }
            };

            this.$websockets.addListener(signListener);
          });
      } catch (e) {
        notifyError(e.message.toString());
        this.hideModal();
      }
    } else {
      this.loading = false;
      notifyError(this.$t('upload.SignDialog.isNotValid'));
    }
  }

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

  private async S3UrlSigner(
    docUrl: string,
    hash: string,
    format: string,
    cert
  ): Promise {
    try {
      const name = docUrl.split('/').slice(-1).join('/') ?? '';

      if (format === PDF_FORMAT) {
        this.signatureHash = hash;
      }
      const signature = await this.$store.dispatch(
        'utils/cryptoPro/createSignature',
        {
          hash,
          certificate: cert,
        }
      );
      const blob = new Blob([signature], {
        type: 'text/plain',
      });
      const signFile = new File([blob], `${name}.sig`);
      const s3url = S3UrlSignerFilesService.uploadFileToS3ByPath({
        file: signFile,
        path: `Applications/${this.model?.departmentId}/sign/${signFile.name}`,
      });
      return Promise.resolve(s3url);
    } catch (e) {
      return Promise.reject(e);
    }
  }

  private async newObjSign() {
    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('upload.SignDialog.cryptoProIncorrectlyConfigured')
          );
        }
        this.loading = false;
      });
    if (typeof isValid !== 'undefined' ? isValid : false) {
      try {
        const certificate = this.certificate.value;
        const base64 = await this.$store
          .dispatch('utils/cryptoPro/generateBase64ByCertificate', {
            certificate,
          })
          .catch((e) => notifyError(e));
        wfApi
          .submitForm(this.processInfo?.taskId, {
            cert: base64,
          })
          .then(() => {
            const listener = async (e: any) => {
              const data = JSON.parse(get(e, 'data', '{}'));
              if (this.processInfo?.traceId === get(data, 'traceId')) {
                if (data?.body && data?.body.type === 'info') {
                  this.$websockets.removeListener(listener);
                  const signedList = data.body.signedList;
                  const dataFile = signedList.find(
                    (i) => i.format === XML_FORMAT
                  );
                  const report = signedList.find(
                    (i) => i.format === PDF_FORMAT
                  );
                  const dataFileSig = await this.S3UrlSigner(
                    dataFile.s3url,
                    dataFile.digest,
                    dataFile.format,
                    certificate
                  );
                  const reportSig = await this.S3UrlSigner(
                    report.s3url,
                    report.digest,
                    report.format,
                    certificate
                  );
                  const registryApi = new RegistryApi();
                  await registryApi.updateRegistry([
                    {
                      id: data.body.exportId,
                      reportLink: report.s3url,
                      dataFileLink: dataFile.s3url,
                      signed: true,
                      state: this.$t('registries.unloadingList.signed'),
                      sigHash: this.signatureHash,
                      dataFileSig,
                      reportSig,
                      certificate,
                    },
                  ]);
                  this.processInfo.success = true;
                  this.hideModal();
                  this.$emit('listRefresh');
                  notifySuccess('Документы успешно подписаны');
                }
              }
            };
            this.$websockets.addListener(listener);
          });
      } catch (e) {
        notifyError(e.message.toString());
        this.hideModal();
      }
    }
  }

  private abortSignProcess(): void {
    wfApi.submitForm(this.processInfo?.taskId, {
      cert: null,
    });
  }

  private hideModal(): void {
    if (this.processInfo && !this.processInfo.success) {
      this.abortSignProcess();
    }
    this.open = false;
    this.certificate = undefined;
    this.certificates = [];
    this.model = undefined;
    this.isValidSystemSetup = false;
    this.loading = false;
    this.signatureHash = '';
    this.processInfo = null;
  }

  async getAccountFromPosition(): void {
    const accId = get(this.currentUser, 'sub');
    const account = await this.$store.dispatch('idm/accounts/dispatchAccount', {
      id: accId,
    });

    this.positionFromAccount = get(account, 'position', null);
  }

  create(): void {
    this.getAccountFromPosition();
  }
}
</script>
