import { ViewpointApiClient } from "../core";
import type { Booking, HOLD_TYPE, Member } from "../type";

export class ViewpointBooking {
  private client: <TResponse>(
    path: string,
    init?: RequestInit,
  ) => Promise<TResponse | null>;
  constructor() {
    const baseURL = process.env.VIEWPOINT_API!;
    const token = process.env.VIEWPOINT_API_KEY!;

    if (!baseURL || !token) {
      throw new Error("Missing API or API Token");
    }
    this.client = ViewpointApiClient(baseURL, token);
  }
  async getBooking(bookingNumber: string) {
    try {
      const booking = await this.client<Booking>(`/Booking/${bookingNumber}`, {
        method: "GET",
      });
      return {
        booking,
        error: null,
      };
    } catch (err) {
      return {
        booking: null,
        error: err,
      };
    }
  }
  async createBookingHold(payload: {
    resortID: string;
    accountID: string;
    arrival: string;
    departure: string;
    unitCategoryID: number;
    holdType: HOLD_TYPE;
    holdExpiration: string;
  }): Promise<{
    holdDetails: Booking | null;
    error: unknown;
  }> {
    try {
      const body = {
        ResortID: parseInt(payload.resortID),
        AccountID: payload.accountID,
        Arrival: payload.arrival,
        Departure: payload.departure,
        UnitCategoryID: payload.unitCategoryID,
        HoldType: payload.holdType,
        HoldExpiration: payload.holdExpiration,
        InventoryGroupID: null,
      };
      const holdDetails = await this.client<Booking>(`/Booking/hold`, {
        method: "POST",
        body: JSON.stringify({ ...body }),
      });
      return {
        holdDetails,
        error: null,
      };
    } catch (err) {
      return {
        holdDetails: null,
        error: err,
      };
    }
  }

  async sendBookingConfirmation(payload: {
    bookingNumber: number;
    emails: string[];
  }): Promise<{
    confirmationResponse: string | null;
    error: unknown;
  }> {
    try {
      const confirmationResponse = await this.client<string>(
        `/Booking/${payload.bookingNumber}/sendConfirmation`,
        {
          method: "PUT",
          body: JSON.stringify({ OtherEmailIds: payload.emails }),
        },
      );
      return {
        confirmationResponse,
        error: null,
      };
    } catch (err) {
      return { confirmationResponse: null, error: err };
    }
  }

  async addTravellers(payload: {
    bookingNumber: number;
    adults: number;
    children: number;
    infants: number;
    bookingComment?: string;
    contacts: {
      memberNumber: number;
      sendConfirmation: boolean;
    }[];
  }): Promise<{
    holdDetails: Booking | null;
    error: unknown;
  }> {
    try {
      const contacts: { ContactID: string; IsPrimary: boolean }[] = [];
      const membersToSendConfirmation: Record<string, boolean> = {};
      for (const contact of payload.contacts) {
        contacts.push({
          ContactID: contact.memberNumber.toString(),
          IsPrimary: true,
        });

        membersToSendConfirmation[contact.memberNumber] =
          contact.sendConfirmation;
      }
      const body = {
        BookingNo: payload.bookingNumber,
        Adults: payload.adults,
        Children: payload.children,
        Infants: payload.infants,
        Cots: 0,
        HighChair: 0,
        BookingComment: payload.bookingComment,
        Contacts: contacts,
      };

      const holdDetails = await this.client<Booking>(`/Booking/update`, {
        method: "POST",
        body: JSON.stringify({ ...body }),
      });
      if (holdDetails?.BookingNo) {
        const sendConfimations: string[] = [];
        for (const guest of holdDetails.Guests) {
          if (
            guest.Email &&
            membersToSendConfirmation[guest.ContactID.toString()]
          ) {
            sendConfimations.push(guest.Email);
          }
        }

        if (sendConfimations.length > 0) {
          await this.sendBookingConfirmation({
            bookingNumber: holdDetails.BookingNo,
            emails: sendConfimations,
          });
        }
      }
      return {
        holdDetails,
        error: null,
      };
    } catch (err) {
      return {
        holdDetails: null,
        error: err,
      };
    }
  }
  async completeBooking(payload: {
    bookingNumber: number;
    contactID: number;
    adults: number;
    children: number;
    infants: number;
    email: string;
    special_request?: string;
    comment?: string;
    bookingSourceID: number;
    holdType: HOLD_TYPE;
    entitlements: { id: number; value: number; expiry: string }[];
  }): Promise<{ data: Booking | null; error: unknown }> {
    try {
      const body = {
        BookingNo: payload.bookingNumber,
        BookingTypeID: payload.holdType === "TARIFF" ? 111 : 1192, // THIS IS TEMPORARY
        ContactID: payload.contactID,
        Adults: payload.adults,
        Children: payload.children,
        Infants: payload.infants,
        Cots: 0,
        Highchairs: 0,
        EmailAddress: [payload.email],
        Entitlements: payload.entitlements.map((ent) => ({
          PointsEntitlement: true,
          EntitleID: ent.id,
          Value: ent.value,
          ExpiryDate: new Date(ent.expiry).toISOString(),
        })),
        SpecialRequests: payload.special_request,
        Comments: payload.comment,
        BookingSourceID: payload.bookingSourceID,
        Confirmed: true,
      };
      const data = await this.client<Booking>(`/Booking/hold/complete`, {
        method: "POST",
        body: JSON.stringify({ ...body }),
      });

      return {
        data,
        error: null,
      };
    } catch (err) {
      return {
        data: null,
        error: err,
      };
    }
  }
  async cancelBooking(
    bookingID: string,
    payload: {
      reason: string;
      invokeRules: boolean;
    },
  ) {
    try {
      await this.client(`/Booking/${bookingID}/cancel`, {
        method: "PUT",
        body: JSON.stringify({
          CancelReason: payload.reason,
          InvokeCancelRules: payload.invokeRules,
        }),
      });
      return true;
    } catch (err) {
      console.log(err);
      return false;
    }
  }

  async createExternalBooking(payload: {
    resortID: string;
    unitTypeID: number;
    externalRef: string;
    arrival: string;
    departure: string;
    accountID: string;
    contactID: number;
    adults: number;
    children: number;
    infants: number;
    special_request?: string;
    comment?: string;
    bookingTypeID: number;
    bookingSourceID: number;
    holdType: HOLD_TYPE;
    entitlements: { id: number; value: number; expiry: string }[];
    guests: Member;
  }): Promise<{ data: Booking | null; error: unknown }> {
    try {
      const body = {
        ResortID: parseInt(payload.resortID),
        UnitTypeID: payload.unitTypeID,
        ExternalBookingReference: payload.externalRef,
        Arrival: payload.arrival,
        Departure: payload.departure,
        AccountID: payload.accountID,
        ContactID: payload.contactID,
        Adults: payload.adults,
        Children: payload.children,
        Infants: payload.infants,
        Cots: 0,
        Highchairs: 0,
        HoldType: payload.holdType,
        Entitlements: payload.entitlements.map((ent) => ({
          PointsEntitlement: true,
          EntitleID: ent.id,
          Value: ent.value,
          ExpiryDate: new Date(ent.expiry).toISOString(),
        })),
        SpecialRequests: payload.special_request,
        Comments: payload.comment ?? "",
        BookingTypeID: payload.bookingTypeID,
        BookingSourceID: payload.bookingSourceID,
        Confirmed: true,
        GuestDetails: payload.guests,
      };
      const data = await this.client<Booking>(`/Booking/external/create`, {
        method: "POST",
        body: JSON.stringify({ ...body }),
      });

      return {
        data,
        error: null,
      };
    } catch (err) {
      return {
        data: null,
        error: err,
      };
    }
  }
}
