use crate::connections::db_connection;
use crate::constants;
use crate::models::{
    AccommodationID, AccommodationReservationCalendar, AccommodationReservationCalendarID,
    FamilyID, NewAccommodationReservationCalendar, UserID,
};
use crate::schema::accommodations_reservations_cals_urls;
use crate::utils::string_utils::rand_str;
use crate::utils::time_utils::time;
use diesel::prelude::*;

/// Create a new reservation calendar entry
pub async fn create(
    user_id: UserID,
    family_id: FamilyID,
    accommodation_id: Option<AccommodationID>,
    name: &str,
) -> anyhow::Result<AccommodationReservationCalendar> {
    db_connection::execute(|conn| {
        let res: AccommodationReservationCalendar =
            diesel::insert_into(accommodations_reservations_cals_urls::table)
                .values(&NewAccommodationReservationCalendar {
                    family_id: family_id.0,
                    accommodation_id: accommodation_id.map(|i| i.0),
                    user_id: user_id.0,
                    name: name.to_string(),
                    token: rand_str(constants::ACCOMMODATIONS_RESERVATIONS_CALENDARS_TOKENS_LEN),
                    time_create: time() as i64,
                    time_used: time() as i64,
                })
                .get_result(conn)?;

        Ok(res)
    })
}

/// Update the information of a reservations calendar
pub async fn update(cal: &AccommodationReservationCalendar) -> anyhow::Result<()> {
    db_connection::execute(|conn| {
        diesel::update(
            accommodations_reservations_cals_urls::dsl::accommodations_reservations_cals_urls
                .filter(accommodations_reservations_cals_urls::dsl::id.eq(cal.id().0)),
        )
        .set((accommodations_reservations_cals_urls::dsl::time_used.eq(cal.time_used),))
        .execute(conn)
    })?;

    Ok(())
}

/// Get all the calendars of a user
pub async fn get_all_of_user(
    user: UserID,
    family: FamilyID,
) -> anyhow::Result<Vec<AccommodationReservationCalendar>> {
    db_connection::execute(|conn| {
        accommodations_reservations_cals_urls::table
            .filter(
                accommodations_reservations_cals_urls::dsl::family_id
                    .eq(family.0)
                    .and(accommodations_reservations_cals_urls::dsl::user_id.eq(user.0)),
            )
            .get_results(conn)
    })
}

/// Get a single calendar by its id
pub async fn get_by_id(
    id: AccommodationReservationCalendarID,
) -> anyhow::Result<AccommodationReservationCalendar> {
    db_connection::execute(|conn| {
        accommodations_reservations_cals_urls::table
            .filter(accommodations_reservations_cals_urls::dsl::id.eq(id.0))
            .get_result(conn)
    })
}

/// Get a single calendar by its token
pub async fn get_by_token(token: &str) -> anyhow::Result<AccommodationReservationCalendar> {
    db_connection::execute(|conn| {
        accommodations_reservations_cals_urls::table
            .filter(accommodations_reservations_cals_urls::dsl::token.eq(token))
            .get_result(conn)
    })
}

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

    Ok(())
}
