use crate::db;
use crate::db::users::{User, UserAttribute};
use crate::errors::CognitoError;
use crate::operations::epoch_secs;
use crate::server::AppState;
use axum::response::{IntoResponse, Response};
use std::sync::Arc;

fn user_to_json(user: &User) -> serde_json::Value {
    serde_json::json!({
        "Username": user.username,
        "UserAttributes": user.attributes,
        "UserCreateDate": epoch_secs(&user.created_at),
        "UserLastModifiedDate": epoch_secs(&user.updated_at),
        "Enabled": user.enabled,
        "UserStatus": user.status,
    })
}

pub async fn admin_create_user(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let username = body["Username"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Username is required".into()))?;

    // Verify pool exists
    db::pools::get(&state.db, pool_id)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?
        .ok_or_else(|| CognitoError::ResourceNotFound(format!("User pool {pool_id} not found")))?;

    // Hash optional temporary password
    let password_hash = match body["TemporaryPassword"].as_str() {
        Some(pw) => Some(
            db::users::hash_password(pw)
                .map_err(|e| CognitoError::InternalError(e.to_string()))?,
        ),
        None => None,
    };

    // Parse user attributes from request
    let attributes: Vec<UserAttribute> = match body["UserAttributes"].as_array() {
        Some(arr) => arr
            .iter()
            .filter_map(|a| {
                let name = a["Name"].as_str()?;
                let value = a["Value"].as_str()?;
                Some(UserAttribute {
                    name: name.to_string(),
                    value: value.to_string(),
                })
            })
            .collect(),
        None => vec![],
    };

    let user = db::users::create(
        &state.db,
        pool_id,
        username,
        password_hash.as_deref(),
        "FORCE_CHANGE_PASSWORD",
        &attributes,
    )
    .map_err(|e| {
        if e.to_string().contains("UNIQUE constraint failed")
            || e.to_string().contains("PRIMARY KEY")
        {
            CognitoError::UsernameExists(format!("User account already exists for {username}"))
        } else {
            CognitoError::InternalError(e.to_string())
        }
    })?;

    let response = serde_json::json!({
        "User": user_to_json(&user),
    });
    Ok(axum::Json(response).into_response())
}

pub async fn admin_get_user(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let username = body["Username"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Username is required".into()))?;

    let user = db::users::get(&state.db, pool_id, username)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?
        .ok_or_else(|| CognitoError::UserNotFound(format!("User {username} not found")))?;

    Ok(axum::Json(user_to_json(&user)).into_response())
}

pub async fn admin_delete_user(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let username = body["Username"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Username is required".into()))?;

    let deleted = db::users::delete(&state.db, pool_id, username)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    if !deleted {
        return Err(CognitoError::UserNotFound(format!(
            "User {username} not found"
        )));
    }

    Ok(axum::Json(serde_json::json!({})).into_response())
}

pub async fn list_users(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let limit = body["Limit"].as_u64().unwrap_or(60) as usize;

    let users = db::users::list(&state.db, pool_id, limit)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let user_list: Vec<serde_json::Value> = users.iter().map(|u| user_to_json(u)).collect();

    let response = serde_json::json!({ "Users": user_list });
    Ok(axum::Json(response).into_response())
}

pub async fn admin_set_user_password(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let username = body["Username"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Username is required".into()))?;

    let password = body["Password"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Password is required".into()))?;

    let permanent = body["Permanent"].as_bool().unwrap_or(false);

    let hash = db::users::hash_password(password)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let updated = db::users::set_password(&state.db, pool_id, username, &hash, permanent)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    if !updated {
        return Err(CognitoError::UserNotFound(format!(
            "User {username} not found"
        )));
    }

    Ok(axum::Json(serde_json::json!({})).into_response())
}

pub async fn admin_update_user_attributes(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let pool_id = body["UserPoolId"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("UserPoolId is required".into()))?;

    let username = body["Username"]
        .as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("Username is required".into()))?;

    let attrs_array = body["UserAttributes"]
        .as_array()
        .ok_or_else(|| CognitoError::InvalidParameter("UserAttributes is required".into()))?;

    let new_attrs: Vec<UserAttribute> = attrs_array
        .iter()
        .filter_map(|a| {
            let name = a["Name"].as_str()?;
            let value = a["Value"].as_str()?;
            Some(UserAttribute {
                name: name.to_string(),
                value: value.to_string(),
            })
        })
        .collect();

    let updated = db::users::update_attributes(&state.db, pool_id, username, &new_attrs)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    if !updated {
        return Err(CognitoError::UserNotFound(format!(
            "User {username} not found"
        )));
    }

    Ok(axum::Json(serde_json::json!({})).into_response())
}
