use crate::constants::{FAMILY_INVITATION_CODE_LEN, StaticConstraints};
use crate::controllers::HttpResult;
use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership};
use crate::models::{FamilyMembership, UserID};
use crate::services::login_token_service::LoginToken;
use crate::services::rate_limiter_service::RatedAction;
use crate::services::{families_service, rate_limiter_service};
use crate::utils::string_utils::rand_str;
use actix_remote_ip::RemoteIP;
use actix_web::{HttpResponse, web};

#[derive(Debug, serde::Deserialize)]
pub struct CreateFamilyReq {
    name: String,
}

/// Create a new family
pub async fn create(req: web::Json<CreateFamilyReq>, token: LoginToken) -> HttpResult {
    if !StaticConstraints::default()
        .family_name_len
        .validate(&req.name)
    {
        return Ok(HttpResponse::BadRequest().body("Invalid family name!"));
    }

    let family = families_service::create(&req.name, token.user_id).await?;

    Ok(HttpResponse::Created().json(family))
}

#[derive(Debug, serde::Deserialize)]
pub struct JoinFamilyReq {
    code: String,
}

/// Join a family
pub async fn join(
    remote_ip: RemoteIP,
    req: web::Json<JoinFamilyReq>,
    token: LoginToken,
) -> HttpResult {
    // Rate limiting
    if rate_limiter_service::should_block_action(remote_ip.0, RatedAction::JoinFamily).await? {
        return Ok(HttpResponse::TooManyRequests().finish());
    }
    rate_limiter_service::record_action(remote_ip.0, RatedAction::JoinFamily).await?;

    let family = match families_service::get_by_invitation_code(&req.code).await {
        Ok(f) => f,
        Err(e) => {
            log::error!("Could not find family by invitation code! {e}");
            return Ok(HttpResponse::NotFound().finish());
        }
    };

    if families_service::is_member(family.id(), token.user_id).await? {
        log::error!(
            "Could not add {:?} to family {:?} because it is already a member of the family!",
            token.user_id,
            family.id()
        );
        return Ok(HttpResponse::Conflict().finish());
    }

    families_service::add_member(family.id(), token.user_id, false).await?;

    Ok(HttpResponse::Accepted().finish())
}

/// Get the list of families of the user
pub async fn list(token: LoginToken) -> HttpResult {
    Ok(
        HttpResponse::Ok()
            .json(families_service::get_user_family_memberships(token.user_id).await?),
    )
}

#[derive(serde::Serialize)]
struct RichFamilyInfo {
    #[serde(flatten)]
    membership: FamilyMembership,
    enable_genealogy: bool,
    enable_accommodations: bool,
    disable_couple_photos: bool,
}

/// Get the information of a single family
pub async fn single_info(f: FamilyInPath) -> HttpResult {
    let membership = families_service::get_family_membership(f.family_id(), f.user_id()).await?;
    let family = families_service::get_by_id(f.family_id()).await?;
    Ok(HttpResponse::Ok().json(RichFamilyInfo {
        membership,
        enable_genealogy: family.enable_genealogy,
        enable_accommodations: family.enable_accommodations,
        disable_couple_photos: family.disable_couple_photos,
    }))
}

/// Attempt to leave a family
pub async fn leave(f: FamilyInPath) -> HttpResult {
    families_service::remove_membership(f.family_id(), f.user_id()).await?;

    Ok(HttpResponse::Accepted().finish())
}

#[derive(serde::Deserialize)]
pub struct UpdateFamilyBody {
    name: Option<String>,
    enable_genealogy: Option<bool>,
    enable_accommodations: Option<bool>,
    disable_couple_photos: Option<bool>,
}

/// Update a family
pub async fn update(
    f: FamilyInPathWithAdminMembership,
    req: web::Json<UpdateFamilyBody>,
) -> HttpResult {
    let mut family = families_service::get_by_id(f.family_id()).await?;

    if let Some(name) = &req.name {
        if !StaticConstraints::default().family_name_len.validate(name) {
            return Ok(HttpResponse::BadRequest().body("Invalid family name!"));
        }

        family.name = name.to_string();
    }

    if let Some(enable_genealogy) = req.enable_genealogy {
        family.enable_genealogy = enable_genealogy;
    }

    if let Some(enable_accommodations) = req.enable_accommodations {
        family.enable_accommodations = enable_accommodations;
    }

    if let Some(disable_couple_photos) = req.disable_couple_photos {
        family.disable_couple_photos = disable_couple_photos;
    }

    families_service::update_family(&family).await?;

    log::info!("User {:?} updated family {:?}", f.user_id(), f.family_id());

    Ok(HttpResponse::Accepted().finish())
}

/// Delete a family
pub async fn delete(f: FamilyInPathWithAdminMembership) -> HttpResult {
    families_service::delete_family(f.family_id()).await?;
    log::info!("User {:?} deleted family {:?}", f.user_id(), f.family_id());

    Ok(HttpResponse::Accepted().finish())
}

/// Renew (change) invitation code
pub async fn renew_invitation_code(f: FamilyInPathWithAdminMembership) -> HttpResult {
    let mut family = families_service::get_by_id(f.family_id()).await?;
    family.invitation_code = rand_str(FAMILY_INVITATION_CODE_LEN);
    families_service::update_family(&family).await?;

    log::info!(
        "User {:?} changed family {:?} invitation code",
        f.user_id(),
        f.family_id()
    );

    Ok(HttpResponse::Accepted().finish())
}

/// Get the list of users who belongs to a family
pub async fn users(f: FamilyInPath) -> HttpResult {
    Ok(HttpResponse::Ok().json(families_service::get_memberships_of_family(f.family_id()).await?))
}

#[derive(serde::Deserialize)]
pub struct UserIdInPath {
    user_id: UserID,
}

#[derive(serde::Deserialize)]
pub struct UpdateMembershipBody {
    is_admin: bool,
}

/// Update a membership
pub async fn update_membership(
    f: FamilyInPathWithAdminMembership,
    path: web::Path<UserIdInPath>,
    req: web::Json<UpdateMembershipBody>,
) -> HttpResult {
    // An admin can not update his own membership
    if path.user_id == f.user_id() {
        return Ok(HttpResponse::Conflict().body("You cannot update your own membership!"));
    }

    let mut membership = families_service::get_membership(f.family_id(), path.user_id).await?;
    membership.is_admin = req.is_admin;
    families_service::update_membership(&membership).await?;

    log::info!(
        "User {:?} updated the membership of user {:?} in the family {:?}",
        f.user_id(),
        path.user_id,
        f.family_id()
    );

    Ok(HttpResponse::Accepted().finish())
}

/// Delete a family membership
pub async fn delete_membership(
    f: FamilyInPathWithAdminMembership,
    path: web::Path<UserIdInPath>,
) -> HttpResult {
    // An admin can not remove his own membership
    if path.user_id == f.user_id() {
        return Ok(HttpResponse::Conflict().body("You cannot remove your own membership!"));
    }

    families_service::remove_membership(f.family_id(), path.user_id).await?;

    log::info!(
        "User {:?} removed the membership of user {:?} in the family {:?}",
        f.user_id(),
        path.user_id,
        f.family_id()
    );

    Ok(HttpResponse::Accepted().finish())
}
