use axum::extract::State;
use axum::Json;
use magnetar_common::config::MagnetarConfig;
use magnetar_core::web_model::rel::{RelNodeInfo20, RelNodeInfo21};
use magnetar_core::web_model::Rel;
use magnetar_nodeinfo::version_2_0::NodeInfo20;
use magnetar_nodeinfo::version_2_1::{NodeInfo21, NodeInfo21Software};
use magnetar_nodeinfo::{
    version_1_0::{NodeInfo10Services, NodeInfo10Software, NodeInfo10Usage, NodeInfo10UsageUsers},
    NodeInfo,
};
use serde::Serialize;
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};

const NODEINFO_PATH: &str = "/nodeinfo";

#[derive(Clone, Debug, Serialize)]
pub struct NodeInfoLink {
    rel: &'static str,
    href: String,
}

pub async fn handle_nodeinfo(State(config): State<&'static MagnetarConfig>) -> Json<Value> {
    let links = vec![
        NodeInfoLink {
            href: format!(
                "{}://{}/nodeinfo/2.0",
                config.networking.protocol, config.networking.host
            ),
            rel: RelNodeInfo20.rel(),
        },
        NodeInfoLink {
            href: format!(
                "{}://{}/nodeinfo/2.1",
                config.networking.protocol, config.networking.host
            ),
            rel: RelNodeInfo21.rel(),
        },
    ];

    let links_serialized = serde_json::to_value(links).unwrap();

    Json(json!({ "links": links_serialized }))
}

pub async fn handle_nodeinfo_21(State(config): State<&'static MagnetarConfig>) -> Json<NodeInfo> {
    Json(NodeInfo::V2_1(NodeInfo21 {
        software: NodeInfo21Software {
            name: config.branding.name.clone(),
            version: config.branding.version.clone(),
            homepage: Some(config.branding.homepage.clone()),
            repository: Some(config.branding.repository.clone()),
        },
        protocols: HashSet::from(["activitypub".to_owned()]),
        services: NodeInfo10Services {
            inbound: HashSet::new(),
            outbound: HashSet::new(),
        },
        open_registrations: false,
        usage: NodeInfo10Usage {
            users: NodeInfo10UsageUsers {
                total: Some(0),
                active_halfyear: Some(0),
                active_month: Some(0),
            },
            local_posts: Some(0),
            local_comments: Some(0),
        },
        metadata: HashMap::new(),
    }))
}

pub async fn handle_nodeinfo_20(State(config): State<&'static MagnetarConfig>) -> Json<NodeInfo> {
    Json(NodeInfo::V2_0(NodeInfo20 {
        software: NodeInfo10Software {
            name: config.branding.name.clone(),
            version: config.branding.version.clone(),
        },
        protocols: HashSet::from(["activitypub".to_owned()]),
        services: NodeInfo10Services {
            inbound: HashSet::new(),
            outbound: HashSet::new(),
        },
        open_registrations: false,
        usage: NodeInfo10Usage {
            users: NodeInfo10UsageUsers {
                total: Some(0),
                active_halfyear: Some(0),
                active_month: Some(0),
            },
            local_posts: Some(0),
            local_comments: None,
        },
        metadata: HashMap::new(),
    }))
}

#[cfg(test)]
mod tests {
    use axum::extract::State;
    use magnetar_common::config::MagnetarConfig;

    #[tokio::test]
    async fn test_nodeinfo() {
        std::env::set_var("MAG_C_HOST", "nattyarch.local");
        std::env::set_var("MAG_C_DATABASE_URL", "dummy");
        std::env::set_var("MAG_C_REDIS_URL", "dummy");

        let config = MagnetarConfig::default();
        let config_ref = Box::leak(Box::new(config));
        let nodeinfo = crate::nodeinfo::handle_nodeinfo(State(config_ref)).await;

        println!("{:#?}", nodeinfo);
    }
}
