<template>
  <div class="q-gutter-y-md">
    <q-input
      v-model="filter.search"
      dense
      filled
      debounce="1000"
      placeholder="Фамилия, СНИЛС, Номер заявления"
      @input="startSearch"
      @keydown.enter="startSearch"
      @keydown.esc="filter.search = ''"
    >
      <template #prepend>
        <q-icon name="search" />
      </template>
      <template #append>
        <q-btn dense flat icon="settings" round @click="toggleFilter" />
      </template>
    </q-input>

    <q-slide-transition>
      <q-card bordered flat v-show="filter.visible">
        <q-card-section>
          <div class="row q-gutter-x-md" style="margin-bottom: -16px">
            <div class="q-mb-md row q-gutter-x-sm items-center">
              <p class="q-ma-none">
                {{ $t('registries.status') }}
              </p>
              <q-select
                v-model="filter.status"
                filled
                stretch
                flat
                dense
                borderless
                multiple
                style="min-width: 20em"
                :emit-value="false"
                :options="statusOptions"
                @input="handleStatusChange"
              >
                <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 class="row q-gutter-x-sm">
              <div class="q-mb-md row q-gutter-x-sm items-center q-ml-no">
                <p class="q-ma-none">
                  Дата принятия решения
                </p>
                <q-field borderless dense style="min-width: 20em">
                  <x-date-input
                    v-model="filter.resDate"
                    hide-bottom-space
                    class="font-12"
                    @input="startSearch"
                  />
                </q-field>
              </div>

              <div class="q-mb-md row q-gutter-x-sm items-center">
                <p class="q-ma-none">
                  Дата выплаты
                </p>
                <q-field borderless dense style="min-width: 20em">
                  <x-date-input
                    v-model="filter.paymentDate"
                    hide-bottom-space
                    class="font-12"
                    @input="startSearch"
                  />
                </q-field>
              </div>
            </div>

            <div
              v-if="$listeners['save-data']"
              class="q-mb-md row q-gutter-x-sm items-center"
            >
              <q-btn
                color="primary"
                label="Сформировать выгрузку"
                @click="saveData({ exnension: 'xls', isNewBtn: true })"
              />
            </div>
          </div>
        </q-card-section>
        <q-btn
          dense
          flat
          class="absolute-top-right"
          color="grey-7"
          icon="close"
          size="10px"
          @click="ResetFilter"
        />
      </q-card>
    </q-slide-transition>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import {
  EmployeeOption,
  Option,
} from '@components/statements/components/search-bar.vue';
import { USER_SCOPES } from '@constants/entities/Scope';
import { oidcClientComputed } from '@state/modules/oidc-client';
import XDateRange from '@components/statements/components/x-date-range.vue';
import XDateInput from '@components/statements/components/x-date-input.vue';
import BaseAutocompleteSelect from '@components/common/base-autocomplete-select/index.vue';
import {
  convertDateFormat,
  convertDateFormatWithoutLocale,
} from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { SearchBarRefs } from '@components/common/base-search-bar/index';
import { ServiceApi } from '@openapi/registry-service';
import { GraphApi } from '@openapi/idm';
import omit from 'lodash/omit';
import startCase from 'lodash/startCase';
import { notifyError } from '@utils/notify';

type SaveDataOptions = {
  exnension: 'xml' | 'xls' | 'csv',
  isNewBtn: boolean // @TODO временное решение, которое позволяет понять: используется новая кнопка выгрузки или старая - как откажемся от старой - надо будет свойство удалить!!!
}

export const REG_ORDER_ID = /^\d+$/g;
const graphApi = new GraphApi();
class AccountsCreateInjection extends Vue.extend({
  computed: {
    ...oidcClientComputed,
  },
}) {}

interface Filter {
  search: string;
  status: Option[];
  employee: EmployeeOption[];
  organizations?: string[];
  departments?: string[];
  services?: string;
  goals?: any;
  subjects?: string[];
  from: Date | string;
  to: Date | string;
  visible: boolean;
  page?: number;
  term?: number | null;
  resDate?: string;
  paymentDate?: string;
}

@Component({
  components: {
    XDateRange,
    BaseAutocompleteSelect,
    XDateInput
  },
})
export default class UkranianRefugeeSearchBar extends AccountsCreateInjection {
  @Prop() statusOptions: Option[];
  $refs!: SearchBarRefs;
  protected filter: Filter = {
    search: '',
    status: [],
    employee: [],
    organizations: [],
    departments: [],
    goals: [],
    services: '',
    from: '',
    to: '',
    visible: false,
    resDate: '',
    paymentDate: '',
  };

  protected goalsList: any[] = [];

  protected organizations = {
    pageSize: 20,
    lastPage: 0,
    nextPage: 1,
    search: '',
    records: [],
    selected: null,
    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 startSearch(): void {
    const query: string[] = [];
    const queryConverter: string[] = [];
    const fulltextConverter: string[] = [];

    let { search } = this.filter;
    const {
      status,
      resDate,
      paymentDate
    } = this.filter;

    if (search) {
      search.trim()
      search = search.replace(/\\/g, ''); 
      if (search.match(/^\d/)) {
        search = search.replace(/\s/g, '%')
      } else {
        search = startCase(search);
      }
      query.push(`subjectData.lastName=like=*${search}* or `+
        `subjectData.snils=like=*${search}* or `+
        `orderId=like=*${search}*`);
      queryConverter.push(`subjectData.lastName=like=*${search}* or `+
        `subjectData.snils=like=*${search}* or `+
        `orderId=like=*${search}*`);
    }
    if (status.length) {
      query.push(status.map((e) => `statement.status=="${e.value}"`).join(' or '));
      fulltextConverter.push(status.map((e) => `statement.status=search='${e.value?.replaceAll(' ', '%')}'`).join(' or '));
    }
    if (resDate) {
      query.push(
        `document.resDate==${convertDateFormatWithoutLocale(resDate, DateFormat.invertedDate)}`
      );
      queryConverter.push(
        `document.resDate==${convertDateFormatWithoutLocale(resDate, DateFormat.invertedDate)}`
      );
    }
    if (paymentDate) {
      query.push(
        `payment.paymentDate==${convertDateFormatWithoutLocale(paymentDate, DateFormat.date)}`
      );
      queryConverter.push(
        `payment.paymentDate==${convertDateFormatWithoutLocale(paymentDate, DateFormat.date)}`
      );
    }

    const qObj = {
      query: query.join(' and '),
      fulltext: '',
      queryConverter: queryConverter.join(' and '),
      fulltextConverter: fulltextConverter.join(' or '),
    };
    this.$emit('query', qObj);
  }

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

  protected saveData(options?: SaveDataOptions): void {
    this.$emit('save-data', options);
  }

  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',
    });
    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,
        };
      }
    );
  }

  handleRangeChange() {
    const result = ['', ''];

    if (this.filter.from !== null) {
      result[0] = convertDateFormatWithoutLocale(this.filter.from);
    }

    if (this.filter.to !== null) {
      result[1] = convertDateFormatWithoutLocale(this.filter.to);
    }
    this.startSearch();
  }

  handleEmployeeChange() {
    this.startSearch();
  }

  handleStatusChange(): void {
    this.startSearch();
  }

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

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

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

    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(', ')
      : '';
  }

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

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

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

  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 }: Option) =>
          employeeSearchRegExp.test(label as string) &&
          !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);
  }

  handleOrgValues(val: any[]) {
    if (val) {
      this.filter.organizations = val.map((item: any) => {
        return item.fullname || '';
      });
    } else {
      this.filter.organizations = [];
    }
    const organizations = this.filter.organizations.join() || undefined;

    this.$router.push({ query: { ...this.$route.query, organizations } });
    this.departments.selected = [];
    this.departments.records = [];
    this.filter.departments = [];
    this.startSearch();
  }

  handleDepartmentValues(val: any[]) {
    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();
  }

  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) {
      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 = [];
    }
  }

  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 ');

      this.$store
        .dispatch('idm/organizations/dispatchOrganizations', {
          page: this.organizations.nextPage,
          perPage: this.organizations.pageSize,
          fulltext: currentQuery,
          handler: 'my',
        })
        .then((data) => {
          resolve(data);
        });
    });
  }

  protected async getDepartments() {
    if (this.isSpecialistOrRegistrar) {
      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) =>
          department?.fullname
            ?.toLowerCase()
            ?.includes(this.departments.search?.toLowerCase())
        );
    }
  }

  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 userDomain() {
    return this.accountsCard['user-domain'];
  }

  handleGoalsChange(): void {
    const goals = this.filter.goals?.join() || undefined;
    const queryParams = this.resetPagination();
    this.$router.push({ query: { ...queryParams, goals } });
    this.startSearch();
  }

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

    const services = this.filter.services || undefined;
    const goals = this.filter.goals || undefined;

    this.$router.push({ query: { ...this.$route.query, services, goals } });
    this.startSearch();
  }

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

  protected ResetFilter(): void {
    this.filter.search = '';
    this.filter.status = [];
    this.filter.employee = [];
    this.filter.goals = [];
    this.filter.subjects = [];
    this.filter.from = '';
    this.filter.to = '';
    this.filter.to = '';
    this.filter.visible = false;
    this.startSearch();
  }

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

  protected async getOrganizationDepartments(
    organizationId: string
  ): Promise<Array<any>> {
    // При использовании данного компонента для улучшения скорости работы запроса
    // его стоит заменить на новый
    // domainApi.getDomainChildrenTreeV1(organizationId, 1, ['Department'])
    const nodes = await graphApi
      .tree('Organization', 'incoming', organizationId, 1, ['Department'])
      .then(({ data }) => data)
      .catch(() => {
        throw new Error(
          '[components][registries][search-bars][search-bars][graphApi]'
        );
      });

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