import { Component, Vue } from 'vue-property-decorator';
import QColumn from '@/types/QColumn';
import TicketsSearchBar from './search-bar.vue';
import {
  TicketsService,
  TicketFindRequestParams,
} from '@services/tickets-service';
import { TicketsFilter } from './search-bar';
import {
  convertDateFormat,
  convertDateFormatWithoutLocale,
  doMathWithDate,
  getCurrentValueFromDate,
  isPastDate,
  parseDateFromString,
} from '@/utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import groupBy from 'lodash/groupBy';
import { ServiceApi } from '@openapi/registry-service';
import { notifyError } from '@utils/notify';

const serviceApi = new ServiceApi();

/**
 * Компонент для отображения списка талонов предварительной записи на услуги
 * Включает в себя два представления
 * Календарь - вывод сетки календаря, подсветка занятых дней, вывод списка талонов дня справа и переходы по талонам.
 *  Заранее талоны группируются по дням
 * Список - простой список талонов с фильтрами. Манипуляции с фильтрами в последующем не оказывают влияния на данные календаря
 */
@Component({
  components: {
    TicketsSearchBar,
  },
})
export default class Tickets extends Vue {
  public model: any = {
    loading: false,
    pagination: {
      descending: true,
      rowsPerPage: 10,
      sortBy: 'modified',
      page: 1,
    },
    records: [],
  };

  private pending = 0;

  /**
   * для перерисовки формы поиска при обновлении
   */
  public initKey = 1;

  public beginDate = '';
  public endDate = '';
  /**
   * Все талоны с группировкой по дню в формате DD.MM.YYYY
   */
  public ticketsList: { [date: string]: CalendarTicket[] } = {};
  /**
   * Талоны выбраного дня
   */
  public activeTicketsList: CalendarTicket[] = [];

  public viewCalendar = true;

  public selectedDate = convertDateFormat(new Date(), '', DateFormat.date);
  public selectedMonth: string | null = null;
  /**
   * локальное хранение наименований услуг
   */
  public servicesCache: { [tnsTargetId: string]: string } = {};

  public columns: QColumn[] = [
    {
      align: 'left',
      field: (val: any) => val,
      format: () => this.$t('tickets.actions.openTicketForm'),
      handler: (id: string) => {
        this.openTicketCardPage(id);
      },
      handlerField: 'bookId',
      label: this.$t('tickets.columns.bookId'),
      name: 'bookId',
      required: true,
      sortable: false,
    },
    {
      align: 'left',
      field: (val: any) => val,
      formatField: 'service',
      handler: (id: string) => {
        this.$router.push({
          name: 'services',
          params: {
            id,
          },
        });
      },
      handlerField: 'service',
      label: this.$t('tickets.columns.service'),
      name: 'service',
      required: true,
      sortable: false,
    },
    {
      align: 'left',
      field: 'fullname',
      label: this.$t('tickets.columns.employee'),
      name: 'fullname',
      required: true,
      classes: ['registries-table__descr-col'],
      sortable: true,
    },
    {
      align: 'left',
      field: 'visitTime',
      format: (val: any) => (val ? convertDateFormatWithoutLocale(val) : ''),
      label: this.$t('tickets.columns.date'),
      name: 'visitTime',
      required: true,
      classes: ['registries-table__descr-col'],
      sortable: true,
    },
  ];

  public get monthData() {
    const selectedDate = parseDateFromString(
      <string>this.selectedMonth,
      DateFormat.yearMonth
    ) as Date;
    const weeks = this.getMonthDays(
      getCurrentValueFromDate('year', selectedDate),
      getCurrentValueFromDate('month', selectedDate)
    );
    return weeks;
  }

  public get translateMonth(): string {
    return convertDateFormat(
      this.selectedMonth,
      DateFormat.yearMonth,
      DateFormat.fullMonth
    );
  }

  public get translateYear(): string {
    return convertDateFormat(
      this.selectedMonth,
      DateFormat.yearMonth,
      DateFormat.fullYear
    );
  }

  public get sort(): string {
    return `${this.model.pagination.descending ? '-' : '+'}${
      this.model.pagination.sortBy
    }`;
  }

  public currentMonth() {
    return convertDateFormat(new Date(), '', DateFormat.yearMonth);
  }

  public prevMonth() {
    const parsed = parseDateFromString(
      <string>this.selectedMonth,
      DateFormat.yearMonth
    ) as Date;
    this.selectedMonth = convertDateFormat(
      doMathWithDate('subMonths', parsed, 1),
      '',
      DateFormat.yearMonth
    );
  }

  public nextMonth() {
    const parsed = parseDateFromString(
      <string>this.selectedMonth,
      DateFormat.yearMonth
    ) as Date;
    this.selectedMonth = convertDateFormat(
      doMathWithDate('addMonths', parsed, 1),
      '',
      DateFormat.yearMonth
    );
  }

  // @Watch('viewCalendar')
  // private pageViewChanged() {
  //   this.fetch();
  // }

  public created() {
    this.init();
    this.selectedMonth = this.currentMonth();
  }

  public clickDay(date: string) {
    this.activeTicketsList = this.ticketsList[date];
  }

  public openTicketCardPage(bookId: string) {
    this.$router.push({
      name: 'cards',
      params: {
        id: bookId,
        path: 'tickets',
      },
      query: {
        active: 'index',
        components: 'index',
      },
    });
  }

  private getMonthDays(year: number, monthIndex: number): CalendarDay[][] {
    const names = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
    const ruIndexes = [6, 0, 1, 2, 3, 4, 5];
    const date = new Date(year, monthIndex, 1);
    const weeks = [];
    let week: CalendarDay[] = [];
    while (date.getMonth() === monthIndex) {
      if (date.getDay() === 1) {
        weeks.push(week);
        week = [];
      }
      week.push({
        dayOfWeek: ruIndexes[date.getDay()],
        dayName: names[date.getDay()],
        dayOfMonth: date.getDate(),
        date: convertDateFormat(date, '', DateFormat.date),
      });
      date.setDate(date.getDate() + 1);
    }
    weeks.push(week);
    return weeks.filter((week) => week.length > 0);
  }

  private init(val: any = null): void {
    // this.initKey++; // сброс формы поиска

    if (val?.pagination) {
      this.model.pagination.descending = val.pagination.descending;
      this.model.pagination.rowsPerPage = val.pagination.rowsPerPage;
      this.model.pagination.sortBy = val.pagination.sortBy;
      this.model.pagination.page = val.pagination.page;
    }

    const pagination = {
      sort: '',
    };

    if (this.sort) {
      pagination.sort = this.sort;
    }
    this.fetch();
  }

  private async fetch(filter?: TicketsFilter) {
    const currentUser = this.$store.getters['oidcClient/currentUser'];

    if (this.pending) {
      this.pending++;
      return;
    }
    this.pending = 1;

    this.model.loading = true;

    const ticketFindParams: TicketFindRequestParams = {
      org: currentUser.ogrn,
    };

    try {
      if (filter) {
        if (filter.employee?.length) {
          ticketFindParams.employee = filter.employee[0].value;
        }
        if (filter.from) {
          ticketFindParams.startDate = convertDateFormat(
            filter.from,
            '',
            DateFormat.backend
          );
        }
        if (filter.to) {
          ticketFindParams.endDate = convertDateFormat(
            filter.to,
            '',
            DateFormat.backend
          );
        }
      }

      const ticketsClient = await TicketsService.handler({ useAuth: true });
      const recordsData = await ticketsClient.ticketFind(ticketFindParams);
      this.processTicketsData(recordsData);
    } catch (err) {
      // первая ошибка игнорится в пользу уточняющего поиска
      try {
        const organization = await this.$store.dispatch(
          'idm/organizations/getOrganization',
          { id: currentUser.domain_id }
        );
        const ticketsClient = await TicketsService.handler({ useAuth: true });
        const recordsData = await ticketsClient.ticketFind({
          org: organization.tnsDepartmentId,
          startDate: ticketFindParams.startDate,
          endDate: ticketFindParams.endDate,
          employee: ticketFindParams.employee,
        });
        this.processTicketsData(recordsData);
      } catch (e) {
        console.error(e);
        notifyError(e.message.toString());
        this.model.records = [];
      }
    } finally {
      this.model.loading = false;
    }

    if (this.pending > 1) {
      this.pending = 0;
      await this.fetch(filter);
      return;
    }
    this.pending = 0;
  }

  private processTicketsData(recordsData: any) {
    const records: TicketRow[] = recordsData.map((it: any) => {
      return {
        bookId: it?.bookId,
        service: it?.timeSlot?.serviceId,
        fullname: [it?.lastName, it?.firstName].join(' '),
        visitTime: it?.timeSlot?.visitTime,
        statusMessage: it?.timeSlot?.statusMessage,
        statusCode: it?.timeSlot?.statusCode,
      };
    });

    this.model.records = records;
    this.model.pagination.rowsNumber = records.length;
    const ticketsList = recordsData.map((rec: any) => {
      return {
        id: rec.bookId,
        // todo это костыль для дат, пока их не приведут впорядок на сервере
        start: convertDateFormat(
          doMathWithDate('subHours', new Date(rec.timeSlot?.visitTime), 3),
          '',
          DateFormat.backend
        ),
        end: convertDateFormat(
          doMathWithDate('subHours', new Date(rec.timeSlot?.endVisitTime), 3),
          '',
          DateFormat.backend
        ),
        startTime: convertDateFormat(
          doMathWithDate('subHours', new Date(rec.timeSlot?.visitTime), 3),
          '',
          DateFormat.timeHours
        ),
        endTime: convertDateFormat(
          doMathWithDate('subHours', new Date(rec.timeSlot?.visitTime), 3),
          '',
          DateFormat.timeHours
        ),
        service: 'Не указана',
        serviceId: rec.timeSlot?.serviceId,
        raw: {
          ...rec,
        },
      };
    });

    // параллельное наполнение тикетов заголовками услуг
    ticketsList.forEach(async (ticket: any) => {
      const serviceName = await this.getServiceName(ticket.serviceId);
      if (serviceName) {
        this.$set(ticket, 'service', serviceName);
      }
    });

    this.ticketsList = groupBy(ticketsList, (value) => {
      return convertDateFormat(new Date(value.start), '', DateFormat.date);
    });
  }

  private async getServiceName(
    tnsTargetId: string
  ): Promise<string | undefined> {
    if (this.servicesCache[tnsTargetId]) {
      return this.servicesCache[tnsTargetId];
    }

    const serviceMatchResults = await serviceApi
      .pagingService(1, 5, `(tnsTargetId==${tnsTargetId})`)
      .then(({ data }) => data?.records || []);
    const result = serviceMatchResults?.length ? serviceMatchResults[0] : null;
    if (result) {
      this.servicesCache[tnsTargetId] = result.fullName;
      return result.fullName;
    }
  }

  private dayBeforeToday(date: string): boolean {
    const result = isPastDate(
      <Date>parseDateFromString(<string>date, DateFormat.date)
    );
    return result;
  }
}

export interface TicketRow {
  service: string;
  fullname: string;
  visitTime: string;
  bookId: string;
  statusMessage: string;
  statusCode: string;
}

export interface CalendarTicket {
  id: string;
  category: string;
  start: string;
  end: string;
  isReadOnly: boolean;
  raw: any;
  cssClass: string;
  icon?: string;
  title?: string;
  body?: string;
}

export interface CalendarDay {
  dayOfWeek: number;
  dayName: string;
  dayOfMonth: number;
  date: string;
}
