use crate::AppState;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use contracts::auth_middleware::{AuthUser, require_admin};
use db::models::config::{
    ConfigRepository, CreateDashboardConfigPayload, CreateOnboardingConfigPayload,
    CreateRuntimeConfigPayload,
};
use db::models::user::UserRepository;
use serde::Deserialize;
use std::collections::{BTreeMap, BTreeSet};
use uuid::Uuid;

pub fn onboarding_router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_onboarding_configs).post(create_onboarding_config))
        .route("/{role_id}", get(get_active_onboarding_config))
        .route("/by-key/{role_key}", get(get_onboarding_config_by_key))
}

pub fn dashboard_router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_dashboard_configs).post(create_dashboard_config))
        .route("/{role_id}", get(get_active_dashboard_config))
        .route("/by-key/{role_key}", get(get_dashboard_config_by_key))
}

pub fn runtime_router() -> Router<AppState> {
    Router::new()
        .route("/", get(get_my_runtime_config).post(create_runtime_config))
        .route("/{role_id}", get(get_active_runtime_config))
}

pub fn admin_runtime_router() -> Router<AppState> {
    Router::new()
        .route("/", get(list_runtime_configs).post(admin_create_runtime_config))
        .route("/{id}", get(get_runtime_config_by_id).delete(delete_runtime_config))
        .route("/{id}/activate", post(activate_runtime_config))
}

#[derive(Deserialize)]
struct AdminListQuery {
    role_id: Option<Uuid>,
    role_key: Option<String>,
}

async fn list_runtime_configs(
    auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<AdminListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    // Resolve role_id by role_key if provided
    let role_id = if let Some(id) = q.role_id {
        Some(id)
    } else if let Some(key) = q.role_key.clone() {
        sqlx::query_scalar::<_, Uuid>("SELECT id FROM roles WHERE key = $1")
            .bind(key.to_uppercase())
            .fetch_optional(&state.pool)
            .await
            .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
    } else {
        None
    };

    #[derive(sqlx::FromRow)]
    struct RcRow {
        id: Uuid,
        role_id: Uuid,
        config_json: serde_json::Value,
        version: i32,
        is_active: bool,
        updated_at: chrono::DateTime<chrono::Utc>,
    }

    let items = if let Some(rid) = role_id {
        sqlx::query_as::<_, RcRow>(
            r#"
            SELECT id, role_id, config_json, version, is_active, updated_at
            FROM role_runtime_configs
            WHERE role_id = $1
            ORDER BY version DESC
            "#,
        )
        .bind(rid)
        .fetch_all(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
        .into_iter()
        .map(|r| serde_json::json!({
            "id": r.id,
            "role_id": r.role_id,
            "config_json": r.config_json,
            "version": r.version,
            "is_active": r.is_active,
            "updated_at": r.updated_at,
        }))
        .collect::<Vec<_>>()
    } else {
        sqlx::query_as::<_, RcRow>(
            r#"
            SELECT rc.id, rc.role_id, rc.config_json, rc.version, rc.is_active, rc.updated_at
            FROM role_runtime_configs rc
            JOIN roles r ON rc.role_id = r.id
            WHERE r.audience = 'INTERNAL'
            ORDER BY rc.updated_at DESC
            "#,
        )
        .fetch_all(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
        .into_iter()
        .map(|r| serde_json::json!({
            "id": r.id,
            "role_id": r.role_id,
            "config_json": r.config_json,
            "version": r.version,
            "is_active": r.is_active,
            "updated_at": r.updated_at,
        }))
        .collect::<Vec<_>>()
    };

    Ok((StatusCode::OK, Json(serde_json::json!({ "items": items }))))
}

async fn get_runtime_config_by_id(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    #[derive(sqlx::FromRow)]
    struct RcDetailRow {
        id: Uuid,
        role_id: Uuid,
        config_json: serde_json::Value,
        version: i32,
        is_active: bool,
        updated_at: chrono::DateTime<chrono::Utc>,
    }
    let r = sqlx::query_as::<_, RcDetailRow>(
        "SELECT id, role_id, config_json, version, is_active, updated_at FROM role_runtime_configs WHERE id = $1",
    )
    .bind(id)
    .fetch_optional(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
    .ok_or((StatusCode::NOT_FOUND, "Runtime config not found".to_string()))?;
    let row = serde_json::json!({
        "id": r.id,
        "role_id": r.role_id,
        "config_json": r.config_json,
        "version": r.version,
        "is_active": r.is_active,
        "updated_at": r.updated_at,
    });
    Ok((StatusCode::OK, Json(row)))
}

async fn admin_create_runtime_config(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<CreateRuntimeConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    match ConfigRepository::create_runtime_config(&state.pool, payload).await {
        Ok(config) => Ok((StatusCode::CREATED, Json(config))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn activate_runtime_config(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    // Fetch role_id for the target config
    let role_id: Uuid = sqlx::query_scalar::<_, Uuid>("SELECT role_id FROM role_runtime_configs WHERE id = $1")
        .bind(id)
        .fetch_optional(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
        .ok_or((StatusCode::NOT_FOUND, "Runtime config not found".to_string()))?;
    // Disable existing active
    sqlx::query("UPDATE role_runtime_configs SET is_active = false WHERE role_id = $1 AND is_active = true")
        .bind(role_id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    // Activate target
    sqlx::query("UPDATE role_runtime_configs SET is_active = true WHERE id = $1")
        .bind(id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    get_runtime_config_by_id(auth, State(state), Path(id)).await
}

async fn delete_runtime_config(
    auth: AuthUser,
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    let result = sqlx::query("DELETE FROM role_runtime_configs WHERE id = $1")
        .bind(id)
        .execute(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
    if result.rows_affected() == 0 {
        return Err((StatusCode::NOT_FOUND, "Runtime config not found".to_string()));
    }
    Ok((StatusCode::NO_CONTENT, "".to_string()))
}
#[derive(Deserialize)]
struct RuntimeConfigQuery {
    role: Option<String>,
}

async fn get_my_runtime_config(
    auth: contracts::auth_middleware::AuthUser,
    State(state): State<AppState>,
    Query(q): Query<RuntimeConfigQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    // Allow frontend to override role via ?role= query param (falls back to JWT claim)
    let role_key = q.role
        .map(|r| r.to_uppercase())
        .filter(|r| !r.is_empty())
        .unwrap_or_else(|| auth.claims.active_role.clone().to_uppercase());

    #[derive(sqlx::FromRow)]
    #[allow(dead_code)]
    struct RoleRow {
        id: Uuid,
        key: String,
        audience: String,
    }
    let role = sqlx::query_as::<_, RoleRow>("SELECT id, key, audience FROM roles WHERE key = $1")
        .bind(&role_key)
        .fetch_optional(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
        .ok_or((StatusCode::NOT_FOUND, format!("Role '{}' not found", role_key)))?;

    let config = match ConfigRepository::get_active_runtime_by_role_key(&state.pool, &role_key).await {
        Ok(c) => Some(c),
        Err(sqlx::Error::RowNotFound) => None,
        Err(e) => {
            return Err((
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Database error: {}", e),
            ))
        }
    };

    // Fetch live user data to merge into the response so the `user` field is always fresh.
    let user = UserRepository::get_by_id(&state.pool, auth.user_id)
        .await
        .map_err(|_| (StatusCode::UNAUTHORIZED, "User not found".to_string()))?;

    let roles = UserRepository::get_user_role_keys(&state.pool, auth.user_id)
        .await
        .unwrap_or_default();

    // Merge the stored config_json with live user data.
    // `config_json` holds role/modules/flags/permissions; we add the `user` sub-object.
    let mut response = config
        .map(|c| c.config_json)
        .unwrap_or_else(|| serde_json::json!({}));
    if !response.is_object() {
        response = serde_json::json!({});
    }
    if let Some(obj) = response.as_object_mut() {
        obj.insert(
            "user".to_string(),
            serde_json::json!({
                "id": user.id.to_string(),
                "name": format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default()),
                "email": user.email,
                "roles": roles,
                "active_role": role_key,
            }),
        );
        obj.insert("role".to_string(), serde_json::Value::String(role_key.clone()));
        obj.insert("audience".to_string(), serde_json::Value::String(role.audience.clone()));
    }

    if role.audience == "INTERNAL" {
        let permission_keys: Vec<String> = sqlx::query_scalar::<_, String>(
            "SELECT permission_key FROM role_admin_permissions WHERE role_id = $1 ORDER BY permission_key",
        )
        .bind(role.id)
        .fetch_all(&state.pool)
        .await
        .unwrap_or_default();

        let mut per_module: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
        for key in permission_keys {
            let key = key.trim().to_uppercase();
            if key.is_empty() {
                continue;
            }

            let parsed = parse_permission_key(&key);
            if let Some((module, action)) = parsed {
                if !module.is_empty() && !action.is_empty() {
                    per_module.entry(module).or_default().insert(action);
                }
            }
        }

        if let Some(obj) = response.as_object_mut() {
            let enabled_modules = per_module.keys().cloned().collect::<Vec<_>>();
            obj.insert("enabled_modules".to_string(), serde_json::json!(enabled_modules));

            let mut permissions_obj = serde_json::Map::new();
            for (module, actions) in per_module {
                permissions_obj.insert(module, serde_json::json!(actions.into_iter().collect::<Vec<_>>()));
            }
            obj.insert("permissions".to_string(), serde_json::Value::Object(permissions_obj));
        }
    } else {
        // EXTERNAL role: derive enabled_modules and sidebar config from the active dashboard_config.
        // Falls back to the runtime_config's own enabled_modules if no dashboard_config exists.
        let dash_config = ConfigRepository::get_active_dashboard_config(
            &state.pool,
            role.id,
            "EXTERNAL",
        )
        .await
        .ok();

        if let Some(dash) = dash_config {
            let config_json = &dash.config_json;

            // Extract sidebar_items (the admin saves this key as snake_case in the API).
            // Try both "sidebar_items" and "sidebarItems" for forward-compatibility.
            let sidebar_items: Vec<String> = config_json
                .get("sidebar_items")
                .or_else(|| config_json.get("sidebarItems"))
                .and_then(|v| v.as_array())
                .map(|arr| {
                    arr.iter()
                        .filter_map(|v| v.as_str().map(str::to_string))
                        .collect()
                })
                .unwrap_or_default();

            // Map display-name sidebar items to the module keys the frontend MODULE_NAV_MAP understands.
            let enabled_modules: Vec<String> = sidebar_items
                .iter()
                .filter_map(|item| sidebar_item_to_module_key(item.as_str()))
                .map(str::to_string)
                .collect();

            if let Some(obj) = response.as_object_mut() {
                // Only overwrite enabled_modules if the dashboard config has sidebar items defined.
                if !enabled_modules.is_empty() {
                    obj.insert("enabled_modules".to_string(), serde_json::json!(enabled_modules));
                }

                // Include the full dashboard config so the frontend can read widgets/tabs directly.
                obj.insert("dashboard_config".to_string(), config_json.clone());
            }
        }
    }

    Ok((StatusCode::OK, Json(response)))
}

/// Maps an admin-configured sidebar display name to the module key used in the frontend's
/// MODULE_NAV_MAP. Returns None for items that don't map to a routable module (e.g. Logout).
fn sidebar_item_to_module_key(item: &str) -> Option<&'static str> {
    let normalized = item.trim().to_lowercase();
    match normalized.as_str() {
        "my dashboard" | "dashboard"                        => Some("dashboard"),
        "my profile" | "profile"                            => Some("profile"),
        "my portfolio" | "portfolio"                        => Some("portfolio"),
        "leads" | "my leads"                                => Some("leads"),
        "my responses" | "responses" | "my requests"        => Some("leads"),
        "received responses" | "shortlisted responses"      => Some("marketplace"),
        "marketplace"                                       => Some("marketplace"),
        "jobs" | "job postings"                             => Some("job_postings"),
        "applications" | "my applications"
            | "shortlisted candidates"                      => Some("applications"),
        "my requirements" | "requirements"
            | "post requirement"                            => Some("requirements"),
        "credits" | "tracecoins" | "wallet"                 => Some("wallet"),
        "services" | "my services"                          => Some("services"),
        "explore nxtgauge" | "explore"                      => Some("onboarding"),
        "verification" | "verification status"              => Some("verification"),
        "notifications"                                     => Some("notifications"),
        "help center" | "support"                           => Some("support"),
        "settings"                                          => Some("settings"),
        "switch services" | "logout"                        => None, // UI-only, not a module
        _                                                   => None,
    }
}

fn parse_permission_key(key: &str) -> Option<(String, String)> {
    // Format: MODULE:Action (e.g. DEPARTMENT_MANAGEMENT:View)
    if let Some((module, action)) = key.split_once(':') {
        let module = module.trim().to_string();
        let action = action.trim().to_uppercase();
        if !module.is_empty() && matches!(action.as_str(), "VIEW" | "CREATE" | "UPDATE" | "DELETE") {
            return Some((module, action));
        }
    }

    // Format: module.action (e.g. departments.view)
    if let Some((module, action)) = key.split_once('.') {
        let module = module.trim().to_uppercase();
        let action = action.trim().to_uppercase();
        if !module.is_empty() && matches!(action.as_str(), "VIEW" | "CREATE" | "UPDATE" | "DELETE") {
            return Some((module, action));
        }
    }

    // Format: MODULE_ACTION (e.g. DEPARTMENTS_VIEW)
    let parts: Vec<&str> = key.split('_').collect();
    if parts.len() >= 2 {
        let action = parts[parts.len() - 1].trim().to_uppercase();
        if matches!(action.as_str(), "VIEW" | "CREATE" | "UPDATE" | "DELETE") {
            let module = parts[..parts.len() - 1].join("_").trim().to_string();
            if !module.is_empty() {
                return Some((module, action));
            }
        }
    }

    None
}

#[cfg(test)]
mod tests {
    use super::parse_permission_key;

    #[test]
    fn parses_colon_format() {
        let parsed = parse_permission_key("INTERNAL_DASHBOARD_CONFIG:View");
        assert_eq!(
            parsed,
            Some(("INTERNAL_DASHBOARD_CONFIG".to_string(), "VIEW".to_string()))
        );
    }

    #[test]
    fn parses_dot_format() {
        let parsed = parse_permission_key("verification_management.update");
        assert_eq!(
            parsed,
            Some(("VERIFICATION_MANAGEMENT".to_string(), "UPDATE".to_string()))
        );
    }

    #[test]
    fn parses_underscore_suffix_format() {
        let parsed = parse_permission_key("APPROVAL_MANAGEMENT_DELETE");
        assert_eq!(
            parsed,
            Some(("APPROVAL_MANAGEMENT".to_string(), "DELETE".to_string()))
        );
    }
}



/// Rejects schemas that would enable the wizard flow with nothing for the
/// user to fill in: an empty `steps` array, or any non-review step whose
/// `fields` array is empty. Guards against the admin editor's "Enable
/// verification wizard flow" checkbox being saved before any fields are
/// added to a step.
fn validate_onboarding_schema(schema_json: &serde_json::Value) -> Result<(), String> {
    let enable_wizard_flow = schema_json
        .get("enableWizardFlow")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);
    if !enable_wizard_flow {
        return Ok(());
    }

    let steps = schema_json
        .get("steps")
        .and_then(|v| v.as_array())
        .cloned()
        .unwrap_or_default();
    if steps.is_empty() {
        return Err("Cannot enable the wizard flow with no steps configured.".to_string());
    }

    for step in &steps {
        let step_type = step.get("type").and_then(|v| v.as_str()).unwrap_or("");
        if step_type == "review" {
            continue;
        }
        let fields_empty = step
            .get("fields")
            .and_then(|v| v.as_array())
            .map(|f| f.is_empty())
            .unwrap_or(true);
        if fields_empty {
            let title = step.get("title").and_then(|v| v.as_str()).unwrap_or("(untitled step)");
            return Err(format!(
                "Cannot enable the wizard flow: step \"{}\" has no fields.",
                title
            ));
        }
    }

    Ok(())
}

async fn create_onboarding_config(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<CreateOnboardingConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    if let Err(msg) = validate_onboarding_schema(&payload.schema_json) {
        return Err((StatusCode::BAD_REQUEST, msg));
    }
    match ConfigRepository::create_onboarding_config(&state.pool, payload).await {
        Ok(config) => Ok((StatusCode::CREATED, Json(config))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn get_active_onboarding_config(
    State(state): State<AppState>,
    Path(role_id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_active_onboarding_config(&state.pool, role_id).await {
        Ok(config) => Ok((StatusCode::OK, Json(config))),
        Err(sqlx::Error::RowNotFound) => Err((
            StatusCode::NOT_FOUND,
            "Active onboarding config not found".to_string(),
        )),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn list_onboarding_configs(
    State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_all_onboarding_configs(&state.pool).await {
        Ok(configs) => Ok((StatusCode::OK, Json(configs))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn create_dashboard_config(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<CreateDashboardConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if let Err(_e) = require_admin(&auth) {
        return Err((StatusCode::FORBIDDEN, "Forbidden".to_string()));
    }
    match ConfigRepository::create_dashboard_config(&state.pool, payload).await {
        Ok(config) => Ok((StatusCode::CREATED, Json(config))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

#[derive(Deserialize)]
struct DashboardQuery {
    audience: String,
}

async fn get_active_dashboard_config(
    State(state): State<AppState>,
    Path(role_id): Path<Uuid>,
    Query(query): Query<DashboardQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_active_dashboard_config(&state.pool, role_id, &query.audience).await {
        Ok(config) => Ok((StatusCode::OK, Json(config))),
        Err(sqlx::Error::RowNotFound) => Err((
            StatusCode::NOT_FOUND,
            "Active dashboard config not found".to_string(),
        )),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn list_dashboard_configs(
    State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_all_dashboard_configs(&state.pool).await {
        Ok(configs) => Ok((StatusCode::OK, Json(configs))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn create_runtime_config(
    State(state): State<AppState>,
    Json(payload): Json<CreateRuntimeConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::create_runtime_config(&state.pool, payload).await {
        Ok(config) => Ok((StatusCode::CREATED, Json(config))),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn get_active_runtime_config(
    State(state): State<AppState>,
    Path(role_id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_active_runtime_config(&state.pool, role_id).await {
        Ok(config) => Ok((StatusCode::OK, Json(config))),
        Err(sqlx::Error::RowNotFound) => Err((
            StatusCode::NOT_FOUND,
            "Active runtime config not found".to_string(),
        )),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn get_onboarding_config_by_key(
    State(state): State<AppState>,
    Path(role_key): Path<String>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_active_onboarding_by_role_key(&state.pool, &role_key).await {
        Ok(config) => Ok((StatusCode::OK, Json(config))),
        Err(sqlx::Error::RowNotFound) => Err((
            StatusCode::NOT_FOUND,
            format!("Active onboarding config for role '{}' not found", role_key),
        )),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}

async fn get_dashboard_config_by_key(
    State(state): State<AppState>,
    Path(role_key): Path<String>,
    Query(query): Query<DashboardQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    match ConfigRepository::get_active_dashboard_by_role_key(&state.pool, &role_key, &query.audience).await {
        Ok(config) => Ok((StatusCode::OK, Json(config))),
        Err(sqlx::Error::RowNotFound) => Err((
            StatusCode::NOT_FOUND,
            format!("Active dashboard config for role '{}' not found", role_key),
        )),
        Err(e) => Err((
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Database error: {}", e),
        )),
    }
}
