<template>
  <div class="multi-sign">
    <r-btn
      dense
      positive
      :disable="disable"
      :label="label"
      icon="pgs-sign"
      no-wrap
      @click="open"
    >
      <q-tooltip v-if="!!attachments.length" anchor="bottom middle">
        <div
          class="tooltipe-recruit"
          v-for="rec in attachments"
          :key="`recruit-${rec.recruitId}`"
        >
          <div class="tooltipe-recruit__name">
            {{ rec.recruitFullName }}
          </div>
          <div class="tooltipe-recruit__documents">
            <div
              v-for="doc in rec.attachments"
              :key="`document-${doc.recruitId}`"
              class="tooltipe-recruit__document"
            >
              {{ getFileName(doc) }}
            </div>
          </div>
        </div>
      </q-tooltip>
    </r-btn>
    <operation-dialog
      sign
      persistent
      ref="dialog"
      :title="titleWindow"
      :sub="sub"
      v-model="opened"
      @hide="close"
    >
      <div v-if="!!attachments.length" class="multi-sign-recruits">
        <div
          v-for="rec in attachments"
          :key="`recruit-${rec.recruitId}`"
          class="multi-sign-recruit"
        >
          <div class="multi-sign-recruit__name">
            {{ rec.recruitFullName }}
          </div>

          <div class="multi-sign-recruit__documents">
            <div
              v-for="doc in rec.attachments"
              :key="`document-${doc.recruitId}`"
              class="multi-sign-recruit__document"
            >
              <q-icon
                class="multi-sign-recruit__document-icon"
                name="pgs-document"
                size="18px"
              />
              <span class="multi-sign-recruit__document-name">
                {{ getFileName(doc) }}
              </span>
              <span style="flex: 1 1 auto"></span>
              <base-preview
                v-if="doc"
                :s3Url="checkUrlForPreview(doc)"
                :extension="get(doc, 'file.format') || get(doc, 'format')"
              >
                <q-icon class="SummonsTable-Icon" name="pgs-view">
                  <q-tooltip>Предпросмотр</q-tooltip>
                </q-icon>
              </base-preview>
            </div>
          </div>
        </div>
      </div>
      <r-input
        v-if="flagCancelButton"
        label="Комментарий"
        v-model="cancelComment"
      />
      <template #process>
        <result-block type="info" icon="pgs-sign" :header="processMessage">
          <TransitionGroup name="results" tag="div">
            <div v-for="res in resultList" :key="res.id">
              <q-icon name="pgs-sign-2" color="info" size="sm" />
              <span>{{ get(res, 'fullName') }}</span>
            </div>
          </TransitionGroup>
        </result-block>
      </template>
      <template #result>
        <result-block
          :type="hasError ? 'negative' : 'positive'"
          :header="resultMessage"
        >
          <div class="results">
            <div v-for="res in resultList" :key="res.id">
              <q-icon name="pgs-check" color="positive" size="sm" />
              <span>{{ get(res, 'fullName') }}</span>
            </div>
          </div>
        </result-block>
      </template>
      <template #right-actions>
        <r-btn
          v-if="needCancelButton && !flagCancelButton"
          negative
          label="Отклонить"
          icon="pgs-close"
          @click="openRejectModal()"
        >
        </r-btn>
        <r-btn
          v-if="!flagCancelButton"
          action
          label="Подписать"
          icon="pgs-sign"
          :disable="disable || !certificateIsActive"
          @click="action"
        />
        <r-btn
          v-if="flagCancelButton"
          action
          label="Продолжить"
          icon="pgs-check"
          :disable="disable"
          @click="action"
        />
      </template>
      <template
        v-if="operationDialog && operationDialog.formState === 'start'"
        #left-actions-panel
      >
        <r-btn
          outline
          label="Отмена"
          icon="pgs-close"
          color="primary"
          @click="closeModal"
        />
      </template>
      <template #right-actions-result>
        <r-btn
          action
          label="Продолжить"
          icon-right="pgs-shevron-right-off"
          @click="finish"
        />
      </template>
      <template #sign-form v-if="!flagCancelButton">
        <crypto-pro-sign-form
          ref="signForm"
          class="no-right-border-radius"
          :process-key="processKey"
          :user-task="userTask"
          :task-process="taskProcess"
          bucket="ervu-attachment"
          @update:certificateIsActive="certificateIsActive = $event"
          @signed="onSigned"
          @error="onError"
          @finish="onFinish"
        />
      </template>
    </operation-dialog>
  </div>
</template>

<i18n>
{
  "ru": {
    "MultiSign": {
      "selected": "не выбрано | {n} документ | {n} документа | {n} документов",
      "signing": " | Документ подписывается | Документы подписываются | Документы подписываются",
      "signed": " | Документ подписан | Документы подписаны | Документы подписаны",
      "error": " | Документ не подписан | Документы не подписаны | Документы не подписаны"
    },
    "MultiReject": {
      "selected": "не выбрано | {n} документ | {n} документа | {n} документов",
      "rejecting": " | Документ отклоняется | Документы отклоняются | Документы отклоняются",
      "rejected": " | Документ отклонен | Документы отклонены | Документы отклонены",
      "error": " | Документ не отклонен | Документы не отклонены | Документы не отклонены"
    }
  }
}
</i18n>

<style lang="scss" scoped>
.SummonsTable-Icon {
  overflow: hidden;
  color: #1491cf;
  font-size: 26px;
  padding: 6px 3px;
  cursor: pointer;
}

.tooltipe-recruit {
  margin-bottom: 8px;

  &:last-child {
    margin-bottom: 0;
  }

  &__name {
    margin-bottom: 2px;
    font-weight: 500;
  }
}

.multi-sign-recruits {
  display: flex;
  flex-flow: column;
}

.multi-sign-recruit {
  background: #ffffff;
  border-bottom: 1px solid rgba(1, 72, 116, 0.11);
  box-shadow: none;
  padding: 8px 0;

  &__name {
    font-weight: bold;
  }

  &__documents {
    display: flex;
    flex-flow: column;
  }

  &__document {
    display: flex;
    flex-flow: row;
    align-items: center;
  }

  &__document-name,
  &__document-icon {
    color: #6c7276;
  }

  &__document-icon {
    transform: translateX(-2px);
  }
}

.results-enter-active,
.results-leave-active {
  transition: all 0.5s ease;
}

.results-enter-from,
.results-leave-to {
  opacity: 0;
  transform: translateY(-30px);
}

// TODO вынести
.title-text {
  font-family: 'Golos Text', sans-serif;
  font-style: normal;
  font-weight: 500;
  font-size: 20px;
  line-height: 24px;
  letter-spacing: -0.015em;

  color: rgba(2, 12, 18, 0.9);
}
</style>

<script lang="ts">
import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator';
import dataBlock from '@components/cards/statements/components/dataBlock.vue';
import TaskButton from '@/mixins/TaskButton';
import OperationDialog from '@/components/common/operation-dialog/index.vue';
import ProcessStarter from '@/mixins/ProcessStarter';
import CryptoProSignForm from '@/components/common/cryptopro-sign-form/index.vue';
import {
  RecruitV3,
  PersonRegistryApi,
} from '@/recruits/openapi/person-registry';
import { AttachedDocument } from '@/recruits/interfaces/deprecated/attached-document';
import {
  MilitaryCommissarDecisions,
  DecisionDocumentServiceApi,
} from '@/recruits/openapi/decision-document';

import {
  SubpoenaRegistryApi,
  SubpoenaByRecruitV2,
} from '@/recruits/openapi/subpoena-service';
import get from 'lodash/get';
import { SignData } from '@/mixins/CriptoProPlugin';
import isString from 'lodash/isString';
import isEmpty from 'lodash/isEmpty';
import RInput from '@/recruits/components/common/r-input.vue';
import { notifyError } from '@utils/notify';
import { convertDateFromBackendFormat, DateFormat } from '@utils/format-date';

type PersonInfo = {
  id: string;
  fullName: string;
};

interface FormModel {
  id: string | null;
  personIds: string[];
  cancelComment: string | null;
  listId: string | null;
  signDate: string | null;
}

export interface RecruitAttachment {
  recruitFullName: string;
  recruitIdERN: string;
  recruitId: string;
  attachments: AttachedDocument[];
}

interface RecruitWithAttachments extends RecruitV3 {
  attachedDecisions: MilitaryCommissarDecisions[];
}

interface RecruitListItem {
  id: string;
  fio: string;
  idERN: string;
  documentName: string;
}

@Component({
  components: {
    dataBlock,
    CryptoProSignForm,
    RInput,
  },
  methods: {
    get,
  },
})
export default class MultiSign extends Mixins(TaskButton, ProcessStarter) {
  get: typeof get;

  // Принимаем список для подписания извне.
  @Prop({ type: Array, default: () => [] }) signatureList: RecruitAttachment[];

  @Prop({ default: () => null }) data: any;

  @Prop({ default: () => [] })
  declare readonly model: (string | RecruitV3)[];

  @Prop({ default: () => false })
  readonly summon: boolean;

  @Prop({ default: () => '' })
  readonly title: string;

  @Prop({ default: () => null })
  // @ts-ignore
  processKey: string | null;

  @Ref('dialog') operationDialog: OperationDialog;

  @Ref('signForm') signForm: CryptoProSignForm;

  personRegistryApi = new PersonRegistryApi();
  decisionDocumentServiceApi = new DecisionDocumentServiceApi();

  opened = false;

  certificateIsActive = false;

  hasError = false;

  resultList: PersonInfo[] = [];

  titleWindow = this.$props.title;

  checkUrlForPreview(doc: any) {
    return (
      get(doc, 'file.s3url') ||
      get(doc, 'file.s3Url') ||
      get(doc, 's3Url') ||
      get(doc, 's3url')
    );
  }

  @Watch('flagCancelButton')
  changeTitle(): void {
    this.titleWindow = this.flagCancelButton ? 'Отклонение' : 'Подписание';
  }

  getFileName(doc: any): string {
    return (
      doc.name ||
      get(doc, 'fileName') ||
      get(doc, 'file.s3Name') ||
      get(doc, 's3Name')
    );
  }

  get processMessage(): string {
    return this.flagCancelButton
      ? this.$tc('MultiReject.rejecting', this.length)
      : this.$tc('MultiSign.signing', this.length);
  }

  get resultMessage(): string {
    if (this.hasError) {
      return this.flagCancelButton
        ? this.$tc('MultiReject.error', this.length)
        : this.$tc('MultiSign.error', this.length);
    }
    return this.flagCancelButton
      ? this.$tc('MultiReject.rejected', this.length)
      : this.$tc('MultiSign.signed', this.length);
  }

  async getRecruit(id: string): Promise<RecruitWithAttachments> {
    const { data } = await this.personRegistryApi.getOneRecruitV4(id);
    (data as RecruitWithAttachments).attachedDecisions =
      (await this.decisionDocumentServiceApi.getDecisionV1(id)).data
        .militaryCommissarDecisions ?? [];
    return data as RecruitWithAttachments;
  }

  async getSummon(id: string): Promise<SubpoenaByRecruitV2[]> {
    const { data } = await new SubpoenaRegistryApi().getSubpoenaByRecruitV2(id);
    return data.subpoenas || [];
  }

  get attachments(): RecruitAttachment[] {
    // Если список для подписания извне не пустой, отображаем его.
    if (this.signatureList.length) return this.signatureList;
    // В противном случаем идем по старой логике.
    return this.prioritySignFlag
      ? [...this.decision]
      : [...this.summons, ...this.decision];
  }

  get length(): number {
    return this.personIds?.length;
  }

  get sub(): string {
    return this.flagCancelButton
      ? `${this.$tc('MultiReject.selected', this.model.length)}`
      : `${this.$tc('MultiSign.selected', this.model.length)}`;
  }

  get personIds(): string[] {
    return (
      this.model?.map((recruitOrId): string => {
        if (isString(recruitOrId)) return recruitOrId;
        return recruitOrId.id;
      }) || []
    );
  }

  recruits: RecruitWithAttachments[] = [];

  summonsByRecruit: Record<string, SubpoenaByRecruitV2[]> = {};

  get summons(): RecruitAttachment[] {
    return this.recruits.flatMap(({ id, mainInfo, systemInfo }) => {
      const notSigned = this.summonsByRecruit[id]
        ?.filter(({ status }) => status === '3.7' || status === '3.13')
        ?.map(({ file }) => file);
      if (!notSigned || !notSigned.length) return [];
      return {
        recruitId: id,
        recruitFullName: mainInfo?.fullName,
        recruitIdERN: systemInfo.idERN,
        attachments: notSigned,
      };
    });
  }

  prioritySignFlag = false;

  get decision(): RecruitAttachment[] {
    return this.recruits.flatMap(
      ({ id, mainInfo, attachedDecisions, systemDocuments, systemInfo }) => {
        // Данный if необходим в связи с кейсом проактивного снятия с учета,
        // Что бы определить приоритетность подписываемых решений или повесток
        if (this.recruits.length === 1) {
          // Проверяем наличие решения о снятии с учета (первый приоритет подписания
          const cancelReg = attachedDecisions?.filter(
            ({ type, status }) => type === '9' && status === '1'
          );
          if (cancelReg && cancelReg.length) {
            this.prioritySignFlag = true;
            return {
              recruitId: id,
              recruitFullName: mainInfo?.fullName,
              attachments: cancelReg,
              recruitIdERN: systemInfo.idERN,
            };
          }

          // Проверяем наличие решения об отсутствии необходимости явки по повестке (второй приоритет)
          const turnoutIsNotRequired = attachedDecisions?.filter(
            ({ type, status }) => type === '11' && status === '1'
          );
          if (turnoutIsNotRequired && turnoutIsNotRequired.length) {
            this.prioritySignFlag = true;
            return {
              recruitId: id,
              recruitFullName: mainInfo?.fullName,
              attachments: turnoutIsNotRequired,
              recruitIdERN: systemInfo.idERN,
            };
          }
        }
        const notSignedDecisions =
          attachedDecisions?.filter(({ status }) => status === '1') || [];

        const notSignedDocuments =
          systemDocuments?.filter(({ status }) => status === 'formed') || [];

        const notSigned = [...notSignedDecisions, ...notSignedDocuments];

        this.prioritySignFlag = false;
        if (!notSigned || !notSigned.length) return [];
        return {
          recruitId: id,
          recruitFullName: mainInfo?.fullName,
          attachments: [notSigned[0]],
          recruitIdERN: systemInfo.idERN,
        };
      }
    );
  }

  @Watch('personIds', { immediate: true })
  async fetchRecruits(ids: string[]): Promise<void> {
    // Выключаем логику загрузки списков для подписания, если его передали извне.
    if (!this.signatureList.length) {
      this.recruits = await Promise.all(
        ids.map(async (id) => {
          const recruitInfo = await this.getRecruit(id);
          if (recruitInfo.status === '3.7' || recruitInfo.status === '3.13') {
            try {
              this.summonsByRecruit[id] = await this.getSummon(id);
            } catch {}
          }
          return recruitInfo;
        })
      );
    }
  }

  get formModel(): FormModel {
    const signDate = convertDateFromBackendFormat(
      Date.now(),
      DateFormat.backend
    );
    return {
      id: this.flagCancelButton ? 'cancel_decision' : null,
      personIds: this.personIds,
      cancelComment: this.cancelComment || null,
      listId: this.data?.listInfo?.id || null,
      signDate,
    };
  }

  flagCancelButton = false;
  cancelComment = '';

  get needCancelButton(): boolean {
    // @ts-ignore Ибо всплыл конфликт модели старой генерации
    return this.userTask?.outData?.options?.sign_registration?.hasCancel;
  }

  async action(): Promise<boolean | void> {
    if (this.flagCancelButton && !this.cancelComment) {
      notifyError('Поле комментарий обязательно к заполнению');
      return false;
    }
    this.$emit('onBeforeAction', 'pack_sign', {
      list: this.recruitsList,
    });

    this.operationDialog.toProcess();
    if (this.flagCancelButton) {
      try {
        await this.$store.dispatch(
          //
          'wfActions/processes/submitForm',
          {
            taskId: this.userTask.taskId,
            traceId: this.taskProcess.traceId,
            formModel: this.formModel,
          }
        );

        this.registerListener((message, removeListener) => {
          if (this.taskProcess.traceId !== message.traceId) return;
          if (message.body.type === 'finish') {
            this.operationDialog.toResult();
            removeListener();
          }
        });
      } catch (error) {
        this.operationDialog.reset();
        notifyError(error.message);
        console.error(error);
      }
    } else {
      try {
        await this.signForm.sendDataForSign(
          (this.formModel as unknown) as Record<string | symbol, unknown>
        );
      } catch (error) {
        this.operationDialog.reset();
        notifyError(error.message);
        console.error(error);
      }
    }
  }

  get recruitsList(): RecruitListItem[] {
    return this.attachments.map((e) => {
      const documentName = e.attachments
        .map(
          (doc) => doc.name || get(doc, 'fileName') || get(doc, 'file.s3Name')
        )
        .join(', ');
      return {
        id: e.recruitId,
        fio: e.recruitFullName,
        idERN: e.recruitIdERN,
        documentName,
      };
    });
  }

  onSigned(data: SignData): void {
    const personInfo: PersonInfo = {
      id: data.operationInfo?.recruitId,
      fullName: data.operationInfo?.recruitFullName,
    };
    if (!isEmpty(personInfo)) this.resultList.push(personInfo);
  }

  onError(): void {
    this.hasError = true;
    this.operationDialog.toResult();
  }

  onFinish(): void {
    this.operationDialog.toResult();
  }

  finish(): void {
    this.close();
    this.$emit('done');
  }

  open(): void {
    if (this.disable) return;
    this.$emit('onBeforeAction', 'pack_prepare', {
      list: this.recruitsList,
    });
    this.opened = true;
    this.$emit('open');
  }

  closeModal(): void {
    if (this.flagCancelButton) {
      this.flagCancelButton = false;
    } else {
      this.close();
      this.$emit('onBeforeAction', 'pack_cancel', {
        list: this.recruitsList,
      });
    }
  }

  openRejectModal(): void {
    this.flagCancelButton = true;
  }

  close(): void {
    this.opened = false;
    this.resultList = [];
    this.operationDialog.reset();
    this.$emit('close');
  }
}
</script>
