use clap::{Parser, Subcommand};
use indieweb_cli_common::{Config, ConfigArgs, OutputFormat, print_json};
use indieweb::http::reqwest::Client as HttpClient;
use miette::IntoDiagnostic;
use serde::Serialize;
use url::Url;

mod commands;

use commands::{discover, send, parse, ptd};

#[derive(Parser)]
#[command(name = "webmention")]
#[command(about = "Webmention CLI tools for the IndieWeb", long_about = None)]
#[command(version)]
struct Cli {
    #[command(flatten)]
    config_args: ConfigArgs,

    #[arg(long, short, global = true, default_value = "json")]
    output: OutputFormat,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Discover {
        url: Url,
    },
    Send {
        source: Url,
        target: Url,
        #[arg(long, short)]
        endpoint: Option<Url>,
        #[arg(long, short)]
        token: Option<String>,
    },
    Parse {
        source: Url,
        target: Url,
        #[arg(long, short)]
        token: Option<String>,
    },
    Ptd {
        source: Url,
        target: Url,
        #[arg(long, short)]
        token: Option<String>,
    },
}

#[derive(Serialize)]
struct CommandResult<T: Serialize> {
    success: bool,
    #[serde(flatten)]
    data: T,
}

impl<T: Serialize> CommandResult<T> {
    fn success(data: T) -> Self {
        Self { success: true, data }
    }
}

#[tokio::main]
async fn main() -> miette::Result<()> {
    let cli = Cli::parse();

    if cli.config_args.verbose {
        tracing_forest::init();
    }

    let config = Config::load(&cli.config_args).into_diagnostic()?;
    let http_client = HttpClient::default();

    match cli.command {
        Commands::Discover { url } => {
            let result = discover::run(&http_client, &url).await?;
            print_json(&CommandResult::success(result)).into_diagnostic()?;
        }
        Commands::Send { source, target, endpoint, token } => {
            let result = send::run(&http_client, &config, &source, &target, endpoint.as_ref(), token.as_deref()).await?;
            print_json(&CommandResult::success(result)).into_diagnostic()?;
        }
        Commands::Parse { source, target, token } => {
            let result = parse::run(&http_client, &source, &target, token.as_deref()).await?;
            print_json(&CommandResult::success(result)).into_diagnostic()?;
        }
        Commands::Ptd { source, target, token } => {
            let result = ptd::run(&http_client, &source, &target, token.as_deref()).await?;
            print_json(&CommandResult::success(result)).into_diagnostic()?;
        }
    }

    Ok(())
}
