<template>
  <white-card class="InfringementAdministrativeBlock">
    <div class="InfringementAdministrativeBlock-Top">
      <div class="InfringementAdministrativeBlock-Header">
        {{ header }}
      </div>
      <r-btn
        v-if="editable"
        dense
        no-wrap
        action
        label="ДОБАВИТЬ ЗАПИСЬ"
        icon="pgs-plus"
        @click="createInfringement"
      />
    </div>
    <div class="InfringementAdministrativeBlock-ContentBox">
      <empty-block v-if="!infringements.length" />
      <div
        class="row q-col-gutter-xs"
        v-for="(infringement, i) in infringements"
        :key="infringement.id"
      >
        <div class="col-12 q-my-sm relative-position">
          <data-block bolder style="padding-top: 12px">
            <q-form
              class="q-gutter-md"
              greedy
              @submit="saveInfringement(infringement)"
              @reset="reset(infringement.id, i)"
            >
              <div class="row q-col-gutter-xs q-mb-lg">
                <div class="InfringementAdministrativeBlock-RecordHeader">
                  <div class="InfringementAdministrativeBlock-RecordCount">
                    {{ infringement.recordNumber }}
                  </div>
                  <template v-if="editor">
                    <div
                      v-if="isFooterVisible(infringement)"
                      class="InfringementAdministrativeBlock-BlockButtons"
                    >
                      <r-btn
                        dense
                        transparent
                        label="ОТМЕНА"
                        icon="pgs-close"
                        no-wrap
                        type="reset"
                      />
                      <r-btn
                        :disabled="
                          !(
                            isCreatedInfrigementAndNotEmpty(infringement) ||
                            changedInfringementIds.includes(infringement.id)
                          )
                        "
                        dense
                        action
                        label="СОХРАНИТЬ"
                        icon="pgs-save"
                        type="submit"
                        no-wrap
                        v-activity-recorder="activityRecorderOption"
                      />
                    </div>
                    <q-btn
                      v-else
                      label="Редактировать"
                      icon="pgs-edit"
                      class="InfringementAdministrativeBlock-EditButton"
                      @click="$set(editor, infringement.id, true)"
                    />
                  </template>
                </div>
                <label class="input-block col-6">
                  <em>Номер дела (досудебный)</em>
                  <q-input
                    v-model.trim="infringement.preTrialNumber"
                    :rules="[rules.required, rules.strings, rules.words]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Дата возбуждения дела о нарушении</em>
                  <x-date-input
                    :rules="[rules.required, rules.date, dateRangeRuleStrict]"
                    :options="datePickerOptionsStrict"
                    :value="unformatDate(infringement.createInfringementDate)"
                    :disable="isDisable(infringement.id)"
                    hide-bottom-space
                    lazy-rules
                    :raw-value="true"
                    @input="
                      infringement.createInfringementDate = formatDate($event)
                    "
                  />
                </label>
                <label class="input-block col-6">
                  <em>Орган, возбудивший дело</em>
                  <q-input
                    v-model.trim="infringement.organization"
                    :rules="[rules.rusStrings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Номер дела (судебный)</em>
                  <q-input
                    v-model.trim="infringement.trialNumber"
                    :rules="[rules.strings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Дата постановления</em>
                  <x-date-input
                    :value="unformatDate(infringement.decisionDate)"
                    :rules="[rules.date, dateRangeRuleStrict]"
                    :options="datePickerOptionsStrict"
                    :disable="isDisable(infringement.id)"
                    hide-bottom-space
                    :raw-value="true"
                    lazy-rules
                    @input="infringement.decisionDate = formatDate($event)"
                  />
                </label>
                <label class="input-block col-6">
                  <em>Суд, вынесший постановление</em>
                  <q-input
                    v-model.trim="infringement.court"
                    :rules="[rules.rusStrings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Военкомат, принявший решение о применении мер</em>
                  <q-input
                    v-model.trim="infringement.vkName"
                    :rules="[rules.rusStrings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Причина применения</em>
                  <q-select
                    v-model.trim="infringement.cause"
                    :disable="isDisable(infringement.id)"
                    emit-value
                    map-options
                    borderless
                    option-value="code"
                    option-label="value"
                    dense
                    :options="causes"
                    class="r-select"
                  >
                    <template v-slot:selected-item="scope">
                      <div class="ellipsis r-select-text">
                        {{ scope.opt.value }}
                      </div>
                    </template>
                  </q-select>
                </label>
                <label class="input-block col-6">
                  <em>Дата, с которой наложена мера</em>
                  <x-date-input
                    :value="unformatDate(infringement.dateApplication)"
                    :rules="[rules.date, dateRangeRule]"
                    :options="datePickerOptions"
                    :raw-value="true"
                    :disable="isDisable(infringement.id)"
                    hide-bottom-space
                    lazy-rules
                    @input="infringement.dateApplication = formatDate($event)"
                  />
                </label>
                <!-- FIXME использовать компонент селекта с поиском -->
                <label class="input-block col-6">
                  <em> Вид наказания </em>
                  <q-select
                    v-model.trim="infringement.punishment"
                    :disable="isDisable(infringement.id)"
                    emit-value
                    map-options
                    use-input
                    borderless
                    dense
                    @filter="filterFn"
                    input-debounce="0"
                    :options="filterOptions"
                    class="r-select"
                  >
                    <template v-slot:no-option>
                      <q-item>
                        <q-item-section class="text-grey">
                          Нет результатов
                        </q-item-section>
                      </q-item>
                    </template>
                    <template v-slot:selected-item="scope">
                      <div class="ellipsis r-select-text">
                        {{ scope.opt.label }}
                      </div>
                    </template>
                  </q-select>
                </label>
                <label class="input-block col-6">
                  <em>Описание наказания</em>
                  <q-input
                    v-model.trim="infringement.description"
                    :rules="[rules.rusStrings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
                <label class="input-block col-6">
                  <em>Основание для внесения записи</em>
                  <q-input
                    v-model.trim="infringement.reasonForAddEntry"
                    :rules="[rules.strings]"
                    :disable="isDisable(infringement.id)"
                    lazy-rules
                    dense
                    borderless
                  />
                </label>
              </div>
            </q-form>
          </data-block>
          <q-inner-loading :showing="loadingIds.includes(infringement.id)">
            <q-spinner-gears size="50px" color="primary"></q-spinner-gears>
          </q-inner-loading>
        </div>
      </div>
    </div>
    <q-dialog v-model="deleteInfringementDialog" no-backdrop-dismiss>
      <q-card>
        <q-card-section class="InfringementDialog-AttentionContent">
          <q-icon
            class="InfringementDialog-AttentionContent__Icon"
            name="pgs-delete"
          />
          <p class="InfringementDialog-AttentionContent__Header">
            Вы уверены, что хотите удалить запись об административном нарушении?
          </p>
        </q-card-section>
        <q-card-actions class="InfringementDialog-AttentionButtons">
          <r-btn
            class="InfringementDialog-AttentionButton"
            label="Закрыть"
            v-close-popup
          />
          <r-btn
            class="InfringementDialog-AttentionButton"
            label="Удалить"
            :action="true"
            @click="deleteInfringement"
          />
        </q-card-actions>
      </q-card>
    </q-dialog>
  </white-card>
</template>

<script lang="ts">
import { Component, PropSync, Prop, Vue, Watch } from 'vue-property-decorator';
import { notifyError, notifySuccess } from '@utils/notify';
import { getDictionaryFormatedToOptions } from '@/recruits/utils';
import dataBlock from '@components/cards/statements/components/dataBlock.vue';
import WhiteCard from '@/recruits/components/common/white-card.vue';
import BlockHeader from '@/recruits/components/common/block-header.vue';
import {
  DecisionDocumentServiceApi,
  InfringementAdministrative,
  InfringementAdministrativeTypeDocumentEnum,
} from '@/recruits/openapi/decision-document';
import isNull from 'lodash/isNull';
import ApiErrors from '@/recruits/components/dialogs/ApiErrors.vue';
import XDateInput from '@/recruits/components/common/x-date-input.vue';
import { VALIDATION_RULES } from '@/recruits/utils/validation-rules';
import moment from 'moment';
import {
  getCurrentDate,
  formatDate,
  unformatDate,
} from '@/utils/field-validation/dates';
import EmptyBlock from '@/recruits/components/recruit-card/infringement/EmptyBlock.vue';

@Component({
  components: {
    EmptyBlock,
    dataBlock,
    WhiteCard,
    BlockHeader,
    XDateInput,
    ApiErrors,
  },
  methods: {
    formatDate,
    unformatDate,
  },
})
export default class InfringementAdministrativeBlock extends Vue {
  @PropSync('infringementsAdministrative')
  _infringementsAdministrative: InfringementAdministrative[];

  @Prop({ required: true }) recruitId: string;
  @Prop() activityRecorderOption: any;
  @Prop({ default: true }) editable: boolean;
  @Prop({ default: '', type: String }) header: string;

  public infringements: InfringementAdministrative[] = [];
  public createdCount = 0; // createdCount нужен чтобы был уникальный id для созданных блоков
  public loadingIds: string[] = [];
  public deleteInfringementDialog = false;
  public deleteInfringementId = '';
  public rules = VALIDATION_RULES;
  public dateRangeRule = this.rules.dateRange({
    from: '1998-03-28',
  });

  editor: Record<string, boolean> = {};

  // recordCount(recordId: string, index: number): string {
  //   return this.isCreatedInfrigement(recordId)
  //     ? 'Создание записи'
  //     : `Запись №${index + 1}`;
  // }

  isDisable(id: InfringementAdministrative['id']): boolean {
    return !(
      id &&
      this.editable &&
      (this.isCreatedInfrigement(id) || this.editor[id])
    );
  }

  public dateRangeRuleStrict = this.rules.dateRange({
    from: '1998-03-28',
    to: getCurrentDate(),
  });

  public datePickerOptions = (dt: string): boolean => {
    return this.dateRangeRule(dt.replace(/\//g, '-')) === true;
  };

  public datePickerOptionsStrict = (dt: string): boolean => {
    return this.dateRangeRuleStrict(dt.replace(/\//g, '-')) === true;
  };

  // FIXME для фильтрации q-select сделать отдельный компонент
  public filterOptions: { label: string; value: string }[] = [];

  DecisionDocumentServiceApi = new DecisionDocumentServiceApi();

  @Watch('_infringementsAdministrative', {
    immediate: true,
  })
  infringementsAdministrativeWatcher(val: InfringementAdministrative[]): void {
    // обновляем только при первом открытии(пока нет данных в локальном массиве infringements)
    // т.к. после сохранение нам надо обновить данные в родителе для синхронизации
    // и если были изменение в другом несохраненном блоке, то он затрется
    if (!this.infringements.length) {
      const data = JSON.parse(JSON.stringify(val));
      this.infringements = data;
    }
  }

  // FIXME для фильтрации q-select сделать отдельный компонент
  @Watch('options', {
    immediate: true,
  })
  optionsWatcher(val: { label: string; value: string }[]): void {
    this.filterOptions = val;
  }

  // FIXME Функция фильтра для q-select сделать отдельный компонент
  filterFn(val: string, update: any): void {
    update(() => {
      const needle = val.toLowerCase();
      this.filterOptions = this.options.filter(
        (v) => v.label.toLowerCase().indexOf(needle) > -1
      );
    });
  }

  // Функция получения index по id
  private getInfringementIndex(id: string): number | null {
    let numElem: number | null = null;
    this.infringements.forEach((el, index) => {
      if (el.id === id) {
        numElem = index;
      }
    });
    return numElem;
  }

  public reset(infringementId: string, index: number): void {
    if (this.isCreatedInfrigement(infringementId)) {
      // открытие окна подтверждения удаления
      this.deleteInfringementId = infringementId;
      this.deleteInfringementDialog = true;
    } else {
      this.$set(
        this.infringements,
        index,
        JSON.parse(JSON.stringify(this._infringementsAdministrative[index]))
      );
      this.editor[infringementId] = false;
    }
  }

  public isFooterVisible(infringement: InfringementAdministrative): boolean {
    return (
      !!infringement.id &&
      (this.isCreatedInfrigement(infringement.id) ||
        this.editor[infringement.id])
    );
  }

  // Функция для определения блок создается или редактируется
  public isCreatedInfrigement(
    infringementId: InfringementAdministrative['id']
  ): boolean {
    return !!infringementId && infringementId.includes('created');
  }

  // Функция для определния заполнено ли хоть одно поле в новом блоке
  public isCreatedInfrigementAndNotEmpty(
    infringement: InfringementAdministrative
  ): boolean {
    let resultState = false;
    // FIXME временная проверка на наличие id, так как оно точно обязательное,
    // но временно в апи указано как необязательное
    if (this.isCreatedInfrigement(infringement?.id || '')) {
      // проверка что хотя бы одно поле не пустое
      // TODO так же, как и блок проверки изменений - changedInfringementIds стоит переписать,
      // чтобы объеденить две логики в одну
      resultState = !!Object.entries(infringement).filter(
        (entry) =>
          !isNull(entry[1]) &&
          entry[1] !== '' &&
          entry[0] !== 'id' &&
          entry[0] !== 'typeDocument' &&
          entry[0] !== 'recruitId'
      ).length;
    }
    return resultState;
  }

  // Функция создания пустого блока, createdCount нужен чтобы был уникальный id
  public createInfringement(): void {
    this.$emit('onBeforeAction', 'adm_add');
    this.infringements.push({
      preTrialNumber: '',
      createInfringementDate: '',
      organization: '',
      trialNumber: '',
      decisionDate: '',
      court: '',
      vkName: '',
      cause: '',
      dateApplication: '',
      punishment: '',
      description: '',
      reasonForAddEntry: '',
      recruitId: this.recruitId,
      id: `created_${this.createdCount}`,
      typeDocument: InfringementAdministrativeTypeDocumentEnum.Administrative,
    });
    this.createdCount++;
  }

  // Функция удаления блока
  public deleteInfringement(): void {
    // сохранение id
    const infringementId = this.deleteInfringementId;
    this.loadingIds.push(infringementId);

    // закрываем модалку и даем пользователю дальше пользоваться сайтом пока происходит удаление
    this.deleteInfringementDialog = false;
    this.deleteInfringementId = '';

    // процесс удаления
    if (this.isCreatedInfrigement(infringementId)) {
      const infringementIndex = this.getInfringementIndex(infringementId);
      if (!isNull(infringementIndex)) {
        this.infringements.splice(infringementIndex, 1);
      }
      this.loadingIds = this.loadingIds.filter(
        (loadingId) => loadingId !== infringementId
      );
    } else {
      this.DecisionDocumentServiceApi.deleteRecruitInfringement(infringementId)
        .then(() => {
          const infringementIndex = this.getInfringementIndex(infringementId);
          if (!isNull(infringementIndex)) {
            this.infringements.splice(infringementIndex, 1);
          }
        })
        .catch(() => {
          notifyError('Не удалось удалить');
        })
        .finally(() => {
          this.loadingIds = this.loadingIds.filter(
            (loadingId) => loadingId !== infringementId
          );
        });
    }
  }

  // Функция сохранения блока
  public async saveInfringement(
    infringement: InfringementAdministrative
  ): Promise<void> {
    this.$emit('onBeforeAction', 'adm_save');
    // FIXME если нет id то подсовываем пустоты чтобы запустить лоадер, но такого не может быть
    // сейчас ошибка так как не обязательное поле
    this.loadingIds.push(infringement.id || '');
    const infringementToSend: Record<string, string> = {};

    // подготовка данных
    // при отсутствии данных поле на бэк не отправляется, так же при создании поле id не должно отправляться
    Object.entries(infringement).forEach(
      ([infringementKey, infringementValue]) => {
        if (
          infringementValue &&
          !(
            infringementKey === 'id' &&
            this.isCreatedInfrigement(infringementValue)
          )
        ) {
          infringementToSend[infringementKey] = infringementValue;
        }
      }
    );

    // FIXME если нет id то подсовываем created чтобы пошло создание, но отсутствовать id не может
    // сейчас ошибка так как не обязательное поле
    if (this.isCreatedInfrigement(infringement.id || 'created')) {
      const oldInfringementId = infringement.id;
      try {
        const {
          data,
        } = await this.DecisionDocumentServiceApi.createRecruitInfringement(
          infringementToSend
        );

        // обновляем id, createDate в корректный с бэка
        infringement.id = data.id;
        infringement.createDate = data.createDate;
        infringement.recordNumber = data.recordNumber;
        notifySuccess('Данные сохранены');
        this.$emit('save');
      } catch (error) {
        notifyError(
          'Не удалось сохранить данные, проверьте корректность ввода данных'
        );
      } finally {
        this.loadingIds = this.loadingIds.filter(
          (loadingId) => loadingId !== oldInfringementId
        );
      }

      // обновляем данные в родители, чтобы корректно работало сравнивание
      this._infringementsAdministrative.push(
        JSON.parse(JSON.stringify(infringement))
      );
    } else {
      if (infringement.id) this.editor[infringement.id] = false;
      try {
        const {
          data,
        } = await this.DecisionDocumentServiceApi.updateRecruitInfringement(
          infringementToSend
        );

        // ищем и обновляем объект из списка в родителе
        this._infringementsAdministrative = this._infringementsAdministrative.map(
          (infringementAdministrative) => {
            if (infringementAdministrative.id === data.id) {
              return JSON.parse(JSON.stringify(data));
            } else {
              return infringementAdministrative;
            }
          }
        );
        notifySuccess('Данные сохранены');
        this.$emit('save');
      } catch (error) {
        notifyError(
          'Не удалось сохранить данные, проверьте корректность ввода данных'
        );
      } finally {
        this.loadingIds = this.loadingIds.filter(
          (loadingId) => loadingId !== infringement.id
        );
      }
    }
  }

  // FIXME список для q-select - сделать отдельный компонент
  get options(): { label: string; value: string }[] {
    return getDictionaryFormatedToOptions(
      this.$store.getters['ervuDictionaries/typeAdministrativePunishment'] || {}
    );
  }

  get causes(): { code: string; value: string }[] {
    const causesList = this.$store.getters[
      'ervuDictionaries/administrativePunishmentCausesList'
    ];
    return causesList?.documents ?? causesList ?? [];
  }

  // computed отслеживающий локальный и родительский массив данных и сравнивающий их
  // бежим по 2 массивам сравнивая их id и существование id - временная проверка пока нет строгого определения наличия id
  // сравниваем два объекта и если они не равны то фиксируем его id
  // TODO переписать в более человеческую-оптимизированную функцию
  get changedInfringementIds(): string[] {
    const changedIds: string[] = [];
    this._infringementsAdministrative.forEach((infringementAdministrative) => {
      const entriesInfringementAdministrative: string[] = [];
      // проверка на пустое поле
      // eslint-disable-next-line array-callback-return
      Object.entries(infringementAdministrative).filter((entries) => {
        if (!isNull(entries[1]) && entries[1] !== '') {
          entriesInfringementAdministrative.push(JSON.stringify(entries));
        }
      });

      this.infringements.forEach((infringement) => {
        if (
          infringementAdministrative.id &&
          infringementAdministrative.id === infringement.id
        ) {
          const entriesInfringement: string[] = [];
          // проверка на пустое поле
          // eslint-disable-next-line array-callback-return
          Object.entries(infringement).filter((entries) => {
            if (!isNull(entries[1]) && entries[1] !== '') {
              entriesInfringement.push(JSON.stringify(entries));
            }
          });

          const difference = entriesInfringement
            .filter((x) => !entriesInfringementAdministrative.includes(x))
            .concat(
              entriesInfringementAdministrative.filter(
                (x) => !entriesInfringement.includes(x)
              )
            );

          if (difference.length) {
            changedIds.push(infringement.id);
          }
        }
      });
    });
    return changedIds;
  }
}
</script>

<style lang="scss" scoped>
.InfringementAdministrativeBlock {
  padding: 12px 16px;
  margin-bottom: 16px;

  &-Top {
    display: flex;
    justify-content: space-between;
    padding-bottom: 12px;
    margin-bottom: 20px;
    border-bottom: 1px solid rgba(1, 72, 116, 0.11);
  }

  &-Header {
    font-family: 'Golos UI', 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);
  }
  &-ContentBox {
    padding: 20px 0;
  }

  &-RecordHeader {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    gap: 12px;
    animation: 0.2s ease-out appear;
  }
  &-RecordCount {
    color: rgba(2, 12, 18, 0.58);
    font-family: 'Golos UI', sans-serif;
    font-size: 10px;
    font-style: normal;
    font-weight: 500;
    line-height: 12px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
  }
  &-BlockButtons {
    display: flex;
    gap: 8px;
  }
  &-EditButton {
    color: #107abc;
    font-family: 'Golos UI', sans-serif;
    font-style: normal;
    font-weight: 500;
    font-size: 14px;
    line-height: 16px;
    border-radius: 24px;
    .pgs-icon {
      font-size: 1.715em !important;
    }
    i:has(+ span) {
      margin-right: 6px;
    }
  }
}
@keyframes appear {
  from {
    max-height: 0;
  }
  to {
    max-height: 150px;
  }
}

.input-block {
  min-height: 84px;
  em {
    font-family: 'Golos Text', sans-serif;
    font-style: normal;
    font-weight: 600;
    font-size: 12px;
    line-height: 16px;
    color: rgba(2, 12, 18, 0.58);
    margin-bottom: 4px;
  }

  ::v-deep .q-field {
    width: 100%;
  }

  ::v-deep .q-select {
    .q-field__bottom {
      bottom: -10px;
    }
  }

  ::v-deep .q-field__control {
    input {
      padding: 0 8px;
    }
    &:focus-within {
      background-color: white;
      box-shadow: inset 0 0 2px rgba(15, 104, 160, 0.87);
      border-color: #1487cf;
    }
    padding: 0 8px !important;
    background-color: #e5edf5;
    border-radius: 4px 4px 0 0;
    background-blend-mode: multiply;
    border-bottom: 1px solid rgba(1, 72, 116, 0.18);
  }
}

.r-select-label {
  font-family: 'Golos Text', sans-serif;
  font-style: normal;
  font-weight: 600;
  font-size: 12px;
  line-height: 16px;
  color: rgba(2, 12, 18, 0.58);
  margin-bottom: 4px;
}
.r-select {
  background: #e5edf5;
  border-radius: 4px 4px 0 0;
  background-blend-mode: multiply;
  border: none;
  border-bottom: 1px solid rgba(1, 72, 116, 0.18);
  padding: 8px 0;
  box-sizing: border-box;
  transition: 0.1s;
  height: 40px;

  &-text {
    max-width: 83%;
  }
}
.InfringementDialog {
  &-AttentionContent {
    * {
      margin: 0;
      padding: 0;
    }
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 30px 40px;
    &__Icon {
      color: #1491cf;
      font-size: 40px;
      margin-bottom: 10px;
    }
    &__Header {
      font-family: 'Golos Text', sans-serif;
      font-style: normal;
      font-weight: 500;
      font-size: 20px;
      line-height: 24px;
      text-align: center;
      letter-spacing: -0.015em;
      color: #015e98;
      margin-bottom: 24px;
    }
  }
  &-AttentionButtons {
    display: flex;
    justify-content: space-between;
    padding: 12px 20px;
  }
  &-AttentionButton {
    padding: 0 10px;
  }
}
</style>
