use harmony_macros::ingress_path;
use k8s_openapi::api::networking::v1::Ingress;
use log::{debug, trace};
use serde::Serialize;
use serde_json::json;

use crate::{
    interpret::Interpret,
    score::Score,
    topology::{K8sclient, Topology},
};

use super::resource::{K8sResourceInterpret, K8sResourceScore};

#[derive(Debug, Clone, Serialize)]
pub enum PathType {
    ImplementationSpecific,
    Exact,
    Prefix,
}

impl PathType {
    fn as_str(&self) -> &'static str {
        match self {
            PathType::ImplementationSpecific => "ImplementationSpecific",
            PathType::Exact => "Exact",
            PathType::Prefix => "Prefix",
        }
    }
}

type IngressPath = String;

#[derive(Debug, Clone, Serialize)]
pub struct K8sIngressScore {
    pub name: fqdn::FQDN,
    pub host: fqdn::FQDN,
    pub backend_service: fqdn::FQDN,
    pub port: u16,
    pub path: Option<IngressPath>,
    pub path_type: Option<PathType>,
    pub namespace: Option<fqdn::FQDN>,
}

impl<T: Topology + K8sclient> Score<T> for K8sIngressScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        let path = match self.path.clone() {
            Some(p) => p,
            None => ingress_path!("/"),
        };

        let path_type = match self.path_type.clone() {
            Some(p) => p,
            None => PathType::Prefix,
        };

        let ingress = json!(
            {
                "metadata": {
                    "name": self.name.to_string(),
                },
                "spec": {
                    "rules": [
                        {   "host": self.host.to_string(),
                            "http": {
                                "paths": [
                                    {
                                        "path": path,
                                        "pathType": path_type.as_str(),
                                        "backend": {
                                            "service": {
                                                "name": self.backend_service.to_string(),
                                                "port": {
                                                    "number": self.port,
                                                }
                                            }
                                        }
                                    }
                                ]
                            }
                        }
                    ]
                }
            }
        );

        trace!("Building ingresss object from Value {ingress:#}");
        let ingress: Ingress = serde_json::from_value(ingress).unwrap();
        debug!(
            "Successfully built Ingress for host {:?}",
            ingress.metadata.name
        );
        Box::new(K8sResourceInterpret {
            score: K8sResourceScore::single(
                ingress.clone(),
                self.namespace.clone().map(|f| f.to_string()),
            ),
        })
    }

    fn name(&self) -> String {
        format!("{} K8sIngressScore", self.name)
    }
}
