//! `pic members ls | add | set | rm` — manage app membership (G2).
//!
//! Wraps `/api/v1/admin/apps/{id}/members*`. All gated on
//! `AppAdmin(app)` server-side; Editors/Viewers get a 403.

use anyhow::Result;
use picloud_shared::AppRole;

use crate::client::Client;
use crate::config;
use crate::output::{KvBlock, OutputMode, Table};

pub async fn ls(app: &str, mode: OutputMode) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let members = client.members_list(app).await?;
    let mut table = Table::new(["user_id", "username", "role", "instance_role", "active"]);
    for m in members {
        table.row([
            m.user_id.to_string(),
            m.username,
            app_role_str(m.role).to_string(),
            format!("{:?}", m.instance_role).to_lowercase(),
            m.is_active.to_string(),
        ]);
    }
    table.print(mode);
    Ok(())
}

pub async fn add(app: &str, user_id: &str, role: &str, mode: OutputMode) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let m = client
        .members_grant(app, user_id, parse_role(role)?)
        .await?;
    print_member(&m, mode);
    Ok(())
}

pub async fn set(app: &str, user_id: &str, role: &str, mode: OutputMode) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let m = client
        .members_set_role(app, user_id, parse_role(role)?)
        .await?;
    print_member(&m, mode);
    Ok(())
}

pub async fn rm(app: &str, user_id: &str) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    client.members_remove(app, user_id).await?;
    println!("Removed {user_id} from {app}");
    Ok(())
}

fn print_member(m: &crate::client::AppMemberDto, mode: OutputMode) {
    let mut block = KvBlock::new();
    block
        .field("user_id", m.user_id.to_string())
        .field("username", m.username.clone())
        .field("role", app_role_str(m.role))
        .field("created_at", m.created_at.to_rfc3339());
    block.print(mode);
}

fn parse_role(s: &str) -> Result<AppRole> {
    match s.to_ascii_lowercase().as_str() {
        "app_admin" | "admin" => Ok(AppRole::AppAdmin),
        "editor" => Ok(AppRole::Editor),
        "viewer" => Ok(AppRole::Viewer),
        other => Err(anyhow::anyhow!(
            "unknown role {other:?} (want app_admin | editor | viewer)"
        )),
    }
}

fn app_role_str(r: AppRole) -> &'static str {
    match r {
        AppRole::AppAdmin => "app_admin",
        AppRole::Editor => "editor",
        AppRole::Viewer => "viewer",
    }
}
