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;

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

    let pool_id = db::pools::generate_pool_id(&state.config.region);
    let config = body.clone();

    let pool = db::pools::create(&state.db, &pool_id, pool_name, &config)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let response = serde_json::json!({
        "UserPool": {
            "Id": pool.id,
            "Name": pool.name,
            "CreationDate": epoch_secs(&pool.created_at),
            "LastModifiedDate": epoch_secs(&pool.updated_at),
        }
    });
    Ok(axum::Json(response).into_response())
}

pub async fn describe_user_pool(
    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 pool = 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 response = serde_json::json!({
        "UserPool": {
            "Id": pool.id,
            "Name": pool.name,
            "CreationDate": epoch_secs(&pool.created_at),
            "LastModifiedDate": epoch_secs(&pool.updated_at),
        }
    });
    Ok(axum::Json(response).into_response())
}

pub async fn list_user_pools(
    body: serde_json::Value,
    state: &Arc<AppState>,
) -> Result<Response, CognitoError> {
    let max_results = body["MaxResults"].as_u64().unwrap_or(60) as usize;

    let pools = db::pools::list(&state.db, max_results)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let pool_list: Vec<serde_json::Value> = pools
        .iter()
        .map(|p| {
            serde_json::json!({
                "Id": p.id,
                "Name": p.name,
                "CreationDate": epoch_secs(&p.created_at),
                "LastModifiedDate": epoch_secs(&p.updated_at),
            })
        })
        .collect();

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

pub async fn delete_user_pool(
    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 deleted = db::pools::delete(&state.db, pool_id)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    if !deleted {
        return Err(CognitoError::ResourceNotFound(format!(
            "User pool {pool_id} not found"
        )));
    }

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

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

    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 config = body.clone();
    let client = db::clients::create(&state.db, pool_id, client_name, &config)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?;

    let response = serde_json::json!({
        "UserPoolClient": {
            "ClientId": client.client_id,
            "UserPoolId": client.pool_id,
            "ClientName": client.client_name,
            "CreationDate": crate::operations::epoch_secs(&client.created_at),
            "LastModifiedDate": crate::operations::epoch_secs(&client.updated_at),
            "ExplicitAuthFlows": body["ExplicitAuthFlows"],
        }
    });
    Ok(axum::Json(response).into_response())
}

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

    let client = db::clients::get(&state.db, pool_id, client_id)
        .map_err(|e| CognitoError::InternalError(e.to_string()))?
        .ok_or_else(|| CognitoError::ResourceNotFound(format!("Client {client_id} not found")))?;

    let response = serde_json::json!({
        "UserPoolClient": {
            "ClientId": client.client_id,
            "UserPoolId": client.pool_id,
            "ClientName": client.client_name,
            "CreationDate": crate::operations::epoch_secs(&client.created_at),
            "LastModifiedDate": crate::operations::epoch_secs(&client.updated_at),
        }
    });
    Ok(axum::Json(response).into_response())
}

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

    let client_list: Vec<serde_json::Value> = clients.iter().map(|c| {
        serde_json::json!({
            "ClientId": c.client_id,
            "UserPoolId": c.pool_id,
            "ClientName": c.client_name,
        })
    }).collect();

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

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

    // No-op: just verify pool exists and return success
    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 response = serde_json::json!({});
    Ok(axum::Json(response).into_response())
}
