use crate::connections::db_connection;
use crate::models::{Accommodation, AccommodationID, FamilyID, NewAccommodation};
use crate::schema::accommodations_list;
use crate::utils::time_utils::time;
use diesel::prelude::*;

/// Create a new accommodation
pub async fn create(family_id: FamilyID) -> anyhow::Result<Accommodation> {
    db_connection::execute(|conn| {
        let res: Accommodation = diesel::insert_into(accommodations_list::table)
            .values(&NewAccommodation {
                family_id: family_id.0,
                name: "".to_string(),
                time_create: time() as i64,
                time_update: time() as i64,
            })
            .get_result(conn)?;

        Ok(res)
    })
}

/// Get the information of an accommodation
pub async fn get_by_id(id: AccommodationID) -> anyhow::Result<Accommodation> {
    db_connection::execute(|conn| {
        accommodations_list::table
            .filter(accommodations_list::dsl::id.eq(id.0))
            .first(conn)
    })
}

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

/// Check whether accommodation with a given id exists or not
pub async fn exists(
    family_id: FamilyID,
    accommodation_id: AccommodationID,
) -> anyhow::Result<bool> {
    db_connection::execute(|conn| {
        let count: i64 = accommodations_list::table
            .filter(
                accommodations_list::id
                    .eq(accommodation_id.0)
                    .and(accommodations_list::family_id.eq(family_id.0)),
            )
            .count()
            .get_result(conn)?;

        Ok(count != 0)
    })
}

/// Update the information of an accommodation
pub async fn update(accommodation: &mut Accommodation) -> anyhow::Result<()> {
    accommodation.time_update = time() as i64;

    db_connection::execute(|conn| {
        diesel::update(
            accommodations_list::dsl::accommodations_list
                .filter(accommodations_list::dsl::id.eq(accommodation.id().0)),
        )
        .set((
            accommodations_list::dsl::time_update.eq(accommodation.time_update),
            accommodations_list::dsl::name.eq(accommodation.name.to_string()),
            accommodations_list::dsl::need_validation.eq(accommodation.need_validation),
            accommodations_list::dsl::description.eq(accommodation.description.clone()),
            accommodations_list::dsl::color.eq(accommodation.color.clone()),
            accommodations_list::dsl::open_to_reservations.eq(accommodation.open_to_reservations),
        ))
        .execute(conn)
    })?;

    Ok(())
}

/// Delete an accommodation
pub async fn delete(accommodation: &mut Accommodation) -> anyhow::Result<()> {
    // Remove the accommodation
    db_connection::execute(|conn| {
        diesel::delete(
            accommodations_list::dsl::accommodations_list
                .filter(accommodations_list::dsl::id.eq(accommodation.id().0)),
        )
        .execute(conn)
    })?;

    Ok(())
}

/// Delete all the accommodations of a family
pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
    for mut m in get_all_of_family(family_id).await? {
        delete(&mut m).await?;
    }
    Ok(())
}
