import { APIClient } from "../ApiClient";
import { Family } from "../FamilyApi";
import { Accommodation } from "./AccommodationListApi";

export enum ValidateResaResult {
  Success,
  Error,
  Conflict,
}

export interface AccommodationReservation {
  id: number;
  family_id: number;
  accommodation_id: number;
  user_id: number;
  time_create: number;
  time_update: number;
  reservation_start: number;
  reservation_end: number;
  validated?: boolean;
}

export class AccommodationsReservationsList {
  private list: AccommodationReservation[];
  private map: Map<number, AccommodationReservation>;

  constructor(list: AccommodationReservation[]) {
    this.list = list;
    this.map = new Map();

    for (const m of list) {
      this.map.set(m.id, m);
    }

    this.list.sort((a, b) => a.reservation_start - b.reservation_start);
  }

  public get isEmpty(): boolean {
    return this.list.length === 0;
  }

  public get size(): number {
    return this.list.length;
  }

  public get fullList(): AccommodationReservation[] {
    return this.list;
  }

  filter(
    predicate: (m: AccommodationReservation) => boolean
  ): AccommodationReservation[] {
    return this.list.filter(predicate);
  }

  forAccommodation(id: number): AccommodationReservation[] {
    return this.filter((a) => a.accommodation_id === id);
  }

  get(id: number): AccommodationReservation | undefined {
    return this.map.get(id);
  }
}

export interface UpdateAccommodationReservation {
  start: number;
  end: number;
  accommodation_id: number;
  reservation_id?: number;
}

export class AccommodationsReservationsApi {
  /**
   * Create a new reservation
   */
  static async Create(
    family: Family,
    reservation: UpdateAccommodationReservation
  ): Promise<AccommodationReservation> {
    return (
      await APIClient.exec({
        method: "POST",
        uri: `/family/${family.family_id}/accommodations/reservations/accommodation/${reservation.accommodation_id}/create`,
        jsonData: {
          start: reservation.start,
          end: reservation.end,
        },
      })
    ).data;
  }

  /**
   * Get the entire list of accommodations of a family
   */
  static async FullListOfFamily(
    family: Family
  ): Promise<AccommodationsReservationsList> {
    const data = (
      await APIClient.exec({
        method: "GET",
        uri: `/family/${family.family_id}/accommodations/reservations/full_list`,
      })
    ).data;

    return new AccommodationsReservationsList(data);
  }

  /**
   * Get the reservations of a given time interval for an accommodation
   */
  static async ReservationsForInterval(
    family: Family,
    accommodation: Accommodation,
    start: number,
    end: number
  ): Promise<AccommodationsReservationsList> {
    const data = (
      await APIClient.exec({
        method: "GET",
        uri: `/family/${family.family_id}/accommodations/reservations/accommodation/${accommodation.id}/for_interval?start=${start}&end=${end}`,
      })
    ).data;

    return new AccommodationsReservationsList(data);
  }

  /**
   * Update a reservation
   */
  static async Update(
    family: Family,
    r: UpdateAccommodationReservation
  ): Promise<void> {
    await APIClient.exec({
      method: "PATCH",
      uri: `/family/${family.family_id}/accommodations/reservation/${r.reservation_id}`,
      jsonData: {
        start: r.start,
        end: r.end,
      },
    });
  }

  /**
   * Delete a reservation
   */
  static async Delete(r: AccommodationReservation): Promise<void> {
    await APIClient.exec({
      method: "DELETE",
      uri: `/family/${r.family_id}/accommodations/reservation/${r.id}`,
    });
  }

  /**
   * Validate or reject a reservation request
   */
  static async Validate(
    r: AccommodationReservation,
    accept: boolean
  ): Promise<ValidateResaResult> {
    const res = await APIClient.exec({
      method: "POST",
      uri: `/family/${r.family_id}/accommodations/reservation/${r.id}/validate`,
      jsonData: {
        validate: accept,
      },
      allowFail: true,
    });

    if (res.status >= 200 && res.status <= 299)
      return ValidateResaResult.Success;
    if (res.status === 409) return ValidateResaResult.Conflict;
    return ValidateResaResult.Error;
  }
}
