use anyhow::Result;
use clap::Subcommand;
use serde_json::json;

use crate::{output, util, Ctx};

#[derive(clap::Args)]
pub struct Args {
    #[command(subcommand)]
    cmd: Cmd,
}

#[derive(Subcommand)]
enum Cmd {
    /// Generate a one-time embed session (GenerateSession as JSON; admin only)
    GenerateSession {
        /// Request body as JSON (inline, @file, or - for stdin)
        #[arg(long, short = 'd')]
        data: String,
    },
    /// Exchange a session id for a signed embed JWT
    Token {
        /// Embed session id
        #[arg(long)]
        session_id: String,
    },
    /// Fetch an embeddable dashboard by public id
    Dashboard {
        /// Public id
        public_id: String,
    },
    /// Enable signed embedding for a dashboard (admin only)
    EnableDashboard {
        /// Public id
        public_id: String,
    },
    /// Disable signed embedding for a dashboard (admin only)
    DisableDashboard {
        /// Public id
        public_id: String,
    },
    /// Manage embed tenants
    Tenant {
        #[command(subcommand)]
        cmd: TenantCmd,
    },
}

#[derive(Subcommand)]
enum TenantCmd {
    /// Delete an embed tenant
    #[command(alias = "rm")]
    Delete {
        /// Name
        name: String,
    },
    /// List an embed tenant's groups
    Groups {
        /// Name
        name: String,
        /// Page size (cursor pagination)
        #[arg(long)]
        first: Option<u64>,
        /// Cursor for the next page (from a previous pageInfo.endCursor)
        #[arg(long)]
        after: Option<String>,
    },
    /// Delete a group of an embed tenant
    DeleteGroup {
        /// Name
        name: String,
        /// Group id
        group: i64,
    },
}

pub async fn command(args: Args, ctx: &Ctx) -> Result<()> {
    let api = ctx.api()?;
    match args.cmd {
        Cmd::GenerateSession { data } => {
            let body = util::parse_data(Some(&data))?;
            let res = api
                .post("/api/v1/embed/generate-session", Some(&util::body(body)))
                .await?;
            if ctx.json {
                output::print_json(&res);
            } else {
                println!("{}", output::field(&res, "sessionId"));
            }
        }
        Cmd::Token { session_id } => {
            let res = api
                .post(
                    "/api/v1/embed/session/token",
                    Some(&json!({ "sessionId": session_id })),
                )
                .await?;
            if ctx.json {
                output::print_json(&res);
            } else {
                println!("{}", output::field(&res, "token"));
            }
        }
        Cmd::Dashboard { public_id } => {
            let res = api
                .get(&format!("/api/v1/embed/dashboard/{public_id}"), &Vec::new())
                .await?;
            output::print_json(&res);
        }
        Cmd::EnableDashboard { public_id } => {
            let res = api
                .patch(
                    &format!("/api/v1/embed/dashboard/{public_id}"),
                    Some(&json!({ "allowEmbed": true })),
                )
                .await?;
            if ctx.json {
                output::print_json(&res);
            } else {
                output::success(&format!(
                    "Enabled signed embedding for dashboard `{public_id}`"
                ));
            }
        }
        Cmd::DisableDashboard { public_id } => {
            let res = api
                .patch(
                    &format!("/api/v1/embed/dashboard/{public_id}"),
                    Some(&json!({ "allowEmbed": false })),
                )
                .await?;
            if ctx.json {
                output::print_json(&res);
            } else {
                output::success(&format!(
                    "Disabled signed embedding for dashboard `{public_id}`"
                ));
            }
        }
        Cmd::Tenant { cmd } => match cmd {
            TenantCmd::Delete { name } => {
                api.delete(&format!("/api/v1/embed-tenants/{name}/"), None)
                    .await?;
                output::success(&format!("Deleted embed tenant `{name}`"));
            }
            TenantCmd::Groups { name, first, after } => {
                let mut query = Vec::new();
                util::push(&mut query, "first", &first);
                util::push(&mut query, "after", &after);
                let res = api
                    .get(&format!("/api/v1/embed-tenants/{name}/groups"), &query)
                    .await?;
                output::print_list(
                    ctx.json,
                    &res,
                    &[("ID", "id"), ("NAME", "name"), ("USERS", "userCount")],
                );
            }
            TenantCmd::DeleteGroup { name, group } => {
                api.delete(
                    &format!("/api/v1/embed-tenants/{name}/groups/{group}"),
                    None,
                )
                .await?;
                output::success(&format!("Deleted group {group} of embed tenant `{name}`"));
            }
        },
    }
    Ok(())
}
