use anyhow::{Context, Result};
use clap::{Args, Subcommand};
use uuid::Uuid;

use crate::output::{OutputFormat, print_json};

use super::CommandContext;

#[derive(Subcommand, Debug)]
pub enum PatCommand {
    Create(CreatePatArgs),
    List,
    Revoke(RevokePatArgs),
}

#[derive(Args, Debug)]
pub struct CreatePatArgs {
    #[arg(long)]
    pub name: String,
}

#[derive(Args, Debug)]
pub struct RevokePatArgs {
    pub pat_id: Uuid,
}

pub async fn run(command: PatCommand, ctx: &CommandContext) -> Result<()> {
    match command {
        PatCommand::Create(args) => create(args, ctx).await,
        PatCommand::List => list(ctx).await,
        PatCommand::Revoke(args) => revoke(args, ctx).await,
    }
}

async fn create(args: CreatePatArgs, ctx: &CommandContext) -> Result<()> {
    let body: nolgia_client::types::CreatePatRequest =
        nolgia_client::types::CreatePatRequest::builder()
            .name(args.name)
            .try_into()
            .context("building create-pat request")?;
    let created = ctx
        .client()
        .create_pat()
        .body(body)
        .send()
        .await
        .context("creating personal access token")?
        .into_inner();
    match ctx.format() {
        OutputFormat::Json => print_json(&created),
        OutputFormat::Text => {
            println!("created {} ({})", created.pat.id, created.pat.name.as_str());
            println!("token: {}", created.token);
            println!("warning: this token will not be shown again; store it securely");
            Ok(())
        }
    }
}

async fn list(ctx: &CommandContext) -> Result<()> {
    let page = ctx
        .client()
        .list_pats()
        .send()
        .await
        .context("listing personal access tokens")?
        .into_inner();
    match ctx.format() {
        OutputFormat::Json => print_json(&page),
        OutputFormat::Text => {
            for pat in page.items {
                let last_used = pat
                    .last_used_at
                    .map(|at| at.to_rfc3339())
                    .unwrap_or_else(|| "never".to_string());
                println!(
                    "{} {} {} created {} last used {}",
                    pat.id,
                    pat.name.as_str(),
                    pat.prefix.as_str(),
                    pat.created_at.to_rfc3339(),
                    last_used
                );
            }
            Ok(())
        }
    }
}

async fn revoke(args: RevokePatArgs, ctx: &CommandContext) -> Result<()> {
    ctx.client()
        .revoke_pat()
        .id(args.pat_id)
        .send()
        .await
        .context("revoking personal access token")?;
    match ctx.format() {
        OutputFormat::Json => print_json(&serde_json::json!({ "revoked": args.pat_id })),
        OutputFormat::Text => {
            println!("revoked {}", args.pat_id);
            Ok(())
        }
    }
}
