use anyhow::Result;
use clap::Parser;
use dropshot::endpoint;
use dropshot::ApiDescription;
use dropshot::ConfigDropshot;
use dropshot::HttpError;
use dropshot::HttpResponseOk;
use dropshot::RequestContext;
use dropshot::ServerBuilder;
use opentelemetry::{trace::TracerProvider as _, KeyValue};
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::{
    trace::{RandomIdGenerator, Sampler},
    Resource,
};
use opentelemetry_semantic_conventions::{
    attribute::{SERVICE_NAME, SERVICE_VERSION},
    SCHEMA_URL,
};
use schemars::JsonSchema;
use serde::Serialize;
use slog::Drain;
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_slog::TracingSlogDrain;
use tracing_subscriber::prelude::*;

use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Cli {}

/// Represents a project in our API.
#[derive(Serialize, JsonSchema)]
struct VersionInfo {
    /// Name of the project.
    name: String,
}

/// Fetch version info.
#[endpoint {
    method = GET,
    path = "/version",
}]
#[tracing::instrument()]
async fn api_version(
    rqctx: RequestContext<Arc<()>>,
) -> Result<HttpResponseOk<VersionInfo>, HttpError> {
    let ver = VersionInfo {
        name: String::from("patagia-controller"),
    };
    Ok(HttpResponseOk(ver))
}

#[tokio::main]
async fn main() -> Result<(), String> {
    let _args = Cli::parse();
    let fmt_layer = tracing_subscriber::fmt::layer();

    let mut config_dropshot = ConfigDropshot::default();
    config_dropshot.bind_address = SocketAddr::from_str("0.0.0.0:9474").unwrap();
    config_dropshot.request_body_max_bytes = 1024 * 1024;

    // Adapt the Dropshot logger to tracing
    let dropshot_logger = {
        let level_drain = slog::LevelFilter(TracingSlogDrain, slog::Level::Debug).fuse();
        let async_drain = slog_async::Async::new(level_drain).build().fuse();
        slog::Logger::root(async_drain, slog::o!())
    };

    let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_tonic()
        .with_endpoint("https://localhost:4317")
        .build()
        .map_err(|e| e.to_string())?;

    let resource = Resource::from_schema_url(
        [
            // KeyValue::new(SERVICE_NAME, env!("CARGO_PKG_NAME")),
            KeyValue::new(SERVICE_NAME, "patagia-controller"),
            KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
        ],
        SCHEMA_URL,
    );

    let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
        .with_config(
            opentelemetry_sdk::trace::Config::default()
                .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
                    1.0,
                ))))
                .with_id_generator(RandomIdGenerator::default())
                .with_resource(resource),
        )
        .with_batch_exporter(otlp_exporter, opentelemetry_sdk::runtime::Tokio)
        .build();

    let tracer = tracer_provider.tracer("patagia-controller");

    // let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);

    tracing_subscriber::registry()
        .with(tracing_subscriber::EnvFilter::from_default_env())
        .with(fmt_layer)
        // .with(telemetry)
        .with(OpenTelemetryLayer::new(tracer))
        .init();

    tracing::info!("Patagia Controller");

    foo().await;

    let mut api = ApiDescription::new();
    api.register(api_version).map_err(|e| e.to_string())?;
    let server = ServerBuilder::new(api, Arc::new(()), dropshot_logger)
        .config(config_dropshot)
        .start()
        .map_err(|e| e.to_string())?;
    server.await
}

#[tracing::instrument]
async fn foo() {
    tracing::info!(
        monotonic_counter.foo = 1_u64,
        key_1 = "bar",
        key_2 = 10,
        "This is the Foo!",
    );

    tracing::info!(histogram.baz = 10, "histogram example",);
}