use super::*;
use tracing::warn;

pub(in crate::api) async fn rotate_secret(
    user: &str,
    body: RotateSecretRequest,
    expected_revision: Option<String>,
    shared: &ApiShared,
) -> Result<(CreateUserResponse, String), ApiFailure> {
    let secret = body.secret.unwrap_or_else(random_user_secret);
    if !is_valid_user_secret(&secret) {
        return Err(ApiFailure::bad_request(
            "secret must be exactly 32 hex characters",
        ));
    }

    let _guard = shared.mutation_lock.lock().await;
    let mut cfg = load_config_from_disk(&shared.config_path).await?;
    ensure_expected_revision(&shared.config_path, expected_revision.as_deref()).await?;

    if !cfg.access.users.contains_key(user) {
        return Err(ApiFailure::new(
            StatusCode::NOT_FOUND,
            "not_found",
            "User not found",
        ));
    }

    cfg.access.users.insert(user.to_string(), secret.clone());
    cfg.validate()
        .map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
    let revision =
        save_access_sections_to_disk(&shared.config_path, &cfg, &[AccessSection::Users]).await?;
    drop(_guard);

    let (detected_ip_v4, detected_ip_v6) = shared.detected_link_ips();
    let users = users_from_config(
        &cfg,
        &shared.stats,
        &shared.ip_tracker,
        detected_ip_v4,
        detected_ip_v6,
        None,
    )
    .await;
    let user_info = users
        .into_iter()
        .find(|entry| entry.username == user)
        .ok_or_else(|| ApiFailure::internal("failed to build updated user view"))?;

    Ok((
        CreateUserResponse {
            user: user_info,
            secret,
        },
        revision,
    ))
}

pub(in crate::api) async fn delete_user(
    user: &str,
    expected_revision: Option<String>,
    shared: &ApiShared,
) -> Result<(String, String), ApiFailure> {
    let _guard = shared.mutation_lock.lock().await;
    let mut cfg = load_config_from_disk(&shared.config_path).await?;
    ensure_expected_revision(&shared.config_path, expected_revision.as_deref()).await?;

    if !cfg.access.users.contains_key(user) {
        return Err(ApiFailure::new(
            StatusCode::NOT_FOUND,
            "not_found",
            "User not found",
        ));
    }
    if cfg.access.users.len() <= 1 {
        return Err(ApiFailure::new(
            StatusCode::CONFLICT,
            "last_user_forbidden",
            "Cannot delete the last configured user",
        ));
    }

    let mut touched_sections = vec![AccessSection::Users];
    cfg.access.users.remove(user);
    if cfg.access.user_enabled.remove(user).is_some() {
        touched_sections.push(AccessSection::UserEnabled);
    }
    if cfg.access.user_ad_tags.remove(user).is_some() {
        touched_sections.push(AccessSection::UserAdTags);
    }
    if cfg.access.user_max_tcp_conns.remove(user).is_some() {
        touched_sections.push(AccessSection::UserMaxTcpConns);
    }
    if cfg.access.user_expirations.remove(user).is_some() {
        touched_sections.push(AccessSection::UserExpirations);
    }
    if cfg.access.user_data_quota.remove(user).is_some() {
        touched_sections.push(AccessSection::UserDataQuota);
    }
    if cfg.access.user_rate_limits.remove(user).is_some() {
        touched_sections.push(AccessSection::UserRateLimits);
    }
    if cfg.access.user_max_unique_ips.remove(user).is_some() {
        touched_sections.push(AccessSection::UserMaxUniqueIps);
    }

    cfg.validate()
        .map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
    let revision =
        save_access_sections_to_disk(&shared.config_path, &cfg, &touched_sections).await?;
    let configured_users = cfg.access.users.keys().cloned().collect();
    if let Err(error) = shared
        .quota_state
        .remove_user(&configured_users, user)
        .await
    {
        warn!(
            user,
            error = %error,
            "Deleted user quota checkpoint cleanup will be reconciled on restart"
        );
    }
    drop(_guard);
    shared.ip_tracker.remove_user_limit(user).await;
    shared.ip_tracker.clear_user_ips(user).await;

    Ok((user.to_string(), revision))
}
