use config::Config;
use std::{path::PathBuf, sync::Arc};
use tokio::sync::RwLock;

use blog::{Blog, RenderedBlog};
use warp::{
    http::HeaderValue,
    hyper::{
        header::{CONTENT_TYPE, EXPIRES},
        Body,
    },
    path::FullPath,
    reply, Filter,
};

mod blog;
mod config;
mod date;
mod favicon;
mod image;
mod load;
mod misc;
mod parser;
mod path;
mod post;
mod resp;
mod style;
mod util;

async fn handle_request(
    req: FullPath,
    blog: Arc<RwLock<RenderedBlog>>,
) -> Result<impl warp::Reply, warp::Rejection> {
    let blog = blog.read().await;
    let (status, content) = blog.get(req.as_str());

    let mut reply = reply::Response::new(Body::from(content.data));
    let headers = reply.headers_mut();
    headers.insert(CONTENT_TYPE, HeaderValue::from_static(content.content_type));

    if let Some(expires) = content.expiry.header_value() {
        // TODO log a metric (statsd) if this fails)
        if let Ok(hv) = HeaderValue::from_str(&expires) {
            headers.insert(EXPIRES, hv);
        }
    }
    *reply.status_mut() = status;

    Ok(reply)
}

#[tokio::main]
async fn main() {
    let config = Config::load().unwrap();

    let blog: RenderedBlog = Blog::new(
        config.name,
        config.description,
        &PathBuf::from(config.path),
        config.logo,
    )
    .unwrap()
    .try_into()
    .unwrap();
    let blog = Arc::new(RwLock::new(blog));

    let blog_filter = warp::any().map(move || blog.clone());

    warp::serve(
        warp::filters::path::full()
            .and(blog_filter.clone())
            .and_then(handle_request),
    )
    .run(([0, 0, 0, 0], 3030))
    .await;
}