<template>
  <div v-if="common.active === 'documents'" class="q-pb-md">
    <a v-show="false" ref="export" />
    <q-card
      v-if="hasFile && allDocuments.length > 0"
      class="no-shadow q-py-md q-mx-md q-mb-lg"
    >
      <div v-for="(doc, index) in allDocuments" :key="`docTable-${index}`">
        <q-table
          class="no-shadow text-grey-9 q-ma-md"
          v-if="doc.visible"
          row-key="id"
          :columns="columns"
          :data="doc.documents"
          :title="doc.title"
          wrap-cells
          hide-bottom
          :pagination.sync="pagination"
        >
          <!--      eslint-disable-next-line-->
          <template v-slot:body-cell-file.name="props">
            <q-td :props="props">
              {{ props.value }}
            </q-td>
          </template>
          <!--      eslint-disable-next-line-->
          <template v-slot:body-cell-comment="props">
            <q-td :props="props">
              {{ props.value }}
            </q-td>
          </template>
          <!--      eslint-disable-next-line-->
          <template v-slot:body-cell-file="props">
            <q-td :props="props">
              <div class="row">
                <q-btn
                  :disable="!get(props.row, 'file.url')"
                  flat
                  dense
                  icon="search"
                  size="16"
                  style="color: rgba(0, 0, 0, 0.54)"
                  color="primary"
                  @click="fetchDocumentAttachment(props.row)"
                >
                  <q-tooltip>
                    {{ $t('registries.objectCard.preview') }}
                  </q-tooltip>
                </q-btn>
                <q-btn
                  :disable="!get(props.row, 'file.url')"
                  target="_blank"
                  flat
                  dense
                  icon="arrow_downward"
                  size="16"
                  style="color: rgba(0, 0, 0, 0.54)"
                  color="primary"
                  @click="fetchDocumentAttachment(props.row, 'download')"
                >
                  <q-tooltip>
                    {{ $t('registries.objectCard.download') }}
                  </q-tooltip>
                </q-btn>
                <q-btn
                  v-if="!revisionNumber"
                  flat
                  dense
                  icon="attach_file"
                  size="16"
                  style="color: rgba(0, 0, 0, 0.54)"
                  color="primary"
                  @click="upload.dialog.value = true"
                >
                  <q-tooltip>
                    {{ $t('registries.uploadDocumentBtnLabel') }}
                  </q-tooltip>
                </q-btn>
                <q-btn
                  v-if="props.row.sign"
                  flat
                  dense
                  icon="gesture"
                  size="16"
                  style="color: rgba(0, 0, 0, 0.54)"
                  color="positive"
                  @click="signatureHandler(props.row, 'info')"
                >
                  <q-tooltip>
                    {{ $t('registries.objectCard.viewingEDSData') }}
                  </q-tooltip>
                </q-btn>
                <q-btn
                  v-if="get(props.row, 'sign')"
                  flat
                  dense
                  icon="gesture"
                  size="16"
                  style="color: rgba(0, 0, 0, 0.54)"
                  color="primary"
                  @click="signatureHandler(props.row, 'create')"
                >
                  <q-tooltip>
                    {{ $t('registries.objectCard.addEDS') }}
                  </q-tooltip>
                </q-btn>
              </div>
            </q-td>
          </template>
        </q-table>
      </div>
    </q-card>
    <q-card v-else class="no-shadow q-py-md q-mx-md q-mb-lg">
      <div class="full-width text-center">
        {{ $t('registries.objectCard.noDocuments') }}
      </div>
      <q-btn
        class="q-mx-auto block q-mt-md"
        icon="attach_file"
        color="primary"
        @click="upload.dialog.value = true"
      >
        {{ $t('registries.uploadDocumentBtnLabel') }}
      </q-btn>
    </q-card>
    <q-dialog v-model="upload.dialog.value">
      <q-card class="no-shadow" style="max-width: 80vw; width: 700px">
        <q-card-section class="text-h6">
          {{ $t('registries.uploadDocumentBtnLabel') }}
        </q-card-section>
        <q-card-section>
          <base-uploader
            :need-file="false"
            :show-description="false"
            accept=".pdf, .doc, .docx, .xls, .xlsx, .txt, image/*"
            @input="(inputFiles) => (upload.files = inputFiles)"
            @upload="uploadDocument"
          />
        </q-card-section>
      </q-card>
    </q-dialog>
    <q-dialog v-model="signature.dialog.value">
      <q-card class="full-width no-shadow" style="max-width: 480px">
        <q-card-section>
          <div class="text-h6">
            {{ $t('registries.objectCard.documentSignature') }}
          </div>
        </q-card-section>
        <template v-if="signature.dialog.type === 'info'">
          <q-card-section class="q-gutter-y-md">
            <q-banner class="bg-grey-1">
              <template #avatar>
                <q-icon color="positive" name="check_circle_outline" />
              </template>
              <div>{{ $t('registries.objectCard.signatureIsValid') }}</div>
            </q-banner>
            <table v-if="signature.attachment" class="text-primary">
              <tr>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label class="text-grey-9">
                        {{ $t('registries.objectCard.signed') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label>
                        {{
                          get(
                            signature.attachment,
                            'sign.certificate.subjectName'
                          )
                        }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
              </tr>
              <tr>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label class="text-grey-9">
                        {{ $t('registries.objectCard.certificatePublisher') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label>
                        {{
                          get(
                            signature.attachment,
                            'sign.certificate.issuerName'
                          )
                        }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
              </tr>
              <tr>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label class="text-grey-9">
                        {{ $t('registries.objectCard.document') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label>
                        {{ get(signature.attachment, 'file.name') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
              </tr>
              <tr>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label class="text-grey-9">
                        {{ $t('registries.objectCard.download') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label>
                        <q-btn
                          :label="$t('registries.objectCard.signedFile')"
                          icon="attach_file"
                          outline
                          @click="
                            fetchDocumentAttachment(
                              signature.attachment,
                              'download',
                              'sign.name',
                              'sign.url'
                            )
                          "
                        />
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
              </tr>
            </table>
          </q-card-section>
        </template>
        <template v-if="signature.dialog.type === 'create'">
          <q-card-section class="q-gutter-y-md">
            <table v-if="signature.attachment" class="text-primary">
              <tr>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label class="text-grey-9">
                        {{ $t('registries.objectCard.document') }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
                <td>
                  <q-item>
                    <q-item-section>
                      <q-item-label>
                        {{ signature.attachment.name }}
                      </q-item-label>
                    </q-item-section>
                  </q-item>
                </td>
              </tr>
            </table>
            <q-select
              v-model="signature.certificate"
              :disable="!signature.certificates.length"
              :options="signature.certificates"
              class="full-width"
              filled
              label="Список сертификатов"
            />
          </q-card-section>
          <q-card-actions align="right">
            <q-btn
              :disable="!signature.certificate"
              :loading="signature.loading"
              color="primary"
              label="Подписать документ"
              @click="createSignatureHandler"
            >
              <template #loading>
                <q-spinner-gears />
              </template>
            </q-btn>
          </q-card-actions>
        </template>
      </q-card>
    </q-dialog>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import S3UrlSignerFilesService from '@services/s3urlsigner/files';
import { DocumentApi } from '@openapi/registry-data';
import { CardNeedToUpdateType } from '@constants/models/Card';
import { TablePagination } from '@models/base/TablePagination';
import QColumn from '@/types/QColumn';
import { BaseCardCommon } from '@/interfaces';
import { DOC_TYPE_CONFIG, DocTypeCode } from '@/constants/DocumentTypes';
import { fetchDocumentAttachment } from '@utils/fetch-document-attachment';
import { notifyError, notifySuccess } from '@utils/notify';
import get from 'lodash/get';
import set from 'lodash/set';

@Component({
  methods: {
    fetchDocumentAttachment,
    get,
  },
})
export default class RegistryDocs extends Vue {
  @Prop() model: any;
  @Prop() common: BaseCardCommon;

  private registryCode = this.$route.query.reg as string;
  private pagination = new TablePagination({ rowsPerPage: 0 });
  private signature: any = {
    attachment: null,
    certificate: null,
    certificates: [],
    dialog: {
      type: '',
      value: false,
    },
    isValidSystemSetup: false,
    loading: false,
  };

  private upload: any = {
    files: [],
    dialog: {
      type: '',
      value: false,
    },
  };

  private get columns(): Array<QColumn> {
    return [
      {
        align: 'left',
        field: (val) => get(val, 'type.name') || '',
        label: this.$t('registries.objectCard.documentType'),
        name: 'type',
        sortable: true,
      },
      {
        align: 'left',
        field: (val) => get(val, 'file.name') || '',
        label: this.$t('registries.objectCard.documentName'),
        name: 'file.name',
        sortable: true,
      },
      {
        align: 'left',
        field: 'file',
        label: this.$t('registries.objectCard.documentAction'),
        name: 'file',
        sortable: false,
      },
    ];
  }

  private get documents(): Array<any> {
    return [this.model.document];
  }

  private get allDocuments(): Array<any> {
    const resultType = DOC_TYPE_CONFIG.find(
      (i: any) => i.code === DocTypeCode.RESULT
    );
    const allDocs =
      this.availableDocuments?.map((typeName: string) => {
        const docs =
          this.model.allDocuments?.filter(
            (doc: any) => doc.source === typeName
          ) ?? [];
        const type = DOC_TYPE_CONFIG.find((i: any) => i.code === typeName);
        return {
          visible: docs.length > 0,
          title: type?.label,
          type: type?.code,
          documents: docs,
        };
      }) ?? [];
    if (
      (allDocs.length === 0 ||
        !this.model.allDocuments ||
        !this.hasResultDoc(allDocs)) &&
      this.documents?.length > 0
    ) {
      allDocs.push({
        visible: true,
        title: resultType?.label,
        type: resultType?.code,
        documents: this.documents,
      });
    }
    return allDocs;
  }

  private get availableDocuments(): Array<any> | undefined {
    return this.registry?.availableDocuments;
  }

  private get registry(): any {
    const models = this.$store.getters[`utils/cards/models`] || {};
    return models[this.registryCode];
  }

  private get revisionNumber(): string | (string | null)[] {
    return this.$route.query.num;
  }

  private get hasFile(): boolean {
    return !!this.model?.document?.file;
  }

  private hasResultDoc(docs: Array<any>): boolean {
    return docs.some((doc: any) => doc.type === DocTypeCode.RESULT);
  }

  signatureHandler(attachment: any, dialogType: string): void {
    this.signature.attachment = attachment;
    this.signature.dialog.type = dialogType;

    switch (dialogType) {
      case 'create':
        this.$store
          .dispatch('utils/cryptoPro/fetchCertificates')
          .then((certificates) => {
            this.signature.certificates = certificates.map(
              (certificate: any) => ({
                label: certificate.subjectName,
                value: certificate,
              })
            );

            if (this.signature.certificates.length) {
              this.signature.dialog.value = true;
            } else {
              notifyError(
                this.$t(
                  this.$t(
                    'registries.objectCard.noCertificatesFound'
                  ).toString()
                ).toString(),
                'warning',
                'warning'
              );
            }
          })
          .catch((e) => {
            notifyError(e.message.toString());
          });

        break;
      case 'info':
        this.signature.dialog.value = true;
        break;
    }
  }

  saveObjWithFile(file: any, s3url: string | undefined): void {
    const obj = { ...this.model };
    const newFile = {
      name: file.name,
      url: s3url,
    };
    obj.document = { file: newFile };
    set(obj, 'document.sign', '');
    this.updateObject(obj);
  }

  saveObjWithSign(
    s3url: string | undefined,
    signFileName: string,
    cert: any
  ): void {
    const { issuerName, subjectName } = cert;
    const obj = { ...this.model };
    set(obj, 'document.sign', {
      name: signFileName,
      url: s3url,
      certificate: {
        issuerName,
        subjectName,
      },
    });
    this.updateObject(obj);
  }

  private async updateObject(obj: any): Promise<void> {
    const documentApi = new DocumentApi();
    return documentApi
      .updateDocument(obj.schema, [obj])
      .then(() => {
        notifySuccess(this.$t('registries.objUpdatedSuccessfully').toString());
      })
      .catch((e) => {
        console.error(e);
        notifyError(this.$t('registries.errorObjUpdate').toString());
      });
  }

  private uploadDocument(): Promise<void> {
    const file = this.upload.files[0];

    return S3UrlSignerFilesService.uploadFileToS3ByPath({
      file,
      path: `Registries/${get(this.common, 'id')}/file/${get(file, 'name')}`,
    })
      .then((s3Url: string | undefined) => {
        notifySuccess(
          this.$t('registries.documentsUploadedSuccessfully').toString()
        );

        this.saveObjWithFile(file, s3Url);
        setTimeout(() => {
          this.$store.commit(
            `utils/cards/needToUpdate`,
            CardNeedToUpdateType.TRUE
          );
        }, 1000);
      })
      .catch(() => {
        notifyError(this.$t('registries.errorLoadingDocuments').toString());
      })
      .finally(() => {
        this.upload.dialog.value = false;
      });
  }

  createSignatureHandler(): void {
    const file = this.model.document.file;
    const name = file.name;

    S3UrlSignerFilesService.fetchFileFromS3ByUrl({ url: file.url })
      .then(({ file }) =>
        this.$store.dispatch('utils/cryptoPro/generateHash', {
          file,
        })
      )
      .then((hash) =>
        this.$store.dispatch('utils/cryptoPro/createSignature', {
          hash,
          certificate: this.signature.certificate.value,
        })
      )
      .then((signature) => {
        const blob = new Blob([signature], {
          type: 'text/plain',
        });
        const signFile = new File([blob], `${name}.sig`);
        return S3UrlSignerFilesService.uploadFileToS3ByPath({
          file: signFile,
          path: `Registries/${this.common.id}/sign/${signFile.name}`,
        });
      })
      .then((s3url) => {
        this.saveObjWithSign(
          s3url,
          `${name}.sig`,
          this.signature.certificate.value
        );
        setTimeout(() => {
          this.$store.commit(
            `utils/cards/needToUpdate`,
            CardNeedToUpdateType.TRUE
          );
        }, 1000);
      })
      .finally(() => {
        this.signature.loading = false;
        this.signature.certificate = null;
        this.signature.dialog.value = false;
        this.signature.dialog.type = '';
      });
  }
}
</script>
