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

fn group_to_json(group: &db::groups::Group) -> serde_json::Value {
    serde_json::json!({
        "GroupName": group.group_name,
        "Description": group.description,
        "Precedence": group.precedence,
        "CreationDate": epoch_secs(&group.created_at),
        "LastModifiedDate": epoch_secs(&group.updated_at),
    })
}

pub async fn create_group(
    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 group_name = body["GroupName"].as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("GroupName is required".into()))?;
    let description = body["Description"].as_str();
    let precedence = body["Precedence"].as_i64().map(|v| v as i32);

    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")))?;

    let group = db::groups::create(&state.db, pool_id, group_name, description, precedence)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let response = serde_json::json!({ "Group": group_to_json(&group) });
    Ok(axum::Json(response).into_response())
}

pub async fn admin_add_user_to_group(
    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 group_name = body["GroupName"].as_str()
        .ok_or_else(|| CognitoError::InvalidParameter("GroupName is required".into()))?;

    db::groups::add_user_to_group(&state.db, pool_id, username, group_name)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

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

pub async fn admin_list_groups_for_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 groups = db::groups::list_groups_for_user(&state.db, pool_id, username)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let group_list: Vec<serde_json::Value> = groups.iter().map(group_to_json).collect();
    let response = serde_json::json!({ "Groups": group_list });
    Ok(axum::Json(response).into_response())
}

pub async fn list_groups(
    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 groups = db::groups::list(&state.db, pool_id, limit)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let group_list: Vec<serde_json::Value> = groups.iter().map(group_to_json).collect();
    let response = serde_json::json!({ "Groups": group_list });
    Ok(axum::Json(response).into_response())
}
