use clap::Parser;
use jwt_simple::algorithms::HS256Key;
use jwt_simple::prelude::{Clock, Duration, JWTClaims, MACLike};
use matrix_gateway::extractors::client_auth::TokenClaims;
use matrix_gateway::utils::base_utils::rand_str;
use std::ops::Add;
use std::os::unix::prelude::CommandExt;
use std::process::Command;

/// cURL wrapper to query MatrixGW
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
    /// URL of Matrix GW
    #[arg(short('U'), long, env, default_value = "http://localhost:8000")]
    matrix_gw_url: String,

    /// Token ID
    #[arg(short('i'), long, env)]
    token_id: String,

    /// User ID
    #[arg(short('u'), long, env)]
    user_id: String,

    /// Token secret
    #[arg(short('t'), long, env)]
    token_secret: String,

    /// Request verb
    #[arg(short('X'), long, default_value = "GET")]
    method: String,

    /// Payload SHA256 digest
    #[arg(short('D'), long)]
    payload_digest: Option<String>,

    /// Request URI
    uri: String,

    /// Command line arguments to pass to cURL
    #[clap(trailing_var_arg = true, allow_hyphen_values = true)]
    run: Vec<String>,
}

fn main() {
    let args: Args = Args::parse();

    let full_url = format!("{}{}", args.matrix_gw_url, args.uri);
    log::debug!("Full URL: {full_url}");

    let key = HS256Key::from_bytes(args.token_secret.as_bytes());

    let claims = JWTClaims::<TokenClaims> {
        issued_at: Some(Clock::now_since_epoch()),
        expires_at: Some(Clock::now_since_epoch().add(Duration::from_mins(15))),
        invalid_before: None,
        issuer: None,
        subject: None,
        audiences: None,
        jwt_id: None,
        nonce: Some(rand_str(10)),
        custom: TokenClaims {
            method: args.method.to_string(),
            uri: args.uri,
            payload_sha256: args.payload_digest.clone(),
        },
    };

    let jwt = key
        .with_key_id(&format!(
            "{}#{}",
            urlencoding::encode(&args.user_id),
            urlencoding::encode(&args.token_id)
        ))
        .authenticate(claims)
        .expect("Failed to sign JWT!");

    let _ = Command::new("curl")
        .args(["-X", &args.method])
        .args(["-H", &format!("x-client-auth: {jwt}")])
        .args(args.run)
        .arg(full_url)
        .exec();
    panic!("Failed to run curl!")
}