<template>
  <div class="redesigned-search-bar q-gutter-y-md">
    <div class="row no-wrap q-mb-sm">
      <div class="col-shrink radio-cell q-px-sm" :class="{ 'selected': onlyMyCast }" style="border-right: 0;">
          <q-radio dense v-model="onlyMyCast" :val="false" label="Все" :disabled="loading" />
      </div>
      <div class="col-shrink radio-cell q-px-sm" :class="{ 'selected': !onlyMyCast }">
        <q-radio dense v-model="onlyMyCast" :val="true" label="Мои" :disabled="loading" />
      </div>
      <div class="col-grow q-pl-sm flex q-gutter-x-xs">
        <q-input
          v-model="filter.search"
          dense
          outlined
          placeholder="Номер заявления, Название услуги, заявитель"
          @keydown.enter="addRouteAndSearch"
          @keydown.esc="filter.search = ''"
          style="flex:1"
        >
          <template #prepend>
            <q-icon name="pgs-search" />
          </template>
        </q-input>
        <q-btn icon="pgs-filter" class="filter-icon" @click="toggleFilter">
          <q-badge
            color="#015E98"
            floating
            v-if="!!filterCount"
          >
            {{ filterCount }}
          </q-badge>
        </q-btn>
      </div>
    </div>

    <q-slide-transition>
      <q-card bordered flat v-show="filter.visible" style="margin-top: 0">
        <q-card-section class="q-pa-xs">
          <div class="row q-pt-md">
            <div class="col-12 col-sm-6 col-md-4 col-lg-3 q-pa-xs">
              <multi-select
                :options.sync="statusOptions"
                :status.sync="filter.status"
                @input="handleStatusChange"
              />
            </div>
            <div class="col-12 col-sm-6 col-md-4 col-lg-3 q-pa-xs">
              <div class="row">
                <div class="col-9 text-right q-pr-sm golos-label" style="display: inline-grid; align-items: center;">
                  Истекает срок оказания (дни)
                </div>
                <div class="col-3">
                  <q-input
                    v-model="filter.term"
                    max="359"
                    type="number"
                    outlined
                    dense
                    class="q-pa-none"
                  />
                </div>
              </div>
            </div>
            <div class="col-12 col-sm-6 col-md-4 col-lg-3 q-pa-xs">
              <div class="row">
                <div class="col-3 text-right q-pr-sm golos-label" style="display: inline-grid; align-items: center;">
                  Дата подачи
                </div>
                <div class="col-9">
                  <date-application-input
                    :from.sync="filter.from"
                    :to.sync="filter.to"
                  />
                </div>
              </div>
            </div>
            <div class="col-12 col-sm-6 col-lg-3 q-pa-xs">
              <div class="row">
                <div class="col-3 text-right q-pr-sm golos-label" style="display: inline-grid; align-items: center;">
                  Дата принятия решения
                </div>
                <div class="col-9">
                  <date-application-input
                    :from.sync="filter.fromDecision"
                    :to.sync="filter.toDecision"
                  />
                </div>
              </div>
            </div>
            <div class="col-12 col-md-6 q-pa-xs" v-if="!isObserver">
              <div class="row q-gutter-x-md items-center">
                <q-select
                  ref="employee"
                  v-model="filter.employee"
                  outlined
                  multiple
                  dense
                  borderless
                  color="primary"
                  options-dense
                  label="Сотрудник"
                  style="min-width: 283px;"
                  :options="employeeFilterOptions"
                >
                  <template #before-options>
                    <q-input
                      outlined
                      dense
                      placeholder="Найти пользователей"
                      :value="employeeSearch"
                      @input="getEmployees"
                      ref="employeeSearch"
                    >
                      <template #append>
                        <q-icon name="search" size="20px" />
                      </template>
                    </q-input>
                  </template>
                  <template #option="scope">
                    <q-separator inset v-if="scope.opt.separator" />
                    <q-item
                      v-bind="scope.itemProps"
                      v-else
                      v-on="scope.itemEvents"
                    >
                      <q-item-section side>
                        <q-checkbox
                          :value="scope.selected"
                          @input="scope.itemEvents.click"
                        />
                      </q-item-section>
                      <q-item-section>
                        <q-item-label v-html="scope.opt.label" />
                      </q-item-section>
                    </q-item>
                  </template>
                </q-select>
              </div>
            </div>
            <div class="col-12 col-md-6 q-pa-xs" v-if="$AC.haveScope(USER_SCOPES.RF_OBSERVER_SCOPE)" style="width: 100%">
              <RedesignedAutocompleteSelect
                :model.sync="subjects"
                :async-getter="getSubjects"
                :is-multiple="true"
                :optionalSearch="true"
                field-label="Субъекты"
                label-value="name"
                @value="handleSubjectsChange"
                dense
              />
            </div>
            <div class="col-12 col-md-6 q-pa-xs" v-if="isObserver">
              <RedesignedAutocompleteSelect
                :model.sync="organizations"
                :async-getter="getOrganizations"
                :is-multiple="true"
                field-label="Организация"
                label-value="fullname"
                @value="handleOrgValues"
                dense
              />
            </div>
            <div class="col-12 col-md-6 q-pa-xs" v-if="isDepartmentFieldVisible">
              <RedesignedAutocompleteSelect
                :model.sync="departments"
                :async-getter="getDepartments"
                :is-multiple="true"
                field-label="Подразделение"
                label-value="fullname"
                @value="handleDepartmentValues"
                dense
              />
            </div>
            <div class="col-12 col-md-6 q-pa-xs">
              <div>
                <RedesignedAutocompleteSelect
                  :model.sync="services"
                  :async-getter="getService"
                  :is-multiple="false"
                  field-label="Наименование услуги"
                  label-value="shortName"
                  @value="handleServicesValues"
                  dense
                />
              </div>
            </div>
            <div class="col-12 col-md-6 q-pa-xs">
              <div>
                <q-select
                  v-model="filter.goals"
                  :options="goalsList"
                  outlined
                  dropdown-icon="pgs-shevron-down-off"
                  stretch
                  flat
                  :disable="!services.selected"
                  borderless
                  multiple
                  label="Цель услуги"
                  emit-value
                  map-options
                  dense
                >
                  <template #option="scope">
                    <q-item v-bind="scope.itemProps" v-on="scope.itemEvents">
                      <q-item-section side>
                        <q-checkbox
                          :value="scope.selected"
                          @input="scope.itemEvents.click"
                        />
                      </q-item-section>
                      <q-item-section>
                        <q-item-label v-html="scope.opt.label" />
                      </q-item-section>
                    </q-item>
                  </template>
                </q-select>
              </div>
            </div>
          </div>
          <div class="row q-pa-xs justify-end q-gutter-sm">
            <!-- FIXME: Скрыта кнопка до принятия решения о её функциональности -->
<!--            <q-btn
              v-if="selectedServiceFound"
              color="secondary"
              style="width: 100%"
              :label="$t('statements.Common.button.editColumns')"
              :loading="loading"
              @click="openMappingDialog"
            />-->
            <q-btn
              icon="pgs-import"
              v-if="isRefugeeCompensation"
              @click="saveRefugeeSubjects"
              class="float-right redesigned-action-btn text-capitalize"
              :loading="loading"
              :label="$t('statements.Common.button.saveRefugeeSubjects')"
            />
            <q-btn
              icon="pgs-import"
              v-if="isSaveDataAllowed && isVisibleSaveDataButton"
              class="float-right redesigned-outline-btn text-capitalize"
              :label="$t('statements.Common.button.saveRegistry')"
              :loading="loading"
              :disable="!isFetchedAfterFilterChanged"
              @click="saveData"
            >
              <q-tooltip v-if="!isFetchedAfterFilterChanged">
                {{ noFetchAfterFilterChangedTooltipLabel }}
              </q-tooltip>
            </q-btn>
            <q-btn
              icon="pgs-search"
              class="float-right redesigned-action-btn text-capitalize"
              :label="$t('statements.Common.button.search')"
              :loading="loading"
              :disable="!searchButtonActive"
              @click="addRouteAndSearch"
            />
          </div>
        </q-card-section>
        <q-btn
          dense
          flat
          class="absolute-top-right close-button q-pr-xs"
          icon="close"
          size="10px"
          @click="ResetFilter"
          label="сбросить все"
        />
      </q-card>
    </q-slide-transition>
    <q-dialog v-model="isMappingDialogOpen">
      <ServiceMapping
        :serviceId="filter.services"
        :columns="columns"
        :defaultColumns="defaultColumns"
        :mapping="selectedServiceMapping"
        @clear-statements-storage="$emit('clear-statements-storage')"
        @change-columns="changeColumns"
      />
    </q-dialog>
  </div>
</template>

<script lang="ts">
import XDateRange from '@components/statements/components/x-date-range.vue';
import DateApplicationInput from '@components/statements/components/date-application-input.vue';
import MultiSelect from '@components/statements/components/multi-select.vue';
import { Component, Prop, PropSync, Vue, Watch } from 'vue-property-decorator';
import { oidcClientComputed } from '@state/modules/oidc-client';
import { date } from 'quasar';
import { Columns, StatementsSearchQuery } from '@components/statements';
import { SearchBarRefs } from '@/components/common/base-search-bar';
import {
  convertDateFormatWithoutLocale,
  convertDateFromBackendFormat,
} from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { ServiceApi } from '@openapi/registry-service';
import {
  DomainApi,
  OrganizationApi,
  Organization,
  RegionApi,
  Region,
} from '@openapi/idm';
import { USER_SCOPES } from '@constants/entities/Scope';
import { omit, map, isEqual } from 'lodash';
import { notifyError } from '@utils/notify';
import ServiceMapping from './service-mapping.vue';

const REG_ORDER_ID = /(i-)?\d+$/;
const domainApi = new DomainApi();
const organizationApi = new OrganizationApi();
const regionApi = new RegionApi();

class AccountsCreateInjection extends Vue.extend({
  computed: {
    ...oidcClientComputed,
  },
}) {}

export interface Option {
  label?: string;
  value?: string;
  option?: string;
}

export interface EmployeeOption extends Option {
  checked?: boolean;
  separator?: boolean;
}

@Component({
  components: {
    XDateRange,
    MultiSelect,
    DateApplicationInput,
    ServiceMapping,
  },
})
export default class StatementsSearchBar extends AccountsCreateInjection {
  $refs!: SearchBarRefs;

  USER_SCOPES = USER_SCOPES;

  @Prop({ required: true })
    query!: StatementsSearchQuery;

  @Prop() loading: boolean;
  @PropSync('onlyMy') onlyMyCast?: boolean;
  @Prop() isRefugeeCompensation: boolean;
  @Prop() columns: Columns;
  @Prop() defaultColumns: Function;
  @Prop() selectedServiceMapping: any;
  @Prop() selectedServiceFound: boolean;
  @Prop() isFetchedAfterFilterChanged: boolean;

  protected filter: Filter = {
    search: '',
    status: [],
    organizations: [],
    departments: [],
    services: '',
    goals: [],
    employee: [],
    subjects: [],
    from: '',
    to: '',
    fromDecision: '',
    toDecision: '',
    visible: this.$store.getters['statements/filterPaneOpen'],
    term: null,
  };
  
  private searchButtonActive: boolean = false
  private isMappingDialogOpen = false;

  private get userDomain() {
    return this.accountsCard['user-domain'];
  }

  /**
   * Для решения проблемы когда страницу поиска открывают по закладке с предустановленными фильтрами в query-строке
   * выходило так что словарик со статусами не успевал загружаться к тому моменту когда разбираются значения из query-строки.
   * Данный код позволяет, без больших переделок, методу async created() дождаться вызова async parseQueryParams()
   * который в свою очередь дожидается загрузки статусов через (await this.statusDictionaryPromise)
   */
  private statusResolver: Function = () => {};
  private statusDictionaryPromise = new Promise(resolve => {
    const statusDictionary = this.gpzu2ApplicationStatusList;
    this.statusResolver = resolve;
    if (statusDictionary) {
      this.statusResolver(statusDictionary);
    }
  });
  private get gpzu2ApplicationStatusList() {
    return this.$store.getters['registryService/referenceBooks/referenceBooks'].gpzu_2_application_status;
  }
  @Watch('gpzu2ApplicationStatusList')
  statusListHandler(newStatuses: any, oldStatuses: any): void {
    if (Object.entries(newStatuses).length > 0) {
      this.statusResolver(newStatuses);
    }
  }

  @Watch('filter', { deep: true })
  filterHandler(filter: Filter): void {
    this.setRoute(filter);
  }

  @Watch('filter.visible')
  filterToggleWatcher(isOpen: boolean): void {
    this.$store.commit('statements/filterPaneToggle', isOpen);
  }

  @Watch('filterCount', { immediate: true })
  isSearchButtonActive(filterCount: number): void {
    const hasQuery: boolean = Object.keys(this.$route.query).length !== 0
    if (hasQuery) {
      this.searchButtonActive = true 
      return
    }
    if (filterCount !== 0) {
      this.searchButtonActive = true 
    } else {
      this.searchButtonActive = false
    }
  }

  private get isObserver(): boolean {
    return this.$AC.haveOneOfScope([
      USER_SCOPES.RF_OBSERVER_SCOPE,
      USER_SCOPES.ALL_ORG_ADMIN,
      USER_SCOPES.REGION_OBSERVER,
      USER_SCOPES.CLOUD_ADMIN,
      USER_SCOPES.OBSERVER,
    ]);
  }

  private get isDepartmentFieldVisible(): boolean {
    const isUserAttachedToDepartment = this.userDomain?.schema === 'Department';

    return (
      this.isObserverOrAdmin ||
      (this.isSpecialistOrRegistrar && !isUserAttachedToDepartment)
    );
  }

  private get isObserverOrAdmin(): boolean {
    return [
      USER_SCOPES.REGION_OBSERVER,
      USER_SCOPES.ALL_ORG_ADMIN,
      USER_SCOPES.CLOUD_ADMIN,
      USER_SCOPES.RF_OBSERVER_SCOPE,
    ].some((scope) => this.$AC.haveScope(scope));
  }

  private get isSpecialistOrRegistrar(): boolean {
    return [
      USER_SCOPES.SPECIALIST,
      USER_SCOPES.ALLOCATE_PERMISSION_REG,
      USER_SCOPES.ALLOCATE_PERMISSION_SPEC,
      USER_SCOPES.REGISTRAR,
      USER_SCOPES.SIGNED_SPECIALIST,
    ].some((scope) => this.$AC.haveScope(scope));
  }

  private get isVisibleSaveDataButton(): boolean {
    /**
     * Запрет для
     * USER_SCOPES.REGION_OBSERVER,
     * USER_SCOPES.RESULT_REGISTRY,
     * USER_SCOPES.RF_OBSERVER_SCOPE,
     * USER_SCOPES.IMPORT_UFK,
     * USER_SCOPES.UNLOADING_CANCEL,
     * USER_SCOPES.PACK_SIGNING_SCOPE,
     * USER_SCOPES.REGION_ADMIN,
     * USER_SCOPES.ALL_ORG_ADMIN,
     * USER_SCOPES.OBSERVER,
     */
    return (
      this.$AC.haveOneOfScope([
        USER_SCOPES.CLOUD_ADMIN,
        // USER_SCOPES.REGION_ADMIN,
        USER_SCOPES.ORGANIZATION_ADMIN,
        USER_SCOPES.SPECIALIST,
        USER_SCOPES.ALLOCATE_PERMISSION_SPEC,
        USER_SCOPES.ALLOCATE_PERMISSION_REG,
        USER_SCOPES.REGISTRAR,
        // USER_SCOPES.ALL_ORG_ADMIN,
        // USER_SCOPES.OBSERVER,
      ]) && !this.$AC.haveOneOfScope([
        USER_SCOPES.REGION_OBSERVER,
        USER_SCOPES.RESULT_REGISTRY,
        USER_SCOPES.RF_OBSERVER_SCOPE,
        USER_SCOPES.IMPORT_UFK,
        USER_SCOPES.UNLOADING_CANCEL,
        USER_SCOPES.PACK_SIGNING_SCOPE,
        USER_SCOPES.REGION_ADMIN,
        USER_SCOPES.ALL_ORG_ADMIN,
        USER_SCOPES.OBSERVER,
      ])
    )
  }

  handleStatusChange(status: [] ): void {
    this.filter.status = status
  }

  handleSubjectsChange(val: any[] = []): void {
    const list = val || [];
    this.filter.subjects = list.map((item: any) => {
      return item.id || '';
    });
  }

  protected get selectedDate(): string {
    const out = [
      convertDateFormatWithoutLocale(this.filter.from || this.filter.to),
    ];

    return out.length ? out.join(' - ') : '';
  }

  protected get selectedStatus(): string {
    return this.filter.status.length
      ? this.filter.status.map((e) => e.label).join(', ')
      : '';
  }

  protected get selectedEmployee(): string {
    return this.employeeFilterChecked.length
      ? this.employeeFilterChecked.map((e) => e.label).join(', ')
      : '';
  }

  // todo use as method before parsing query
  protected get statusOptions(): Option[] {
    // Срочный hotfix PGSRUSH-5160 - Изменить порядок статусов на странице поиска заявлений
    // Решили хардкодом проставить желаемый разпорядок статусов в выпадающем списке
    function orderByPriority(statuses: Array<{ label: string, value: string }>) : Array<{ label: string, value: string }> {
      const priorities: { [key : string]: number } = {
        "Заявление получено ведомством": 1,
        "Заявление зарегистрировано": 2,
        "Заявление принято к рассмотрению": 3,
        "Назначен исполнитель": 4,
        "Принятие решения о приеме документов": 5,
        "Запрос сведений по СМЭВ": 6,
        "Запрос сведений СМЭВ (повторный)": 7,
        "Сведения по СМЭВ получены": 8,
        "Сведения по СМЭВ получены не полностью": 9,
        "Сведения по СМЭВ не получены": 10,
        "Рассмотрение документов": 11,
        "Заявление передано на рассмотрение": 12,
        "Приостановлено": 13,
        "Формирование решения": 14,
        "Подготовлен проект решения": 15,
        "Проект решения отправлен на подписание": 16,
        "Отказано в приеме документов": 17,
        "Услуга оказана": 18,
        "Отказано в предоставлении услуги": 19,
        "Заявление отменено": 20,
        "Заявителю направлено приглашение": 21,
        "Заявитель пришёл на приём": 22,
        "Заявитель не пришёл на приём": 23,
        "Заявитель не пришёл на приём (авт.)": 24,
        "Запрос дополнительной информации": 25,
        "Запрос дополнительной информации(приостановка)": 26,
        "Получены дозапрошенные документы": 27,
        "Срок приостановки заявления истек": 28,
        "Заявление требует исправления": 29,
        "Исправления по заявлению получены": 30,
        "Начисление сформировано": 31,
        "Начисление зарегистрировано": 32,
        "Ожидание сведений об оплате": 33,
        "Сведения об оплате аннулированы": 34,
        "Сведения об оплате получены": 35,
        "Невозможно сформировать начисление": 36,
        "Ошибка получения сведений об оплате": 37,
        "Отправлено приглашение на приём": 38,
        "Ожидание записи заявителя": 39,
        "Ошибка отправки ответа на ЕПГУ": 40,
        "Документы отправлены": 41,
        "Приглашение на приём принято": 42,
        "Срок приглашения на приём истек": 43,
        "Документы не отправлены": 44,
        "Ошибка отправки документов": 45,
        "Заявление не было создано в ЕЛК": 46,
        "Заявление было создано в ЕЛК": 47,
        "Запрос дополнительной информации по заявке": 48,
        "Получена дополнительная информация": 49,
        "Назначены социальные услуги": 50,
        "Согласование заявления завершено": 51,
        "Обработка заявления завершена оператором": 52,
        "Заявка взята в работу": 53,
        "Социальная услуга предоставлена": 54,
        "Социальная услуга не предоставлена": 55,
        "Заявления социальных услуг обработаны": 56,
        "Заявка на социальную услугу создана": 57,
        "Необходимо предоставить дополнительную информацию по соц.услуге": 58,
        "Запись в реестр сформирована": 59,
        "Запись в реестре обновлена": 60,
        "Проактивное уведомление отправлено": 61,
        "Проактивное уведомление не требуется": 62,
        "Заявление не прошло проверку": 63,
        "Приостановлено(14 статус)": 64,
        "Приостановлено(15 статус)": 65,
        "Получена новая информация по заявлению": 66,
        "Промежуточные результаты по заявлению": 67,
      }
      const statusListWithPriorities = statuses.map((s, i) => {
        const priority = priorities[s.label] || 100 + i;
        return { status: s, priority };
      });
      statusListWithPriorities.sort((a, b) => a.priority - b.priority);
      return statusListWithPriorities.map(i => i.status);
    }
    const temp = map(
      this.$store.getters['registryService/referenceBooks/referenceBooks']
        .gpzu_2_application_status || {},
      (label, value) => ({ value, label: label.description })
    ).filter((e) => e.value !== 'Draft'); // todo
    const distinctStatuses = new Map<string, string>();
    for (const x of temp) {
      if (distinctStatuses.has(x.label)) {
        distinctStatuses.set(
          x.label,
          `${distinctStatuses.get(x.label)},${x.value}`
        );
      } else {
        distinctStatuses.set(x.label, x.value);
      }
    }
    const statuses = Array.from(distinctStatuses, ([label, value]) => ({
      value,
      label,
    }));
    return orderByPriority(statuses);
  }

  protected get userid() {
    // @ts-ignore
    return this.currentUser.sub;
  }

  protected toggleFilter(): void {
    this.filter.visible = !this.filter.visible;
  }

  protected saveData(): void {
    if (this.selectedServiceFound) {
      this.$emit('download-export-mapping');
    } else {
      this.$emit('save-data');
    }
  }

  protected saveRefugeeSubjects(): void {
    this.$emit('save-refugee-subjects');
  }

  protected ResetFilter(): void {
    this.filter.search = '';
    this.filter.status = [];
    this.filter.employee = [];
    this.filter.goals = [];
    this.filter.subjects = [];
    this.filter.organizations = [];
    this.filter.departments = [];
    this.filter.from = '';
    this.filter.to = '';
    this.filter.fromDecision = '';
    this.filter.toDecision = '';
    this.filter.term = null;
    this.filter.visible = false;
    this.filter.services = '';

    this.services.selected = null
    this.subjects.selected = []
    this.organizations.selected = []
    this.departments.selected = []
    this.searchButtonActive = false
    // FIXME Это временное решение.
    if (!this.$AC.haveScope(USER_SCOPES.CLOUD_ADMIN)) {
      this.startSearch();
    } else {
      this.$emit('reset-filter');
    }

    this.$router.push({ path: '/statements' });
  }

  protected resetPagination() {
    this.$emit('pagination');
    return omit(this.$route.query, 'page');
  }

  protected get filterCount(): number {
    let result = 0;

    if (this.filter.search.length) result++;

    if (this.filter.status.length) result++;

    if (this.filter.subjects?.length) result++;

    if (this.filter.services?.length) result++;

    if (this.filter.employee.length) result++;

    if (this.filter.term) result++;

    if (this.filter.from || this.filter.to) result++;

    if (this.filter.fromDecision || this.filter.toDecision) result++;

    if (this.filter.organizations && this.filter.organizations.length) result++;

    return result;
  }

  protected addRouteAndSearch(): void {
    this.startSearch(true); // resetPagination = true - что бы новый поиск с первой страницы
  }
  
  protected setRoute(filter: Filter): void {
    const params: any = {}
    const {
      status,
      from,
      to,
      fromDecision,
      toDecision,
      employee,
      organizations,
      departments,
      services,
      goals,
      subjects,
      term,
    } = filter;
    let { search } = filter;
    if (search) {
      params.search = search
    } else {
      params.search = undefined;
    }
    if (status.length) {
      let statusesArr: Array<string> = [];
      for (const val of status) {
        if (val.value && val.value.includes(',')) {
          const temp = val.value.split(',');
          statusesArr = statusesArr.concat(temp);
        } else {
          statusesArr.push(val.value ?? '');
        }
      }
      const preparedValue = statusesArr.join(',') || undefined;
      params.status = preparedValue;
    } else {
      params.status = undefined;
    }

    if (employee.length) {
      const result = employee.map((el) => el.value);
      const employees = result.join() || undefined;
      params.employees = employees
    } else {
      params.employees = undefined;
    }

    if (services?.length)
      params.services = services;
    else
      params.services = undefined;
    if (goals?.length) {
      params.goals = goals.join() || undefined;
    } else {
      params.goals = undefined;
    }
    if (subjects?.length) {
      params.subjects = subjects.join() || undefined;
    } else {
      params.subjects = undefined;
    }
    if (from || to) {
      const result = ['', ''];
      if (from != null && from !== '') {
        result[0] = convertDateFormatWithoutLocale(from);
      }
      if (to != null && to !== '') {
        result[1] = convertDateFormatWithoutLocale(to);
      }
      params.daterange = result.join();
    } else {
      params.daterange = undefined;
    }
    if (fromDecision || toDecision) {
      const result = ['', ''];
      if (fromDecision != null && fromDecision !== '') {
        result[0] = convertDateFormatWithoutLocale(fromDecision);
      }
      if (toDecision != null && toDecision !== '') {
        result[1] = convertDateFormatWithoutLocale(toDecision);
      }
      params.daterangeDecision = result.join();
    } else {
      params.daterangeDecision = undefined;
    }
    params.term = term?.toString() || undefined;
    if (organizations?.length) { 
      params.organizations = organizations.join(',') || undefined;
    } else {
      params.organizations = undefined;
    }
    if (departments?.length) {
      params.departments = departments.join(',') || undefined;
    } else {
      params.departments = undefined;
    }
    this.patchQueryParams(params);
  }

  private patchQueryParams(params: any) {
    // Используем Object.assign() что бы избежать потерю query-параметров, например параметры пагинации.
    const updatedQueryParams = (Object.assign({}, this.$route.query, params));
    if (!isEqual(this.$route.query, updatedQueryParams)) {
      // избегаем некретичной, но всётаки ошибки - NavigationDuplicated
      this.$router.push({ query : { ...updatedQueryParams } });
    }
  }

  protected startSearch(resetPagination = false): void {
    const fulltext: string[] = [];
    const query: string[] = [];
    const params: any = {}
    const {
      status,
      from,
      to,
      fromDecision,
      toDecision,
      employee,
      organizations,
      departments,
      services,
      goals,
      subjects,
      term,
    } = this.filter;
    let { search } = this.filter;

    if (search) {
      if (REG_ORDER_ID.test(search)) {
        query.push(`common.orderId=like=${search}*`);
      } else {
        // Проверяем, что не только числа в строке ввели иначе ищем по common.orderId
        search = search.replace(/\\/g, '');
        search = search.replace(/'/g, "\\'");
        search = search.replace(/"/g, '\\"');

        fulltext.push(`system.applicant.fullName=searchprefix="${search}"`);
        fulltext.push(`system.applicant.fullName=search="${search}"`);
        fulltext.push(`system.applicant.fullName=searchany="${search}"`);
        fulltext.push(`system.shortName=searchprefix="${search}"`);
        fulltext.push(`system.shortName=search="${search}"`);
        fulltext.push(`system.shortName=searchany="${search}"`);
      }
    }
    if (status.length) {
      let statusesArr: Array<string> = [];
      for (const val of status) {
        if (val.value && val.value.includes(',')) {
          const temp = val.value.split(',');
          statusesArr = statusesArr.concat(temp);
        } else {
          statusesArr.push(val.value ?? '');
        }
      }
      // TODO : Проверить для всех фильтров
      query.push(
        `(${statusesArr
          .map((e) => `system.status.internal==${e}`)
          .join(' or ')})`
      );
    }
    if (employee.length) {
      query.push(
        employee.map((e) => `common.assignee.${e.option}==${e.value}`).join(' or ')
      );
      const result = employee.map((el) => el.value);
    }
    if (departments?.length) {
      query.push(
        departments.map((e) => `system.currentDivision.id==${e}`).join(' or ')
      );
    } else if (organizations?.length) {
      query.push(
        organizations.map((e) => `system.department.id==${e}`).join(' or ')
      );
    }

    if (services?.length) {
      query.push(`system.serviceId==${services}`);
    }
    if (goals?.length) {
      const goalsQuery = goals
        .map((value: string) => `statement.goal==${value}`)
        .join(' or ');
      query.push(`(${goalsQuery})`);
    }
    if (subjects?.length) {
      const subjectsQuery = subjects
        .map((value: string) => `system.regionId==${value}`)
        .join(' or ');
      query.push(`(${subjectsQuery})`);
    }
    if (from || to) {
      if (from) {
        query.push(
          `system.startDateIso>=${convertDateFormatWithoutLocale(
            from,
            DateFormat.invertedDate
          )}`
        );
      }
      if (to) {
        const toPlus = new Date(to as any);

        toPlus.setDate(toPlus.getDate() + 1);
        query.push(
          `system.startDateIso<${convertDateFormatWithoutLocale(
            toPlus,
            DateFormat.invertedDate
          )}`
        );
      }
    }
    if (fromDecision || toDecision) {      
      if (fromDecision) {
        query.push(
          `common.finishDate>=${convertDateFormatWithoutLocale(
            fromDecision,
            DateFormat.invertedDate
          )}`
        );
      }
      if (toDecision) {
        const toDecisionPlus = new Date(toDecision as any);

        toDecisionPlus.setDate(toDecisionPlus.getDate() + 1);
        query.push(
          `common.finishDate<${convertDateFormatWithoutLocale(
            toDecisionPlus,
            DateFormat.invertedDate
          )}`
        );
      }
    }
    if (term) {
      const termDate = this.convertDate(term);
      const nextDate = this.convertDate(term, 1);

      query.push(
        `common.dueDate<${convertDateFromBackendFormat(
          nextDate,
          DateFormat.backend
        )} and common.dueDate>${convertDateFromBackendFormat(
          termDate,
          DateFormat.backend
        )}`
      );
    }
    const qObj = {
      query: query.join(' and '),
      fulltext: fulltext.join(' or '),
    };
    if (resetPagination) {
      this.$emit('pagination', { page: 1 });
      this.patchQueryParams({ page: "1" });
    }
    this.$emit('query', qObj);
  }

  private convertDate(term: number | null, day = 0) {
    const date = new Date();
    date.setHours(0, 0, 0, 0);
    date.setDate(date.getDate() + (Number(term) + day));
    return date;
  }

  protected async getEmployees(val: string) {
    this.employeeSearch = val;
    this.$refs.employee.refresh();

    // todo actual search

    /* подзапросы не работают
      if(middlename) {
        query.push(`person.middlename==${middlename}*`)
      }
      if(firstname) {
        query.push(`person.firstname==${firstname}*`)
      }
      if(surname) {
        query.push(`person.surname==${surname}*`)
      }
      if(!firstname && !middlename) {
        query.push(`person.middlename==${surname}*`)
        query.push(`person.firstname==${surname}*`)
        queryString = query.join(' or ')
      } else {
        queryString = query.join(' and ')
      }


      //@ts-ignore
      await this.dispatchAccounts({
        opts: {
          expand: 'person',
          page: 1,
          perPage: 1000,
          query: `(${queryString})`, // and own_type==my
        },
      });
      //@ts-ignore
      update(() => this.employeeOptions = this.accountsRecords.map(({id, person}) => {
        let label = 'Неизвестный';
        if (person && typeof person === 'object') {
          label = `${person.surname || ''} ${person.firstname || ''} ${person.middlename || ''} `;
        }
        return {
          label,
          value: id,
          checked: true
        }
      }));

      */
  }

  protected employeeOptions = [];
  protected employeeSearch = '';

  protected get employeeFilterOptionsDefault(): EmployeeOption[] {
    return [
      {
        label: 'Текущий',
        value: this.userid,
        checked: false,
        option: 'id',
      },
      {
        label: 'Не назначен',
        value:'"Не назначен"',
        checked: false,
        option: 'fullName',
      },
    ];
  }

  protected filterOutDefault({ value }: EmployeeOption): boolean {
    return !this.employeeFilterOptionsDefault.some((d) => d.value === value);
  }

  protected get employeeFilterOptions(): EmployeeOption[] {
    const def = this.employeeFilterOptionsDefault;
    const checked = this.filter.employee.filter(this.filterOutDefault);
    const employeeSearch = this.employeeSearch
      .replace(/[^\s'`a-zа-яё-]/i, '')
      .trim();

    let employeeFilterOptions: EmployeeOption[] = [];

    if (employeeSearch) {
      const employeeSearchRegExp = new RegExp(
        employeeSearch.replace(/\s+/g, '\\S*\\s+'),
        'i'
      );

      employeeFilterOptions = this.employeeOptions.filter(
        ({ label, value }) =>
          employeeSearchRegExp.test(label) &&
          !def.some((d) => d.value === value) &&
          !checked.some((d) => d.value === value)
      );
    }

    const result: EmployeeOption[] = [{ separator: true }];
    if (checked.length) {
      result.push(...checked);
    }
    if (employeeFilterOptions.length) {
      result.push(...employeeFilterOptions);
    }

    if (result.length === 1) {
      return def;
    }

    return def.concat(result);
  }

  protected get employeeFilterChecked(): EmployeeOption[] {
    return this.employeeFilterOptions.filter((e) => e.checked);
  }

  /*
   ************** Organizations ******************
   */

  protected organizations = {
    pageSize: 20,
    lastPage: 0,
    nextPage: 1,
    search: '',
    records: [],
    selected: [] as Organization[],
    loading: false,
    coolDown: false,
  };

  protected departments = {
    pageSize: 20,
    lastPage: 0,
    nextPage: 1,
    search: '',
    records: [],
    selected: null as any,
    loading: false,
    coolDown: false,
  };

  protected services = {
    search: '',
    records: [],
    selected: null,
    loading: false,
  };

  protected organizationsSearch: string[] = [];
  protected goalsList: any[] = [];

  protected getOrganizations() {
    return new Promise((resolve) => {
      this.organizations.loading = true;
      const arrQuery = [
        this.organizations.search
          ? `fullname=searchprefix="${this.organizations.search}" or inn=searchprefix="${this.organizations.search}" or ogrn=searchprefix="${this.organizations.search}"`
          : null,
      ];
      const currentQuery = arrQuery.filter((filter) => !!filter).join(' and ');

      const isRegionObserver = this.$AC.haveScope(USER_SCOPES.REGION_OBSERVER);

      const isOneOfScope = this.$AC.haveOneOfScope([
        USER_SCOPES.ALL_ORG_ADMIN,
        USER_SCOPES.CLOUD_ADMIN,
        USER_SCOPES.RF_OBSERVER_SCOPE,
      ]);

      let handler = this.query.onlyMy ? 'my' : 'managed';

      if (isRegionObserver) {
        handler = 'myRegion';
      }

      if (isOneOfScope) {
        handler = 'managed';
      }

      // todo OPENAPI idm, pagingOrganizations add handler typings "myRegion"
      this.$store
        .dispatch('idm/organizations/dispatchOrganizations', {
          handler: handler,
          page: this.organizations.nextPage,
          perPage: this.organizations.pageSize,
          fulltext: currentQuery,
        })
        .then((data) => {
          resolve(data);
        });
    });
  }

  protected async getDepartments() {
    if (this.isSpecialistOrRegistrar && !this.isObserverOrAdmin) {
      if (this.userDomain?.schema !== 'Organization') {
        return [];
      }

      const departments = await this.getOrganizationDepartments(
        this.userDomain.id
      );

      return departments?.filter((department: any) =>
        department?.fullname
          ?.toLowerCase()
          ?.includes(this.departments.search?.toLowerCase())
      );
    } else {
      const orgs = this.organizations.selected || [];
      const deps = await Promise.all(
        orgs.map((org: any) => this.getOrganizationDepartments(org.id))
      );
      return deps
        .flatMap((d) => d)
        ?.filter((department: any) =>
          department?.fullname
            ?.toLowerCase()
            ?.includes(this.departments.search?.toLowerCase())
        );
    }
  }

  protected async getOrganizationDepartments(organizationId: string) {
    const nodes = await domainApi
      .getDomainChildrenTreeV1(organizationId, 1, ['Department'])
      .then(({ data }) => data)
      .catch(() => {
        throw new Error(
          '[components][statements][components][search-bar][domainApi]'
        );
      });

    return nodes?.leafs?.map((leaf: any) => leaf.value) || [];
  }

  protected async getService() {
    try {
      this.services.loading = true;
      const fulltextString = this.services.search
        ? `(shortName=searchprefix="${this.services.search}" or shortName=search="${this.services.search}") or (fullName=searchprefix="${this.services.search}" or fullName=search="${this.services.search}")`
        : '';
      const records = await new ServiceApi().searchService('', fulltextString);
      return records.data;
    } catch (e) {
      if (e instanceof Error) notifyError(e.message.toString());
    } finally {
      this.services.loading = false;
    }
  }

  setOrgValues(val: any[]) {
    if (val) {
      this.organizationsSearch = val.map((item: any) => {
        return item.id || '';
      });
    } else {
      this.organizationsSearch = [];
    }
  }

  handleServicesValues(val: any): void {
    if (val) {
      this.goalsList = val.goalsList || [];
      this.filter.services = val.id || '';
    } else {
      this.filter.services = '';
      this.goalsList = [];
      this.filter.goals = [];
    }
  }

  handleOrgValues(val: any[]) {
    if (val) {
      this.filter.organizations = val.map((item: any) => {
        return item.id || '';
      });
    } else {
      this.filter.organizations = [];
    }
    this.departments.selected = [];
    this.departments.records = [];
    this.filter.departments = [];
  }

  handleDepartmentValues(val: any[]): void {
    if (val) {
      this.filter.departments = val.map((item: any) => {
        return item.id || '';
      });
    } else {
      this.filter.departments = [];
    }
    const departments = this.filter.departments.join() || undefined;
    // this.$router.push({ query: { ...this.$route.query, departments } });
    // this.startSearch();
  }

  /*
   ************** /Organizations ******************
   */

  protected subjects = {
    pageSize: 20,
    lastPage: 0,
    nextPage: 1,
    search: '',
    records: [],
    selected: [] as Region[],
    loading: false,
    coolDown: false,
  };

  async getSubjects() {
    this.subjects.loading = true;
    return await regionApi
      .pagingRegions(
        undefined,
        this.subjects.lastPage + 1,
        this.subjects.pageSize
      )
      .then(({ data }) => data.records)
      .catch(() => {
        throw new Error(
          '[components][statements][components][search-bar][pagingRegions]'
        );
      });
  }

  async parseQueryParams(query: StatementsSearchQuery): Promise<void> {
    const range: string | undefined = query.daterange;
    const rangeDecision: string | undefined = query.daterangeDecision;
    const {
      employees,
      status,
      organizations,
      departments,
      search,
      services,
      goals,
      subjects,
      term,
    } = query;

    if (range) {
      const dateMask = this.$store.getters['i18N/localeMask'].date;
      const dates = range.split(',');

      if (dates[0]) {
        this.filter.from = date.extractDate(dates[0], dateMask);
      }
      if (dates[1]) {
        this.filter.to = date.extractDate(dates[1], dateMask);
      }
    }

    if (rangeDecision) {
      const dateMask = this.$store.getters['i18N/localeMask'].date;
      const dates = rangeDecision.split(',');

      if (dates[0]) {
        this.filter.fromDecision = date.extractDate(dates[0], dateMask);
      }
      if (dates[1]) {
        this.filter.toDecision = date.extractDate(dates[1], dateMask);
      }
    }

    if (employees) {
      const idList: string[] = employees.split(',');

      idList.forEach((id) => {
        const employee: Option | undefined = this.employeeOptions.find(
          (el: any) => el.value === id
        );
        const alreadyInList = this.filter.employee.find(
          (el) => el.value === id
        );

        if (employee && !alreadyInList) {
          this.filter.employee.push(employee);
        }
      });
    }

    if (status) {
      const codes: string[] = status.split(',');

      const originalDictionaryOfStatuses: any = (await this.statusDictionaryPromise) || {};
      codes.forEach((code) => {
        const statusLabel = originalDictionaryOfStatuses[code]?.description;
        const stItem: Option | undefined = this.statusOptions.find(
          (el) => el.label === statusLabel
        );
        const alreadyInList = this.filter.status.find(
          (el) => el.value === stItem?.value
        );

        if (stItem && !alreadyInList) {
          this.filter.status.push(stItem);
        }
      });
    }

    if (goals?.length) {
      const codes: string[] = goals.split(',');
      this.filter.goals = codes;
    }

    if (organizations) {
      this.filter.organizations = organizations.split(',');
      // todo fix костыльный проброс выбранных данных в стейт-объект для BaseAutocompleteSelect
      this.getOrganizationsSelected();
    }

    if (departments) {
      this.filter.departments = departments.split(',');
      this.getDepartmentsSelected(departments.split(','));
    }

    if (subjects?.length) {
      this.filter.subjects = subjects.split(',');
      // todo fix костыльный проброс выбранных данных в стейт-объект для BaseAutocompleteSelect
      this.getSubjectsSelected();
    }

    if (services) {
      this.filter.services = services;
      this.setServicesQuery();
    }

    if (search) {
      this.filter.search = search;
    }
    if (term) {
      this.filter.term = Number(term);
    }

    // Если в query есть значения, то это уже не просто открытие раздела "Заявления" а переход к ним по ссылке с определёнными параметрами
    // Только при простом переходе (без параметров) к разделу "Заявления" мы не запускаем запрос данных автоматически
    if (Object.entries(this.$route.query).length > 0) {
      this.startSearch();
    }
  }

  private async setServicesQuery() {
    if (this.filter.services?.length) {
      const queryServices = `id==${this.filter.services}`;
      const organizationsSelected = await new ServiceApi().searchService(
        queryServices
      );
      // @ts-ignore
      this.services.selected = organizationsSelected.data[0];
      // @ts-ignore
      this.goalsList = organizationsSelected.data[0].goalsList;
    }
  }

  private async getOrganizationsSelected() {
    if (this.filter.organizations) {
      const queryOrganizations = this.filter.organizations.map((id) => {
        return `id==${id}`;
      });

      this.organizations.selected = await organizationApi
        .searchOrganizations(undefined, queryOrganizations.join(' or '))
        .then(({ data }) => data)
        .catch(() => {
          throw new Error(
            '[components][statements][components][search-bar][searchOrganizations]'
          );
        });
    }
  }

  private async getDepartmentsSelected(departments: string[]) {
    this.departments.selected = await Promise.all(
      departments.map((id) => domainApi.getDomain(id).then(({ data }) => data))
    );
  }

  private async getSubjectsSelected() {
    if (this.filter.subjects) {
      const querySubjects = this.filter.subjects.map((id) => {
        return `id==${id}`;
      });

      const subjectsSelected = await regionApi
        .pagingRegions(
          undefined,
          undefined,
          undefined,
          querySubjects.join(' or ')
        )
        .then(({ data }) => data)
        .catch(() => {
          throw new Error(
            '[components][statements][components][search-bar][pagingRegions]'
          );
        });
      this.subjects.selected = subjectsSelected?.records || [];
    }
  }
  // FIXME: Скрыто до принятия решения о функциональности кнопки настроить колонки
  // private openMappingDialog() {
  //   this.isMappingDialogOpen = true;
  // }

  private changeColumns(result: any, serviceId: string, attrList: any) {
    this.isMappingDialogOpen = false;
    this.$emit('change-columns', result, serviceId, attrList);
  }

  protected async created(): Promise<void> {
    await this.$store.dispatch('idm/accounts/dispatchAccount', {
      id: this.userid,
      data: {
        expand: 'user-domain',
      },
    });
    await this.$store.dispatch('idm/accounts/dispatchAccounts', {
      expand: 'person',
      page: 1,
      perPage: 50,
      handler: 'my',
      // query: `(${queryString})`, // and own_type==my
    });
    this.employeeOptions = this.accountsRecords.map(
      ({ id, person }: { id: any; person: any }) => {
        let label = 'Неизвестный';

        if (person && typeof person === 'object') {
          label = `${person.surname || ''} ${person.firstname || ''} ${
            person.middlename || ''
          } `;
        }

        return {
          label,
          value: id,
          checked: true,
        };
      }
    );

    await this.parseQueryParams(this.query);
    // FIXME Это временное решение.
    if (!this.$AC.haveScope(USER_SCOPES.CLOUD_ADMIN)) {
      this.startSearch();
    }
  }

  private get accountsCard() {
    return this.$store.getters['idm/accounts/accountsCard'];
  }

  private get accountsRecords() {
    return this.$store.getters['idm/accounts/accountsRecords'];
  }

  get isSaveDataAllowed() {
    return this.$AC.haveOneOfScope([
      USER_SCOPES.CLOUD_ADMIN,
      USER_SCOPES.REGION_ADMIN,
      USER_SCOPES.ORGANIZATION_ADMIN,
      USER_SCOPES.SPECIALIST,
      USER_SCOPES.ALLOCATE_PERMISSION_SPEC,
      USER_SCOPES.ALLOCATE_PERMISSION_REG,
      USER_SCOPES.REGISTRAR,
      USER_SCOPES.OBSERVER,
      USER_SCOPES.REGION_OBSERVER,
    ]);
  }

  get noFetchAfterFilterChangedTooltipLabel() {
    return `${this.$t('statements.Common.tooltip.noFetchAfterFilterChange')}` +
      ` (${this.$t('statements.Common.tooltip.button')}` +
      ` "${this.$t('statements.Common.button.search')}")`;
  }
}

export interface Filter {
  search: string;
  status: Option[];
  employee: EmployeeOption[];
  organizations?: string[];
  departments?: string[];
  services?: string;
  goals?: any;
  subjects?: string[];
  from: Date | string;
  to: Date | string;
  fromDecision: Date | string;
  toDecision: Date | string;
  visible: boolean;
  page?: number;
  term?: number | null;
}
</script>

<style lang="scss">

.redesigned-search-bar {

  .filter-icon {
    color: #1491CF;
    border-radius: 24px;

    .q-badge {
      top: 2px;
      right: 10px;
      font-family: "Golos Text", sans-serif;
      font-style: normal;
      font-weight: 600;
      font-size: 10px;
      line-height: 12px;
      letter-spacing: 0.05em;
      border-radius: 50%;
    }
  }

  .golos-label {
    font-family: 'Golos Text', sans-serif;
    font-style: normal;
    font-weight: 500;
    font-size: 10px;
    line-height: 12px;
    letter-spacing: 0.05em;
    text-transform: uppercase;    
  }

  .radio-cell {
    --q-color-primary: #1491CF; 
    border: 1px solid rgba(1, 72, 116, 0.18);
    overflow: hidden;

    display: inline-grid;
    align-items: center;
    justify-items: center;

    font-family: 'Golos UI', sans-serif;
    font-style: normal;
    font-weight: 400;
    font-size: 14px;
    line-height: 16px;
    
    color: rgba(2, 12, 18, 0.9);

    min-width: 5.2em;

    &.selected {
      background: #e9f1f8;
    }
  }

  .close-button {
    font-family: 'Golos UI', sans-serif;
    font-style: normal;
    font-weight: 400;
    font-size: 12px;
    line-height: 14px;
    color: #CF5F14;
  }

  > .q-card {
    background: #eef2f6;
    border: 1px solid rgba(1, 72, 116, 0.11);
    border-radius: 0px 0px 8px 8px;
    .q-field__control {
      background: #e5edf5 !important;
      //color: rgba(2, 12, 18, 0.7);

    }
  }

  .q-field:not(.q-field--focused) {
    .q-field__control {
      border-bottom: 1px solid rgba(1, 72, 116, 0.18);
    }
  }

  .q-field--outlined .q-field__control:before {
    border: none;
  }

  .q-field__control {
    font-family: 'Golos Text', sans-serif;
    background: #e9f1f8 !important;
    color: #1491CF;
    background-blend-mode: multiply;
    border-radius: 0px;
    border-bottom: 1px solid #e9f1f8; // такой же толщены что бы не прыгало при фокусе
  }

  .m-search-bar .q-field__control-container > .q-field__native.row.items-center > span {
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
  }
  .m-search-bar {
    justify-content: space-between;
  }
}
</style>
