<i18n>
{
  "ru": {
    "TicketsSchedule": {
      "plural": {
        "day": "{n} дней | {n} день | {n} дня | {n} дней",
        "capacity": "Нет слотов | {n} слот | {n} слота | {n} слотов",
        "hour": "{n} часов | {n} час | {n} часа | {n} часов",
        "minute": "{n} минут | {n} минута | {n} минуты | {n} минут"
      },
      "areaId": "Номер кабинета",
      "cancel": "Отмена",
      "cancelAll": "Отменить все изменения",
      "save": "Сохранить",
      "saveAll": "Сохранить все изменения",
      "create": "Создать",
      "generate": "Сформировать",
      "add": "Добавить",
      "edit": "Редактировать",
      "deleteSchedule": "Удалить",
      "restoreSchedule": "Восстановить",
      "created": "Расписание успешно создано",
      "saved": "Расписание успешно сохранено",
      "areaIdIsRequired": "Обязательно заполните номер кабинета!",
      "timeSlotLabel": "Длительность слота:",
      "periodLabel": "Период действия расписания до",
      "daysDelayLabel": "Период обработки документов",
      "useProductionCalendar":"Использовать производственный календарь при расчете периода обработки документов",
      "schedule": "Расписание",
      "employees": "Сотрудники",
      "services": "Услуги",
      "payAttentionToChanges": "Вы внесли изменения в текущее расписание, но еще не сохранили их. Будьте внимательны.",
      "nothingFound": "Для вашей организации еще не создано ни одного расписания",
      "doesntSet": "не задан",
      "pushAddToCreateOne1": "Чтобы добавить расписание нажмите кнопку",
      "pushAddToCreateOne2": "в панели сверху",
      "scheduleWillBeSaved": "Расписание будет сохранено",
      "scheduleWillBeDeleted": "Расписание будет удалено",
      "timezoneHasNotSet": "Невозмжно создать расписание. Для вашей организации не задан часовой пояс. Обратитесь к администратору организации",
      "scheduleFor": "Расписание для кабинета №{0}",
      "capacity": "Вместимость",
      "error": {
        "notValid": "Содержит недопустимые символы",
        "notUniq": "Расписание для этого кабинета существует",
        "organization-404": "Не удалось загрузить огранизацию",
        "create-500": "Не удалось создать организацию"
      }
    }
  },
  "en": {
    "TicketsSchedule": {
      "plural": {
        "day": "{n} day | {n} days",
        "capacity": "{n} slot | {n} slots",
        "minute": "{n} minute | {n} minutes"
      },
      "areaId": "Area id",
      "cancel": "Cancel",
      "cancelAll": "Create all changes",
      "save": "Save",
      "saveAll": "Save all changes",
      "create": "Create",
      "generate": "Generate",
      "add": "Add",
      "edit": "Edit",
      "deleteSchedule": "Delete schedule",
      "restoreSchedule": "Restore schedule",
      "created": "Schedule has been create successfully!",
      "saved": "Schedule has been saved successfully!",
      "areaIdIsRequired": "AreaId is required!",
      "timeSlotLabel": "Time slot:",
      "periodLabel": "Generate slots until",
      "daysDelayLabel": "Document processing period",
      "useProductionCalendar":"Use production calendar when calculating document processing period",
      "schedule": "Schedule",
      "employees": "Employees",
      "services": "Services",
      "payAttentionToChanges": "You have changed some this chedule.",
      "nothingFound": "Nothing has found",
      "doesntSet": "doesn't set",
      "pushAddToCreateOne1": "Push button ",
      "pushAddToCreateOne2": "to create first one",
      "scheduleWillBeSaved": "Schedule will be saved",
      "scheduleWillBeDeleted": "Schedule will be deleted",
      "timezoneHasNotSet": "The timezone has not set for your organization. Inform your administrator.",
      "scheduleFor": "Schedule for office number {0}",
      "capacity": "Room's capacity",
      "error": {
        "notValid": "Not valid",
        "notUniq": "Not uniq",
        "organization-404": "Organization has no found"
      }
    }
  }
}
</i18n>

<template>
  <div class="q-gutter-y-md">
    <office-select
      :id.sync="areaId"
      :name.sync="areaName"
      :action="areaIdAction"
      :open.sync="areaIdPrompt"
      :schedules="schedules"
      :areaIdslist="areaIdslist"
      @save="saveAreaId"
    />
    <q-card>
      <q-tabs align="left" v-model="mainTab" mobile-arrows style="height: 53px">
        <q-tab
          v-for="sch in schedules"
          :key="sch.id"
          :name="getTabIndex(sch)"
          :title="$t('TicketsSchedule.scheduleFor', [sch.areaId])"
          :icon="sch.deleted ? 'delete' : 'date_range'"
          :disable="sch.deleted"
          class="bg-gray"
          color="grey-7"
        >
          {{ sch.areaName }}

          <q-badge
            v-if="isScheduleDirty(getTabIndex(sch))"
            color="red-4"
            floating
            rounded
            style="padding: 6px"
            :title="
              sch.deleted
                ? $t('TicketsSchedule.scheduleWillBeDeleted')
                : $t('TicketsSchedule.scheduleWillBeSaved')
            "
          />

          <q-menu touch-position context-menu>
            <q-list dense style="min-width: 100px">
              <q-item
                clickable
                v-close-popup
                v-if="isOrganizationAdmin && sch.deleted"
                @click="restoreSchedule(getTabIndex(sch))"
              >
                <q-item-section>{{
                  $t('TicketsSchedule.restoreSchedule')
                }}</q-item-section>
              </q-item>
              <q-item
                clickable
                v-close-popup
                v-else-if="isOrganizationAdmin"
                @click="deleteSchedule(getTabIndex(sch))"
              >
                <q-item-section>{{
                  $t('TicketsSchedule.deleteSchedule')
                }}</q-item-section>
              </q-item>
              <q-item
                clickable
                v-close-popup
                v-if="isOrganizationAdmin && !sch.deleted"
                @click="openEditAreaIdPrompt(getTabIndex(sch), getTabName(sch))"
              >
                <q-item-section>{{
                  $t('TicketsSchedule.edit')
                }}</q-item-section>
              </q-item>
            </q-list>
          </q-menu>
        </q-tab>
        <q-btn
          v-if="isOrganizationAdmin"
          dense
          :loading="loading"
          name="add"
          icon="add"
          class="q-ml-sm"
          @click="openAddAreaIdPrompt"
        />
      </q-tabs>
    </q-card>
    <q-card>
      <q-card-section v-if="editingSchedule" class="row" style="gap: 1em">
        <employees-select
          v-model="editingSchedule.employees"
          value-id-key="employeeId"
          :is-schedule-saved="isScheduleSaved(currentSchedule)"
          :unavelible="selectedEmployeesIds"
          @input="touch"
          style="flex: 1 1 auto"
        />
        <services-select
          v-model="editingSchedule.services"
          :is-schedule-saved="isScheduleSaved(currentSchedule)"
          :unavelible="selectedServicesIds"
          :disable="!employeesAreSelected"
          value-id-key="serviceId"
          @input="touch"
          style="flex: 1 1 auto"
        />
        <div style="flex: 1 1 auto" v-if="isServiceUFSIN(editingSchedule)">
          <q-select
            outlined
            v-model="officeType"
            :options="officeTypesOptions"
            option-value="value"
            option-label="label"
            label="Тип помещения"
            @input="setOffiseType"
          />
        </div>
      </q-card-section>
      <q-card-section v-if="editingSchedule && isOrganizationAdmin">
        <week-schedule
          v-model="editingSchedule.schedules"
          :timeSlot="editingSchedule.timeSlot"
          :work-calendar="workCalendar"
          :disable="!employeesAreSelected || !servicesAreSelected"
          @input="touch"
        />
        <q-card flat bordered class="q-mt-lg q-mx-sm">
          <q-card-section>
            <div class="row q-gutter-md items-end">
              <div class="col-sm-6 q-mx-none">
                <div class="q-mx-md q-mb-md caption">
                  Ваше помещение может вмещать больше одного посетителя
                </div>
                <div class="q-mx-md">
                  <q-item-label class="q-mr-sm q-item__label">
                    {{ $t('TicketsSchedule.capacity') }}
                    {{
                      $tc(
                        'TicketsSchedule.plural.capacity',
                        editingSchedule.capacity
                      )
                    }}
                  </q-item-label>
                  <q-slider
                    label
                    :label-value="
                      $tc(
                        'TicketsSchedule.plural.capacity',
                        editingSchedule.capacity
                      )
                    "
                    :marker-labels="formatMarkersDaysRange"
                    :min="1"
                    :max="31"
                    color="green"
                    :value="editingSchedule.capacity"
                    @input="setCapacity"
                  />
                </div>
              </div>
              <div class="col-sm-6 q-mx-none">
                <div class="q-mx-md">
                  <q-item-label class="q-mr-sm q-item__label">
                    Длительность слота
                    {{ formatTimeSlot(editingSchedule.timeSlot) }}, для генерации
                    слотов выберите количество дней. Слоты для приема будут
                    автоматически генерироваться в пределах периода.
                  </q-item-label>
                  <q-slider
                    snap
                    label
                    :label-value="formatTimeSlot(editingSchedule.timeSlot)"
                    :step="5"
                    :min="10"
                    :max="maxTimeSlot"
                    color="green"
                    :value="editingSchedule.timeSlot"
                    @input="setTimeSlot"
                  />
                </div>
              </div>
            </div>
            <div class="row q-gutter-md items-end">
              <div class="col-sm-6 q-mx-none">
                <div class="q-mx-md">
                  <q-item-label class="q-mr-sm q-item__label">
                    {{ $t('TicketsSchedule.periodLabel') }} {{ currentEndDate }}
                  </q-item-label>
                  <q-slider
                    label
                    :label-value="
                      $tc('TicketsSchedule.plural.day', editingSchedule.endDay)
                    "
                    :marker-labels="formatMarkersDaysRange"
                    :min="1"
                    :max="31"
                    color="green"
                    :value="editingSchedule.endDay"
                    @input="setDaysRange"
                  />
                </div>
              </div>
              <div class="col-sm-6 q-mx-none">
                <div class="q-ml-md">
                  <q-item-label class="q-mr-sm q-item__label">
                    <span
                      >{{ $t('TicketsSchedule.daysDelayLabel') }}&nbsp;</span
                    >
                    <span v-if="editingSchedule.daysDelay">{{
                      $tc(
                        'TicketsSchedule.plural.day',
                        editingSchedule.daysDelay
                      )
                    }}</span>
                    <span v-else>{{ $t('TicketsSchedule.doesntSet') }}</span>
                  </q-item-label>
                  <q-slider
                    label
                    :label-value="
                      $tc(
                        'TicketsSchedule.plural.day',
                        editingSchedule.daysDelay
                      )
                    "
                    :marker-labels="formatMarkersDaysRange"
                    :min="0"
                    :max="30"
                    color="green"
                    :value="editingSchedule.daysDelay"
                    @input="setDalay"
                  />
                </div>
              </div>
            </div>
            <div class="row q-gutter-md items-end">
              <div class="col-sm-6 q-mx-none">
                <div class="q-ml-md">
                  {{ $t('TicketsSchedule.useProductionCalendar') }}
                </div>
                <div class="q-ml-md">
                  <q-toggle
                    v-model="editingSchedule.useProductionCalendar"
                    color="green"
                    keep-color
                    @input="setuseProductionCalendar"
                  />
                </div>
              </div>
            </div>
          </q-card-section>
        </q-card>
      </q-card-section>
      <q-card-section v-if="!schedules.length && !loading">
        <div class="text-h6">{{ $t('TicketsSchedule.nothingFound') }}</div>
        <p>
          {{ $t('TicketsSchedule.pushAddToCreateOne1') }}
          <q-icon name="add" size="md" color="grey-8" />
          {{ $t('TicketsSchedule.pushAddToCreateOne2') }}
        </p>
      </q-card-section>

      <q-card-section v-if="!loading && !timezoneHasSet">
        <q-banner class="bg-red-3 text-white">
          <p>{{ $t('TicketsSchedule.timezoneHasNotSet') }}</p>
        </q-banner>
      </q-card-section>

      <q-card-actions v-if="isOrganizationAdmin && organization" align="right">
        <q-btn
          dense
          v-if="organization && dirty"
          text-color="primary"
          color="white"
          :disable="!dirty || saving"
          :title="$t('TicketsSchedule.cancelAll')"
          @click="resetSchedules()"
        >
          {{ $t('TicketsSchedule.cancel') }}
        </q-btn>
        <q-btn
          dense
          v-if="organization && dirty && timezoneHasSet"
          text-color="primary"
          color="white"
          :disable="saving || !shaduleIsReadyToBeSaved"
          :loading="saving"
          :title="$t('TicketsSchedule.saveAll')"
          @click="saveSchedules"
        >
          {{ $t('TicketsSchedule.save') }}
        </q-btn>
      </q-card-actions>
    </q-card>

    <q-inner-loading :showing="loading" />

    <q-card>
      <q-banner class="bg-blue-3 text-white" v-if="isScheduleDirty(mainTab)">
        <p>{{ $t('TicketsSchedule.payAttentionToChanges') }}</p>
      </q-banner>
      <slots-calendar
        v-if="currentSchedule && isScheduleSaved(currentSchedule)"
        :class="[
          {
            disabled: isScheduleDirty(mainTab),
          },
        ]"
        :work-calendar="workCalendar"
        :schedule="currentSchedule"
        :timezone="region && region.timezone"
        :organization="organization"
      />
    </q-card>
  </div>
</template>

<script lang="ts">
import Axios from 'axios';
import { Component, Vue, Watch } from 'vue-property-decorator';
import preorderService from '@services/preorder';
import {
  EmployeeDTO,
  OrgDTO,
  OrgScheduleDTO,
  ScheduleDTO,
  ServiceDTO,
  SlotDTO,
} from '@openapi/preorder-pgs';
import WeekSchedule from './components/WeekSchedule.vue';
import EmployeesSelect from './components/EmployeesSelect.vue';
import ServicesSelect from './components/ServicesSelect.vue';
import OfficeSelect from './components/OfficeSelect.vue';
import SlotsCalendar from './components/SlotsCalendar.vue';
import { notifyError, notifySuccess } from '@utils/notify';
import {
  Account,
  Region as IDMRegion,
  Organization as IDMOrganization,
  RegionApi,
} from '@openapi/idm';
import { OrganizationApi, Service } from '@openapi/registry-service';
import { shiftDateTo } from '@components/tickets/utils/date-manipulations';
import { convertDateFormat, getCurrentDate } from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { USER_SCOPES } from '@/constants/entities/Scope';

import { WorkCalendarForDomain } from './utils/WorkCalendarForDomain';
import { FpsDocument, searchRegistryBy } from './utils/nsiService';
import { timeParse, timeFormat } from './utils/date-manipulations';
import { get, has, isString } from 'lodash';

const organizationApi = new OrganizationApi();

interface Organization extends IDMOrganization {
  schema: 'Organization';
  parent?: Organization | Region;
  regionId?: string;
}

interface Region extends IDMRegion {
  schema: 'Region';
}

interface ExtendedOrganization extends Organization {
  services: Service[];
}

interface EditableSchedule extends OrgScheduleModel {
  deleted?: boolean;
  prevAreaId?: string;
  prevAreaName?: string;
}

class OrgScheduleModel implements OrgScheduleDTO {
  id?: number;
  areaId?: string;
  areaName?: string;
  type?: string;
  startDay: number;
  endDay: number;
  daysDelay: number;
  timeSlot: number;
  capacity: number;
  employees: EmployeeDTO[];
  services: ServiceDTO[];
  schedules: ScheduleDTO[];
  useProductionCalendar: boolean;

  constructor(model: OrgScheduleDTO = {}) {
    return {
      ...model,
      areaName: model.areaName || model.areaId,
      type: model.type || '',
      startDay: model.startDay || 1,
      endDay: model.endDay || 31,
      daysDelay: model.daysDelay || 0,
      timeSlot: model.timeSlot || 30,

      capacity: model.capacity || 1,

      employees: model.employees || [],
      services: model.services || [],
      schedules: model.schedules || [],
      useProductionCalendar:
        model.useProductionCalendar == null
          ? true
          : model.useProductionCalendar,
    };
  }
}

@Component({
  components: {
    WeekSchedule,
    EmployeesSelect,
    ServicesSelect,
    OfficeSelect,
    SlotsCalendar,
  },
  methods: {
    get,
  },
})
export default class ScheduleTab extends Vue {
  loading = false;
  saving = false;
  dirty = false;

  touch(): void {
    this.dirty = true;
  }

  clear(): void {
    this.dirty = false;
    this.fetch();
  }

  region: Region | null = null;
  organization: ExtendedOrganization | null = null;
  organizationFromServer: OrgDTO | null = null;
  fpsOrganization: FpsDocument | null = null;
  fpsCode = '';
  schedules: EditableSchedule[] = [];
  slots: SlotDTO[] = [];
  employees: Account[] = [];

  mainTab = '';
  subTab = 'schedule';
  slotsAreLoading = false;

  get timezone(): number | undefined {
    const timezone = this.region?.timezone;
    return timezone ? parseInt(timezone) : undefined;
  }

  get timezoneHasSet(): boolean {
    return !!this.timezone;
  }

  get currentEndDate(): string {
    const now = new Date(getCurrentDate());
    // Отображать дату включительно с текущим днем
    let endDay = (this.editingSchedule?.endDay || 1) - 1;
    endDay = endDay < 0 ? 0 : endDay;
    const endDate = shiftDateTo(now, endDay);
    return convertDateFormat(endDate, '', DateFormat.date);
  }

  get editingSchedule(): EditableSchedule | undefined {
    return this.editableSchedulesIndex[this.mainTab];
  }

  get currentSchedule(): OrgScheduleDTO | undefined {
    return this.schedulesIndex[this.mainTab];
  }

  get selectedEmployeesIds(): string[] {
    return this.schedules.flatMap(({ employees }) => {
      return employees?.map(({ employeeId }) => employeeId || '') || [];
    });
  }

  get selectedServicesIds(): string[] {
    return this.schedules.flatMap(({ services }) => {
      return services?.map(({ serviceId }) => serviceId || '') || [];
    });
  }

  get maxTimeSlot(): number {
    let maxRange = 60;
    const rangeList = this.editingSchedule?.schedules?.map((e) => {
      if (isString(e.startTime) && isString(e.endTime)) {
        return Math.abs(timeParse(e.endTime) - timeParse(e.startTime)) / 60;
      }
      return 60;
    });
    if (rangeList?.length) {
      maxRange = Math.min(...rangeList);
    }
    return maxRange;
  }

  formatTimeSlot(timeSlot: number): string {
    const [h, m] = timeFormat(timeSlot * 60)
      .split(':')
      .map((e) => parseInt(e));
    const hours = h !== 0 ? `${this.$tc('TicketsSchedule.plural.hour', h)} ` : '';
    const minutes = m !== 0 ? this.$tc('TicketsSchedule.plural.minute', m) : '';
    return `${hours}${minutes}`;
  }

  public getTabIndex(schedule: OrgScheduleDTO): string {
    return schedule?.areaId || '';
  }

  public getTabName(schedule: OrgScheduleDTO): string {
    return schedule?.areaName || '';
  }

  isScheduleSaved(schedule?: OrgScheduleDTO): boolean {
    return !!schedule?.id;
  }

  isServiceUFSIN(schedule?: EditableSchedule): boolean {
    return !!schedule?.services?.reduce((memo, serv) => {
      return memo || serv.serviceId === '-10000000469';
    }, false);
  }

  get employeesAreSelected(): boolean {
    return !!this.editingSchedule?.employees?.length;
  }

  get servicesAreSelected(): boolean {
    return !!this.editingSchedule?.services?.length;
  }

  get scheduleIsSet(): boolean {
    return !!this.editingSchedule?.schedules?.length;
  }

  get scheduleIsSetToDelite(): boolean {
    return !!this.editingSchedule?.deleted;
  }

  get shaduleIsReadyToBeSaved(): boolean {
    return (
      this.dirty ||
      this.employeesAreSelected ||
      this.servicesAreSelected ||
      this.scheduleIsSet ||
      this.scheduleIsSetToDelite
    );
  }

  get officeTypesOptions(): Record<string, string>[] {
    return [
      {
        value: 'type1',
        label: 'Кабинет',
      },
      {
        value: 'type2',
        label: 'Кабина',
      },
      {
        value: 'type3',
        label: 'Комната',
      },
    ];
  }

  officeType: Record<string, string> | null = null;

  @Watch('editingSchedule')
  onEditingScheduleChange(editingSchedule: EditableSchedule): void {
    this.officeType =
      this.officeTypesOptions.find(({ value }) => {
        return editingSchedule.type === value;
      }) || null;
  }

  // timeSlotStep = 60;
  // stepMin = 10;

  // @Watch('editingSchedule.timeSlot')
  // watchTimeSlot(timeSlot: number): void {
  //   if (timeSlot > 60 && timeSlot < 241) {
  //     this.timeSlotStep = 30;
  //   } else if (timeSlot > 240) {
  //     this.timeSlotStep = 60;
  //   } else {
  //     this.timeSlotStep = 5;
  //   }
  //   this.stepMin = timeSlot < 30 ? 10 : 0;
  // }

  setOffiseType(officeType: Record<string, string> | null): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.type = officeType?.value || '';
    this.touch();
  }

  areaId = '';
  areaName = '';
  prevAreaId = '';
  prevAreaName = '';
  areaIdPrompt = false;
  areaIdAction: 'edit' | 'add' = 'edit';

  get areaIdslist(): string[] {
    return Object.keys(this.editableSchedulesIndex);
  }

  openEditAreaIdPrompt(areaId: string, areaName: string): void {
    this.areaIdAction = 'edit';
    this.areaIdPrompt = true;
    this.prevAreaId = areaId;
    this.prevAreaName = areaName;
    this.areaId = areaId;
    this.areaName = areaName;
  }

  openAddAreaIdPrompt(): void {
    this.areaIdAction = 'add';
    this.areaIdPrompt = true;
    this.prevAreaId = '';
    this.prevAreaName = '';
  }

  isScheduleDirty(tabIndex: string): boolean {
    const original = JSON.stringify(this.schedulesIndex[tabIndex]);
    const copy = JSON.stringify(this.editableSchedulesIndex[tabIndex]);

    return original !== copy;
  }

  public saveAreaId(areaId: string, areaName: string): void {
    areaId = areaId || areaName;
    areaName = areaName || areaId;

    switch (this.areaIdAction) {
      case 'add':
        return this.addSchedule(areaId, areaName);
      case 'edit':
        return this.editAreaId(areaId, areaName);
    }
  }

  public editAreaId(areaId: string, areaName: string): void {
    const schedule = this.editableSchedulesIndex[this.prevAreaId];
    schedule.areaId = areaId;
    schedule.areaName = areaName;
    schedule.prevAreaId = this.prevAreaId;
    schedule.prevAreaName = this.prevAreaName;
    this.mainTab = areaId;
    this.prevAreaId = this.areaId = '';
    this.touch();
  }

  public addSchedule(areaId: string, areaName: string): void {
    if (!areaId) {
      notifyError(this.$t('TicketsSchedule.areaIdIsRequired').toString());
    }
    this.schedules.push(
      new OrgScheduleModel({
        areaId,
        areaName,
      })
    );
    this.mainTab = areaId;
    this.areaId = '';
    this.areaName = '';
    this.touch();
  }

  public restoreSchedule(areaId: string): void {
    delete this.editableSchedulesIndex[areaId].deleted;

    this.mainTab = areaId;
  }

  public resetSchedules(): void {
    this.setSchedules(this.organizationFromServer?.orgSchedules);
    this.clear();
  }

  public deleteSchedule(areaId: string): void {
    this.editableSchedulesIndex[areaId].deleted = true;
    this.touch();

    const listOfAvelible = this.schedules.filter(
      (schedule) => !has(schedule, 'deleted')
    );
    this.schedules.forEach((schedule, index) => {
      if (schedule.areaId !== areaId) return;

      const activeTabId = this.mainTab !== areaId && this.mainTab;
      const siblingTabId = this.getTabIndex(
        listOfAvelible[index - 1] || listOfAvelible[index + 1]
      );
      this.mainTab = activeTabId || siblingTabId;
    });
  }

  private filterDeleted(schedules: EditableSchedule[]): EditableSchedule[] {
    return schedules.filter(({ deleted }) => !deleted);
  }

  get editableSchedulesIndex(): Record<string, EditableSchedule> {
    return this.schedules.reduce((index, schedule) => {
      const indexKey = this.getTabIndex(schedule);
      index[indexKey] = schedule;
      return index;
    }, {} as Record<string, EditableSchedule>);
  }

  get schedulesIndex(): Record<string, OrgScheduleDTO> {
    const schedules = this.organizationFromServer?.orgSchedules || [];
    return schedules
      .map((s) => {
        return new OrgScheduleModel(s);
      })
      .reduce((index, schedule) => {
        const indexKey = this.getTabIndex(schedule);
        index[indexKey] = schedule;
        return index;
      }, {} as Record<string, OrgScheduleDTO>);
  }

  // eslint-disable-next-line camelcase
  get currentUser(): { domain_id: string; ogrn: string } {
    return this.$store.getters['oidcClient/currentUser'];
  }

  get isOrganizationAdmin(): boolean {
    return this.$AC.haveScope(USER_SCOPES.PREORDER_ADMIN);
  }

  async fetchNSCIInfo(): Promise<void> {
    const okato = this.organization?.okato;
    try {
      const { records: fpsDocks } = await searchRegistryBy<FpsDocument>(
        'PGS_FPS',
        1,
        1,
        `OKATO==${okato}`
      );
      this.fpsOrganization = fpsDocks.pop() || null;
      this.fpsCode = this.fpsOrganization?.code || '';
    } catch (e) {
      console.warn('[tickets/schedule] Не удалось получить словарь PGS_FPS');
    }
  }

  async fetch(): Promise<void> {
    this.loading = true;
    try {
      this.organization = await this.loadOrganization();
      if (this.organization) {
        this.region = await this.loadRegion(this.organization);
      }
      await this.fetchNSCIInfo();
      const organizationId = this.currentUser.ogrn;
      this.organizationFromServer = await preorderService.getOrgSchedule(
        organizationId
      );

      this.setSchedules(this.organizationFromServer?.orgSchedules);

      const serviceId = get(this.schedules, '[0].services[0].serviceId');
      this.slots = await preorderService.getSlots({
        organizationId,
        serviceId,
      });
    } catch (error) {
      if (Axios.isAxiosError(error)) {
        if (error.response?.status !== 404) {
          notifyError(
            this.$t(
              `TicketsSchedule.errors.${error.response?.status}`
            ).toString()
          );
        }
      } else if (error instanceof Error) {
        notifyError(error.message);
      }
    }
    this.loading = false;
  }

  created(): void {
    this.clear();
    this.fetchCalendar();
  }

  async loadRegion(org: Organization): Promise<Region | null> {
    try {
      let regionId = '';
      if (org.schema === 'Organization') {
        regionId = org.region || '';
      } else if (org.parent?.schema === 'Organization') {
        regionId = org.parent?.region || '';
      }
      if (!regionId) throw new Error('Не удалось найти regionId');

      const { data: region } = await new RegionApi().getRegion(regionId);
      return region as Region;
    } catch (error) {
      console.error(error);
      return null;
    }
  }

  async loadServicesOfOrganization(domainId: string): Promise<Service[]> {
    try {
      const { data } = await organizationApi.getOrganization(
        domainId,
        'services'
      );

      return get(data, 'services', []);
    } catch (error) {
      if (Axios.isAxiosError(error)) {
        notifyError(
          this.$t(
            `TicketsSchedule.error.organization-${error.response?.status}`
          ).toString()
        );
      } else if (error instanceof Error) {
        notifyError(error.message);
      }
      return [];
    }
  }

  async loadOrganization(): Promise<ExtendedOrganization | null> {
    try {
      const organization: ExtendedOrganization = await this.$store.dispatch(
        'idm/organizations/getOrganization',
        {
          id: this.currentUser.domain_id,
          opts: {
            expand: ['parent'],
          },
        }
      );

      organization.services = await this.loadServicesOfOrganization(
        this.currentUser.domain_id
      );
      return organization;
    } catch (error) {
      if (Axios.isAxiosError(error)) {
        notifyError(
          this.$t(
            `TicketsSchedule.error.organization-${error.response?.status}`
          ).toString()
        );
      } else if (error instanceof Error) {
        notifyError(error.message);
      }
      return null;
    }
  }

  setSchedules(
    schedules: OrgScheduleDTO[] = new Array<OrgScheduleDTO>()
  ): void {
    this.schedules = schedules.map((s) => {
      return new OrgScheduleModel(s);
    });
    const current = this.schedulesIndex[this.mainTab];
    const first = schedules[0];
    if (!current && first) {
      this.mainTab = this.getTabIndex(first);
    }
  }

  setSchedule(schedule: ScheduleDTO[]): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.schedules = schedule;
    this.touch();
  }

  setTimeSlot(timeSlot: number): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.timeSlot = timeSlot;
    this.touch();
  }

  setCapacity(capacity: number): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.capacity = capacity;
    this.touch();
  }

  setDaysRange(endDay: number): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.endDay = endDay;
    this.touch();
  }

  setuseProductionCalendar(useProductionCalendar: boolean): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.useProductionCalendar = useProductionCalendar;
    this.touch();
  }

  setDalay(delay: number): void {
    if (!this.editingSchedule) return;
    this.editingSchedule.daysDelay = delay;
    this.touch();
  }

  async saveSchedules(): Promise<void> {
    this.schedules.forEach((schedule) => {
      if (!this.isServiceUFSIN(schedule)) {
        schedule.type = '';
      }
    });
    if (!this.organization) return;

    if (this.organizationFromServer) {
      await this.updateSchedules();
    } else {
      await this.createNewSchedules();
    }
    this.dirty = false;
  }

  async updateSchedules(): Promise<void> {
    this.saving = true;
    try {
      this.organizationFromServer = await preorderService.updateOrgSchedule({
        ...this.organizationFromServer,
        domainId: this.currentUser.domain_id,
        regionId: undefined,
        orgIdFromMS: this.organization?.ogrn,
        orgSchedules: this.filterDeleted(this.schedules),
        organisationTimeZone: this.timezone,
      });

      this.setSchedules(this.organizationFromServer?.orgSchedules);

      notifySuccess(
        this.$t('TicketsSchedule.saved').toString(),
        'check_circle_outline'
      );
    } catch (error) {
      if (Axios.isAxiosError(error)) {
        if (error.response?.status !== 400) {
          notifyError(
            this.$t(
              `TicketsSchedule.errors.${error.response?.status}`
            ).toString()
          );
        } else {
          console.warn('[tickets/schedule] Не удалось обновить расписание');
        }
      } else if (error instanceof Error) {
        notifyError(error.message);
      }
    }
    this.saving = false;
  }

  async createNewSchedules(): Promise<void> {
    this.saving = true;
    try {
      this.organizationFromServer = await preorderService.createOrgSchedule({
        domainId: this.currentUser.domain_id,
        regionId: undefined,
        orgIdFromMS: this.organization?.ogrn,
        orgSchedules: this.filterDeleted(this.schedules),
        organisationTimeZone: this.timezone,
      });

      this.setSchedules(this.organizationFromServer?.orgSchedules);

      notifySuccess(
        this.$t('TicketsSchedule.created').toString(),
        'check_circle_outline'
      );
    } catch (error) {
      if (Axios.isAxiosError(error)) {
        if (error.response?.status !== 400) {
          notifyError(
            this.$t(
              `TicketsSchedule.errors.${error.response?.status}`
            ).toString()
          );
        } else {
          console.warn('[tickets/schedule] Не удалось создать расписание');
        }
      } else if (error instanceof Error) {
        notifyError(error.message);
      }
    }
    this.saving = false;
  }

  workCalendar: WorkCalendarForDomain | null = null;

  async fetchCalendar(): Promise<void> {
    this.workCalendar = new WorkCalendarForDomain(this.currentUser.domain_id);
    await this.workCalendar.fetch();
  }

  formatMarkersTimeSlot(v: number): string {
    return this.$tc('TicketsSchedule.plural.minute', v).toString();
  }

  formatMarkersDaysRange(v: number): string {
    return this.$tc('TicketsSchedule.plural.day', v).toString();
  }
}
</script>

<style scoped></style>
