use std::collections::BTreeMap;

use k8s_openapi::api::core::v1::{Service, ServicePort, ServiceSpec};
use kube::api::ObjectMeta;
use kube::Resource;

use crate::types::chatservice::ChatService;
use crate::types::minioinstance::MinioInstance;
use crate::types::webservice::WebService;

pub fn build_web_service(ws: &WebService) -> Service {
    let name = ws.metadata.name.clone().unwrap();
    let ns = ws.metadata.namespace.clone().unwrap();
    let oref = ws.controller_owner_ref(&()).unwrap();
    let port = ws.spec.port.unwrap_or(8080);
    build_service_inner(&name, &ns, port, oref)
}

pub fn build_chat_service(cs: &ChatService) -> Service {
    let name = cs.metadata.name.clone().unwrap();
    let ns = cs.metadata.namespace.clone().unwrap();
    let oref = cs.controller_owner_ref(&()).unwrap();
    let port = cs.spec.port.unwrap_or(8080);
    build_service_inner(&name, &ns, port, oref)
}

pub fn build_minio_service(mi: &MinioInstance) -> Service {
    let name = mi.metadata.name.clone().unwrap();
    let ns = mi.metadata.namespace.clone().unwrap();
    let oref = mi.controller_owner_ref(&()).unwrap();

    let selector: BTreeMap<String, String> = [("app".to_string(), name.clone())].into();

    Service {
        metadata: ObjectMeta {
            name: Some(name),
            namespace: Some(ns),
            owner_references: Some(vec![oref]),
            ..Default::default()
        },
        spec: Some(ServiceSpec {
            selector: Some(selector),
            ports: Some(vec![
                ServicePort {
                    name: Some("api".to_string()),
                    port: 9000,
                    ..Default::default()
                },
                ServicePort {
                    name: Some("console".to_string()),
                    port: 9001,
                    ..Default::default()
                },
            ]),
            ..Default::default()
        }),
        ..Default::default()
    }
}

fn build_service_inner(
    name: &str,
    ns: &str,
    port: i32,
    oref: k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference,
) -> Service {
    let selector: BTreeMap<String, String> = [("app".to_string(), name.to_string())].into();

    Service {
        metadata: ObjectMeta {
            name: Some(name.to_string()),
            namespace: Some(ns.to_string()),
            owner_references: Some(vec![oref]),
            ..Default::default()
        },
        spec: Some(ServiceSpec {
            selector: Some(selector),
            ports: Some(vec![ServicePort {
                name: Some("http".to_string()),
                port,
                ..Default::default()
            }]),
            ..Default::default()
        }),
        ..Default::default()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::testutil::{test_chatservice, test_minioinstance, test_webservice};

    #[test]
    fn web_service_named_correctly() {
        let svc = build_web_service(&test_webservice());
        assert_eq!(svc.metadata.name.unwrap(), "txt");
    }

    #[test]
    fn web_service_selector_uses_app_label() {
        let svc = build_web_service(&test_webservice());
        let selector = svc.spec.unwrap().selector.unwrap();
        assert_eq!(selector["app"], "txt");
    }

    #[test]
    fn web_service_default_port_8080() {
        let svc = build_web_service(&test_webservice());
        let ports = svc.spec.unwrap().ports.unwrap();
        assert_eq!(ports[0].port, 8080);
    }

    #[test]
    fn web_service_has_owner_reference() {
        let svc = build_web_service(&test_webservice());
        let orefs = svc.metadata.owner_references.unwrap();
        assert_eq!(orefs[0].name, "txt");
    }

    #[test]
    fn chat_service_named_correctly() {
        let svc = build_chat_service(&test_chatservice());
        assert_eq!(svc.metadata.name.unwrap(), "chat");
    }

    #[test]
    fn minio_service_has_two_ports() {
        let svc = build_minio_service(&test_minioinstance());
        let ports = svc.spec.unwrap().ports.unwrap();
        assert_eq!(ports.len(), 2);
        assert_eq!(ports[0].port, 9000);
        assert_eq!(ports[1].port, 9001);
    }

    #[test]
    fn minio_service_has_owner_reference() {
        let svc = build_minio_service(&test_minioinstance());
        let orefs = svc.metadata.owner_references.unwrap();
        assert_eq!(orefs[0].name, "minio");
    }
}
