//! ServerInfo implementation.
use std::io::Write;

use anyhow::Result;
use trillium::{Handler, KnownHeaderName, Status, conn_try};

use crate::options::ServerOptions;

const SERVER_VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub struct ServerInfo {
    options: &'static ServerOptions,
}

pub fn server_info(options: &'static ServerOptions) -> impl Handler {
    ServerInfo { options }
}

#[trillium::async_trait]
impl Handler for ServerInfo {
    async fn run(&self, conn: trillium::Conn) -> trillium::Conn {
        let conn = self.server_headers(conn);
        if self.options.expose_info && conn.path() == "/.opserve/info" {
            let response = conn_try!(self.render_info(&conn), conn);

            conn.with_status(Status::Ok)
                .with_response_header(KnownHeaderName::CacheControl, "no-cache")
                .with_response_header(KnownHeaderName::ContentType, "text/plain")
                .with_body(response)
                .halt()
        } else {
            conn
        }
    }
}

impl ServerInfo {
    fn server_headers(&self, conn: trillium::Conn) -> trillium::Conn {
        let mut name = format!("opserve/{}", SERVER_VERSION);
        if let Some(tname) = conn.response_headers().get(KnownHeaderName::Server) {
            name += &format!(" ({})", tname);
        }
        conn.with_response_header(KnownHeaderName::Server, name)
    }

    fn render_info(&self, conn: &trillium::Conn) -> Result<Vec<u8>> {
        let mut response = Vec::<u8>::new();
        writeln!(
            &mut response,
            "{} {}{}",
            conn.method(),
            conn.path(),
            conn.querystring()
        )?;
        writeln!(&mut response, "Client IP: {:?}", conn.peer_ip())?;
        writeln!(&mut response, "\nRequest Headers:")?;
        for (name, val) in conn.request_headers() {
            writeln!(&mut response, "  {}: {:?}", name, val)?;
        }

        writeln!(&mut response, "\nExtracted Info:")?;
        if let Some(encs) = conn
            .request_headers()
            .get_values(KnownHeaderName::AcceptEncoding)
        {
            writeln!(&mut response, "  Accepted Encodings:")?;
            for eh in encs {
                for enc in eh.to_string().split(',') {
                    writeln!(&mut response, "    {}", enc.trim())?;
                }
            }
        }

        Ok(response)
    }
}
