use crate::prelude::PrincipalId;
use crate::prelude::RoleAssignment;
use crate::prelude::RoleDefinition;
use crate::prelude::RoleDefinitionId;
use crate::prelude::fetch_all_role_assignments;
use crate::prelude::fetch_all_role_definitions;
use crate::prelude::fetch_all_users;
use cloud_terrastodon_user_input::Choice;
use std::collections::HashMap;
use std::future::IntoFuture;
use tokio::try_join;
use tracing::warn;

pub async fn get_role_assignment_choices() -> eyre::Result<Vec<Choice<RoleAssignment>>> {
    let (role_assignments, role_definitions, users) = try_join!(
        fetch_all_role_assignments(),
        fetch_all_role_definitions(),
        fetch_all_users().into_future()
    )?;

    let role_definition_lookup: HashMap<&RoleDefinitionId, &RoleDefinition> =
        role_definitions.iter().map(|ra| (&ra.id, ra)).collect();
    let user_lookup = users
        .iter()
        .map(|u| (u.id.into(), u))
        .collect::<HashMap<PrincipalId, _>>();

    let mut choices: Vec<Choice<RoleAssignment>> = Vec::new();
    for ra in role_assignments {
        let Some(rd) = role_definition_lookup.get(&ra.role_definition_id) else {
            warn!("Could not identify role definition for {ra:?}");
            continue;
        };
        let user = user_lookup.get(&ra.principal_id);
        let role_name = &rd.display_name;
        let scope = &ra.scope;
        let principal_display_name = user
            .map(|u| u.display_name.to_string())
            .unwrap_or_else(|| ra.principal_id.to_string());
        let key = format!("{role_name:41} {principal_display_name:36} {scope}");
        choices.push(Choice { key, value: ra });
    }

    Ok(choices)
}
