use std::sync::Arc;

use axum::{
    Router,
    body::Body,
    extract::Path,
    http::{HeaderValue, StatusCode, header},
    response::{Html, IntoResponse, Response},
    routing::get,
};
use include_dir::{Dir, include_dir};

use crate::state::{DashboardState, GLOBAL_DASHBOARD_STATE};

// $DASHBOARD_ASSETS_DIR is generated by the build script
// and contains all the static files for the dashboard.
static ASSETS_DIR: Dir = include_dir!("$DASHBOARD_ASSETS_DIR");
const INDEX: &str = "index.html";
const NOT_FOUND: &str = "404.html";

async fn serve_default() -> impl IntoResponse {
    let index_file = ASSETS_DIR.get_file(INDEX).unwrap();
    Html(index_file.contents())
}

async fn serve_not_found() -> Response {
    let not_found_file = ASSETS_DIR.get_file(NOT_FOUND).unwrap();
    // Would prefer to use the builder pattern, but keep getting errors from multiple http-body versions
    (
        [(
            header::CONTENT_TYPE,
            HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
        )],
        not_found_file.contents(),
    )
        .into_response()
}

async fn serve_asset(path: String) -> impl IntoResponse {
    match ASSETS_DIR.get_file(&path) {
        None => serve_not_found().await,
        Some(file) => {
            let mime_type = mime_guess::from_path(path).first_or_octet_stream();
            let cache = if mime_type == mime::TEXT_HTML {
                "max-age=0"
            } else {
                "max-age=31536000" // 365 days
            };

            Response::builder()
                .status(StatusCode::OK)
                .header(header::CONTENT_TYPE, mime_type.as_ref())
                .header(header::CACHE_CONTROL, cache)
                .body(Body::from(file.contents()))
                .unwrap()
        }
    }
}

pub(crate) fn routes() -> Router<Arc<DashboardState>> {
    Router::new()
        .route("/", get(serve_default))
        .route("/404.html", get(serve_not_found))
        .route("/queries", get(|| serve_asset("queries.html".to_string())))
        .route("/query", get(|| serve_asset("query.html".to_string())))
        .route(
            "/{*path}",
            get(|Path(path): Path<String>| async move { serve_asset(path).await }),
        )
        .with_state(GLOBAL_DASHBOARD_STATE.clone())
}
