use std::convert::Infallible;
use hyper::{service::{ make_service_fn, service_fn, }};
use hyper::{ Body, Request, Response, Server, };

use log::{Record, Level, Metadata};
use log::{SetLoggerError, LevelFilter};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }

    fn flush(&self) {}
}

static LOGGER: SimpleLogger = SimpleLogger;

pub fn init() -> Result<(), SetLoggerError> {
    log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
}


async fn hello(body: Request<Body>) -> Result<Response<Body>, Infallible> {
    log::info!("URI: {} {} {:?}", body.method(), body.uri(), body.version());
    for (k, v) in body.headers() {
        log::info!("{}: {:?}", k, v);
    }
    log::info!("Extensions: {:?}", body.extensions());
    let inner_body = body.body();
    log::info!("{:?}", inner_body);
    
    Ok(Response::new(Body::from("Hello World!\n")))
}

// https://github.com/hyperium/hyper/blob/master/examples/hello.rs
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // pretty_env_logger::init();
    init()?;

    println!("{}", log::STATIC_MAX_LEVEL);
    log::info!("hello hyper.rs");
    log::warn!("hello hyper.rs");
    log::error!("hello hyper.rs");

    // For every connection, we must make a `Service` to handle all
    // incoming HTTP requests on said connection.
    let make_svc = make_service_fn(|_conn| {
        // This is the `Service` that will handle the connection.
        // `service_fn` is a helper to convert a function that
        // returns a Response into a `Service`.
        async { Ok::<_, Infallible>(service_fn(hello)) }
    });

    let addr = ([127, 0, 0, 1], 3000).into();

    let server = Server::bind(&addr).serve(make_svc);

    println!("Listening on http://{}", addr);

    server.await?;

    Ok(())
}
