use backon::{ExponentialBuilder, Retryable};
use cfg_if::cfg_if;
use tonic::transport::{Endpoint, Uri};
use tower::service_fn;

use asciidoctor_client::Args;
use std::io::Read;
use std::time::Duration;

use asciidoctor_client::grpc::asciidoctor_converter_client::AsciidoctorConverterClient;
use asciidoctor_client::grpc::AsciidoctorConvertRequest;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let Args {
        extensions,
        backend,
        attributes,
        no_header_footer,
        server_address,
        input: _,
        max_timeout,
    } = argh::from_env();
    let standalone = !no_header_footer;
    let addy = server_address.clone();
    let backoff = ExponentialBuilder::default()
        .with_min_delay(Duration::from_millis(125))
        .with_max_times(4)
        .with_max_delay(Duration::from_secs(max_timeout))
        .with_jitter();
    let endpoint = Endpoint::try_from("http://[::]:50051")?;
    cfg_if!(
        if #[cfg(unix)] {
            let connector = move |_: Uri| {
                use tokio::net::UnixStream;
                UnixStream::connect(addy.path().to_owned())
            };
        } else if #[cfg(windows)] {
            let connector = async move |_: Uri| {
                use tokio::net::windows::named_pipe as pipe;
                pipe::ClientOptions::new().open(addy.path().to_owned())
            };
        } else {
            compile_error!("Not windows or unix")
        }
    );

    let channel = match server_address.scheme() {
        "unix" => {
            (|| endpoint.connect_with_connector(service_fn(connector.clone())))
                .retry(&backoff)
                .await?
        }
        _ => (|| endpoint.connect()).retry(&backoff).await?,
    };

    let mut input = String::new();
    let mut stdin = std::io::stdin().lock();
    stdin.read_to_string(&mut input)?;
    drop(stdin);
    let input = input;

    let request = AsciidoctorConvertRequest {
        input,
        attributes,
        backend,
        extensions,
        standalone,
    };

    let convert = || async {
        let mut client = AsciidoctorConverterClient::new(channel.clone());
        client.convert(tonic::Request::new(request.clone())).await
    };

    let output = convert.retry(&backoff).await?.into_inner().output;

    println!("{}", output);

    Ok(())
}
