use crate::connections::db_connection;
use crate::models::{
    AccommodationID, AccommodationReservation, AccommodationReservationID, FamilyID,
    NewAccommodationReservation,
};
use crate::schema::accommodations_reservations;
use crate::utils::time_utils::time;
use diesel::prelude::*;

/// Create a new reservation
pub async fn create(new: &NewAccommodationReservation) -> anyhow::Result<AccommodationReservation> {
    db_connection::execute(|conn| {
        let res: AccommodationReservation = diesel::insert_into(accommodations_reservations::table)
            .values(new)
            .get_result(conn)?;

        Ok(res)
    })
}

/// Update a reservation
pub async fn update(r: &mut AccommodationReservation) -> anyhow::Result<()> {
    r.time_update = time() as i64;

    db_connection::execute(|conn| {
        diesel::update(
            accommodations_reservations::dsl::accommodations_reservations
                .filter(accommodations_reservations::dsl::id.eq(r.id().0)),
        )
        .set((
            accommodations_reservations::dsl::time_update.eq(r.time_update),
            accommodations_reservations::dsl::validated.eq(r.validated),
            accommodations_reservations::dsl::reservation_start.eq(r.reservation_start),
            accommodations_reservations::dsl::reservation_end.eq(r.reservation_end),
        ))
        .execute(conn)
    })?;

    Ok(())
}

/// Delete a reservation
pub async fn delete(r: AccommodationReservation) -> anyhow::Result<()> {
    // Remove the reservation
    db_connection::execute(|conn| {
        diesel::delete(
            accommodations_reservations::dsl::accommodations_reservations
                .filter(accommodations_reservations::dsl::id.eq(r.id().0)),
        )
        .execute(conn)
    })?;

    Ok(())
}

/// Get all the reservations of an accommodation
pub async fn get_all_of_accommodation(
    id: AccommodationID,
) -> anyhow::Result<Vec<AccommodationReservation>> {
    db_connection::execute(|conn| {
        accommodations_reservations::table
            .filter(accommodations_reservations::dsl::accommodation_id.eq(id.0))
            .get_results(conn)
    })
}

/// Get all the reservations of a family
pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result<Vec<AccommodationReservation>> {
    db_connection::execute(|conn| {
        accommodations_reservations::table
            .filter(accommodations_reservations::dsl::family_id.eq(id.0))
            .get_results(conn)
    })
}

/// Get a single accommodation reservation by its id
pub async fn get_by_id(id: AccommodationReservationID) -> anyhow::Result<AccommodationReservation> {
    db_connection::execute(|conn| {
        accommodations_reservations::table
            .filter(accommodations_reservations::dsl::id.eq(id.0))
            .get_result(conn)
    })
}

/// Get the reservations that are between a given interval of time for a given accommodation
pub async fn get_reservations_for_time_interval(
    id: AccommodationID,
    start: usize,
    end: usize,
) -> anyhow::Result<Vec<AccommodationReservation>> {
    db_connection::execute(|conn| {
        accommodations_reservations::table
            .filter(
                accommodations_reservations::dsl::accommodation_id
                    .eq(id.0)
                    .and(accommodations_reservations::dsl::reservation_start.lt((end) as i64))
                    .and(accommodations_reservations::dsl::reservation_end.gt((start) as i64)),
            )
            .get_results(conn)
    })
}
