<template>
  <widget-wrapper
    v-bind="$props"
    class="checkbox"
    v-slot="{ inputEvents }"
  >
    <div>
      <div>
        <q-btn
          :disable="delegateTypeFieldPath && !delegateType"
          @click="() => isModalOpen = true"
          color="primary"
        >
          Посмотреть свободные слоты
        </q-btn>

        <div v-if="reserveData && reserveData.reservedUntil">
            <div>
              Данные о брони:
            </div>
            <div>
              {{ reserveData.room }}
            </div>
            <div>
              {{ reserveData.date }}
            </div>
            <div>
              C {{ reserveData.timeFrom }} до {{ reserveData.timeTo }}
            </div>
            <div>
              Зарезервировано до {{ reserveData.reservedUntil }}
            </div>
            <q-btn
              @click="cancelReservation"
              color="negative"
            >
              Отменить бронь
            </q-btn>
         </div>
      </div>

      <q-dialog
        persistent
        v-model="isModalOpen"
      >
        <q-card class="q-pa-md" style="max-width: 1000px;">
          <q-card-section>
            <RoomSelect
              v-if="orgSchedule"
              :schedules="orgSchedule.orgSchedules"
              @change="changeRoom"
            />
          </q-card-section>

          <q-card-section v-if="selectedAreaId">
            <SlotsCalendar
              ref="slotsCalendarRef"
              v-if="organization && service && orgSchedule"
              :organization="organization"
              :showSelect="false"
              :workCalendar="workCalendar"
              :service="service"
              :schedule="workSchedule"
              :selected="currentTicket"
              @selectTicket="ticket => currentTicket = ticket"
              @cancelReservation="cancelReservation"
            />
          </q-card-section>

          <q-card-section v-if="reserveData">
            <div>
              Данные о брони:
            </div>
            <div>
              {{ reserveData.room }}
            </div>
            <div>
              {{ reserveData.date }}
            </div>
            <div>
              C {{ reserveData.timeFrom }} до {{ reserveData.timeTo }}
            </div>
            <template v-if="reserveData.reservedUntil">
              <div>
                Зарезервировано до {{ reserveData.reservedUntil }}
              </div>
              <q-btn
                @click="cancelReservation"
                color="negative"
              >
                Отменить бронь
              </q-btn>
            </template>
            <div v-else class="text-color-red">
              Пока что не зарезервировано
            </div>
          </q-card-section>

          <q-card-actions align="right">
            <q-btn
              @click="reserve"
              color="primary"
              :disable="isReservationDisabled"
            >
              Зарезервировать
            </q-btn>
            <q-btn
              @click="closeModal"
              color="negative"
            >
              Закрыть
            </q-btn>
          </q-card-actions>

          <q-inner-loading :showing="loading">
            <q-spinner-gears color="primary" size="50px" />
          </q-inner-loading>
        </q-card>
      </q-dialog>
    </div>
  </widget-wrapper>
</template>

<script lang="ts">
import { Component, Prop, Watch } from 'vue-property-decorator';
import { mixins } from 'vue-class-component';
import {
  ValidatedField,
  FormWidget,
  WidgetWrapper,
} from '@devsun/form-generator/dist';
import { ServiceApi, Service } from '@openapi/registry-service';
import { notifyInfo, notifyError, notifySuccess } from '@utils/notify';
import SlotsCalendar from './SlotsCalendar.vue';
import RoomSelect from './RoomSelect.vue';
import { WorkCalendarForDomain } from '@components/tickets/utils/WorkCalendarForDomain';
import { OrgScheduleDTO, ScheduleDTO, TicketDTO } from '@openapi/preorder-pgs';
import { Organization, OrganizationApi } from '@openapi/idm';
import format from 'date-fns/format';
import { TicketControllerApi, ScheduleControllerApi } from '@openapi/preorder-pgs';
import { isPastDate } from '@utils/format-date';
import get from 'lodash/get';

const organizationApi = new OrganizationApi();
const serviceApi = new ServiceApi();
const ticketsApi = new TicketControllerApi();
const scheduleControllerApi = new ScheduleControllerApi();

const dateFormat = 'dd.MM.yyyy';
const timeFormat = 'HH:mm';

@Component({
  components: {
    WidgetWrapper,
    SlotsCalendar,
    RoomSelect,
  }
})
export default class SlotSelector extends mixins(FormWidget, ValidatedField, WidgetWrapper) {
  @Prop() delegateTypeFieldPath: string;

  isModalOpen = false;
  loading = false;
  service: Service | null = null;
  workCalendar: WorkCalendarForDomain | null = null;
  orgSchedule: OrgScheduleDTO | null = null;
  selectedAreaId = '';
  workSchedule: ScheduleDTO | null = null;
  organization: Organization | null = null;

  currentTicket: any = null;

  created() {
    this.currentTicket = this.getCurrentTicket();
  }

  @Watch('isModalOpen')
  async onModalToggle(isModalOpen: boolean) {
    if (isModalOpen) {
      this.fetchCalendar();
      this.service = await this.getService();
      this.organization = await this.getOrganization();
      this.orgSchedule = await this.getOrgSchedule();
    } else {
      this.clearData();
    }
  }

  @Watch('currentTicket', { immediate: true })
  onTicketChange(): void {
    this.input();
  }

  get isReservationDisabled(): boolean {
    return !this.currentTicket || !!this.currentTicket.reservedUntil;
  }

  get delegateType(): string {
    return this.delegateTypeFieldPath && get(this.model, this.delegateTypeFieldPath, '');
  }

  get currentTicketSlot() {
    return this.currentTicket?.slots?.[0] || this.currentTicket?.timeSlot;
  }

  get currentUser() {
    return this.$store.getters['oidcClient/currentUser'];
  }

  get reserveData() {
    const slot = this.currentTicket?.slots?.[0] || this.currentTicket?.timeSlot;
    if (!slot) {
      return null;
    }
    const timeFrom = new Date(slot.visitTime);
    const timeTo = new Date(slot.endVisitTime);
    const reservedUntil = this.currentTicket.reservedUntil ? new Date(this.currentTicket.reservedUntil) : undefined;
    const onlyDigitsAndSpaces = /^(\d|\s)+$/;
    const room = slot.areaId.search(onlyDigitsAndSpaces) === 0 ? `Кабинет №${slot.areaId}` : slot.areaId;
    return {
      room,
      date: format(timeFrom, dateFormat),
      timeFrom: format(timeFrom, timeFormat),
      timeTo: format(timeTo, timeFormat),
      ...(reservedUntil && { reservedUntil: format(reservedUntil, timeFormat) }),
    };
  }

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

  async getService(): Promise<Service | null> {
    const routeServiceId = this.$route.query.serviceId;
    const currentService = this.$store.getters['formGenerator/data']?.service;
    if (!currentService && routeServiceId) {
      this.loading = true;
      try {
        const { data } = await serviceApi.getService(routeServiceId);
        return data;
      } catch (e) {
        if (e instanceof Error) {
          notifyError(e.message);
        }
      } finally {
        this.loading = false;
      }
    } else if (currentService) {
      return currentService;
    }
  }

  async getOrgSchedule(): Promise<any> {
    this.loading = true;
    try {
      const { data } = await scheduleControllerApi.getFilteredOrgSchedule({
        organizationId: this.organization.ogrn,
        serviceId: this.service.targetId,
        ...((this.delegateTypeFieldPath && this.delegateType) && { type: `type${this.delegateType}` }),
      });
      return data;
    } catch (e) {
      notifyError('Расписание не найдено');
    } finally {
      this.loading = false;
    }
  }

  async getOrganization() {
    const tmpOrg = this.$store.getters['formGenerator/data']?.organization;
    return tmpOrg.parent?.timezone ? tmpOrg : await this.getOrgWithParent(tmpOrg);
  }

  async getOrgWithParent(org): Promise<any> {
    this.loading = true;
    try {
      const { data } = await organizationApi.getOrganization(org.id, 'parent');
      return data;
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
      return null;
    } finally {
      this.loading = false;
    }
  }

  changeRoom(newRoomId: string): void {
    this.selectedAreaId = newRoomId;
    this.workSchedule = this.orgSchedule.orgSchedules
      .find((sch: ScheduleDTO) => sch.areaId === newRoomId);
  }

  input() {
    if (!this.inputEvents) {
      return;
    }

    if (!this.currentTicket) {
      this.inputEvents.input(null);
    } else if (this.currentTicket.reservedUntil) {
      this.inputEvents.input({ ...this.currentTicket,
        textData: this.reserveData,
      });
    }
  }

  async reserve(): Promise<void> {
    this.loading = true;
    try {
      const { data } = await ticketsApi.createReservedTicket({
        slotId: this.currentTicket.slots[0].slotId,
      });
      this.currentTicket = data;
      this.saveCurrentTicket(data);
      const cancelTime = format(new Date(this.currentTicket.reservedUntil), timeFormat);
      notifyInfo(`Вы зарезервировали время. ` +
        `Бронь автоматически отменится в ${cancelTime}, если не подать заявление`);
      this.closeModal();
    } catch (e) {
      if (e.response?.data?.errorMessage) {
        notifyError(e.response.data.errorMessage);
      } else if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.loading = false;
    }
  }

  get lsname() {
    return `${this.currentUser.sub}-reception-booking`;
  }

  getCurrentTicket() {
    const item = localStorage.getItem(this.lsname);
    if (!item) {
      return;
    }
    const obj = JSON.parse(item);
    const isPast = isPastDate(new Date(obj.reservedUntil))
    if (isPast) {
      this.removeCurrentTicket();
    } else {
      return obj;
    }
  }

  saveCurrentTicket(newTicket) {
    localStorage.setItem(this.lsname, JSON.stringify(newTicket));
  }

  removeCurrentTicket() {
    localStorage.removeItem(this.lsname);
  }

  confirmCancelBooking(tickets: TicketDTO[]): Promise<boolean> {
    return new Promise((resolve) => {
      if (!this.reserveData) {
        return;
      }
      this.$q
        .dialog({
          title: 'Отмена резервации',
          message: `Действительно хотите отменить резервацию `+
            `${this.reserveData.date} с ${this.reserveData.timeFrom}`+
            ` по ${this.reserveData.timeTo}`,
          cancel: true,
          persistent: true,
        })
        .onOk(() => {
          resolve(true);
        })
        .onCancel(() => {
          resolve(false);
        })
        .onDismiss(() => {
          resolve(false);
        });
    });
  }

  public async cancelReservation(afterFn): Promise<void> {
    const descision = await this.confirmCancelBooking([this.currentTicket.bookId]);
    if (!descision) return;

    this.loading = true;
    try {
      const { data } = await ticketsApi.declineTicketsByBookId([this.currentTicket.bookId]);
      this.removeCurrentTicket();
      notifySuccess('Вы успешно отменили резервацию');
      this.currentTicket = null;

      if (afterFn instanceof Function) {
        this.$nextTick(() => {
          afterFn();
        });
      }
    } catch (e) {
      if (e.response?.data?.errorMessage) {
        notifyError(e.response.data.errorMessage);
      } else if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.loading = false;
    }
  }

  closeModal() {
    this.clearData();
    this.isModalOpen = false;
  }

  clearData(): void {
    this.selectedAreaId = '';
    this.service = null;
    this.organization = null;
    this.orgSchedule = null;
    this.workCalendar = null;

    if (!this.currentTicket?.reservedUntil) {
      this.currentTicket = null;
    }
  }
}
</script>

<style scoped>

</style>