// TODO types, interfaces

import {
  ScheduleControllerApi,
  SimpleResponse,
  SlotControllerApi,
  TicketControllerApi,
  TicketDTO
} from '@openapi/preorder-pgs';
import type { SlotDTO, OrgDTO } from '@openapi/preorder-pgs/api';
import { configCreator } from './config';
import { saveBlob } from '@/utils/files';

type TicketsOptions = {
  organizationId?: string;
  serviceId: string;
  employeeId?: string;
  areaId?: string;
  orderId?: string;
  startDate?: string;
  endDate?: string;
};

interface DeleteResponce extends SimpleResponse {
  ids: string[];
}

type SlotsOptions = Pick<SlotDTO, 'organizationId' | 'serviceId' | 'slotId' | 'areaId'>

type DateRange = 'startDay' | 'endDay';

type OrgOptions = Pick<OrgDTO, 'orgRguId' | 'orgIdFromMS' | 'regionId' | 'orgSchedules' | DateRange | 'organisationTimeZone'>

class PreorderService {
  private scheduleApi: ScheduleControllerApi;
  private slotsApi: SlotControllerApi;
  private ticketsApi: TicketControllerApi;

  public async getTickets({
    organizationId,
    serviceId,
    employeeId,
    areaId,
    orderId,
    startDate,
    endDate,
  }: TicketsOptions): Promise<TicketDTO[]> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.findTicketForOrg({
      org: organizationId,
      service: serviceId,
      employee: employeeId,
      areaId,
      caseNumber: orderId,
      startDate,
      endDate,
    });
    return data;
  }

  public async declineTickets(bookIds: string[]): Promise<TicketDTO> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.declineTicketsByBookId(bookIds);
    return data;
  }

  public async getTicket(bookId: string): Promise<TicketDTO> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.getTicket(bookId);
    return data;
  }

  public async updateTicketEmployee({ bookId, employeeId }: TicketDTO): Promise<TicketDTO> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.updateTicketEmpl({ bookId, employeeId });
    return data;
  }

  public async updateTicketStatus(ticket: TicketDTO): Promise<TicketDTO> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.updateTicketStatus(ticket);
    return data;
  }

  public async finishTicket(ticket: TicketDTO): Promise<TicketDTO> {
    const api = await this.getConfiguredTicketsAPI();
    const { data } = await api.updateTicket(ticket);
    return data;
  }

  public async getSlots({ organizationId, serviceId, slotId, areaId }: SlotsOptions): Promise<SlotDTO[]> {
    const api = await this.getConfiguredSlotsAPI();
    const { data } = await api.getSlots({ organizationId, serviceId, slotId, areaId });
    return data;
  }

  public async deleteSlot(slotId: string): Promise<SimpleResponse> {
    const api = await this.getConfiguredSlotsAPI();
    const { data } = await api.deleteSlot(slotId);
    return data;
  }

  public async deleteSlots(slotIds: string[]): Promise<DeleteResponce> {
    const api = await this.getConfiguredSlotsAPI();
    const response = await Promise.all(
      slotIds.map(async (slotId) => {
        try {
          await api.deleteSlot(slotId);
          return [slotId];
        } catch (error) {
          return [];
        }
      })
    );
    return response.reduce(
      (res, [id]) => {
        if (id) {
          res.ids.push(id);
        }
        return res;
      },
      {
        ids: [],
      } as DeleteResponce
    );
  }

  public async downloadSlots(slotsOption: SlotDTO, defaultFileName = ''): Promise<true> {
    const api = await this.getConfiguredSlotsAPI();
    const { data: blob, headers } = await api.downloadXLS(slotsOption, {
      responseType: 'blob'
    });
    const fileName = this.parseFileNameFromHeaders(headers, defaultFileName);
    const blobType = this.parseContentType(headers);

    saveBlob({
      blob,
      blobType,
      fileName
    })

    return true;
  }

  public async getOrgSchedule(orgIdByMs: string): Promise<OrgDTO> {
    const api = await this.getConfiguredScheduleAPI();
    const { data } = await api.getOrgSchedule(orgIdByMs);
    return data;
  }

  public async createOrgSchedule(organization: OrgDTO): Promise<OrgDTO> {
    const api = await this.getConfiguredScheduleAPI();
    const { data } = await api.createOrgSchedule(organization);
    return data;
  }

  public async updateOrgSchedule(organization: OrgDTO): Promise<OrgDTO> {
    const api = await this.getConfiguredScheduleAPI();
    const { data } = await api.updateOrgSchedule(organization);
    return data;
  }

  public async updateOrgTimezone(organization: Pick<OrgDTO, 'organisationTimeZone' | 'orgIdFromMS'>): Promise<OrgDTO> {
    const api = await this.getConfiguredScheduleAPI();
    const { data } = await api.updateOrgTimezone(organization);
    return data;
  }

  private async getConfiguredSlotsAPI(): Promise<SlotControllerApi> {
    if (this.slotsApi) return Promise.resolve(this.slotsApi);
    const config = await configCreator();
    return new SlotControllerApi(config);
  }

  private getConfiguredScheduleAPI(): Promise<ScheduleControllerApi> {
    if (this.scheduleApi) return Promise.resolve(this.scheduleApi);
    return configCreator().then((config) => {
      return new ScheduleControllerApi(config);
    });
  }

  private getConfiguredTicketsAPI(): Promise<TicketControllerApi> {
    if (this.ticketsApi) return Promise.resolve(this.ticketsApi);
    return configCreator().then((config) => {
      return new TicketControllerApi(config);
    });
  }

  private parseFileNameFromHeaders(headers: {'content-disposition': string}, defaultName = ''): string {
    const [, fileName] = headers['content-disposition'].match(/(?:attachment; filename=(.*$))/) || [];
    return fileName || defaultName;
  }

  private parseContentType(headers: {'content-type': string}): string {
    return headers['content-type'];
  }
}

export default new PreorderService();
