use std::net::SocketAddr;

use serde::Serialize;

use crate::{
    interpret::Interpret,
    modules::load_balancer::LoadBalancerScore,
    score::Score,
    topology::{
        BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer,
        LoadBalancerService, Topology,
    },
};

#[derive(Debug, Clone, Serialize)]
pub struct OKDBootstrapLoadBalancerScore {
    load_balancer_score: LoadBalancerScore,
}

impl OKDBootstrapLoadBalancerScore {
    pub fn new(topology: &HAClusterTopology) -> Self {
        let private_ip = topology.router.get_gateway();

        let private_services = vec![
            LoadBalancerService {
                backend_servers: Self::topology_to_backend_server(topology, 80),
                listening_port: SocketAddr::new(private_ip, 80),
                health_check: Some(HealthCheck::TCP(None)),
            },
            LoadBalancerService {
                backend_servers: Self::topology_to_backend_server(topology, 443),
                listening_port: SocketAddr::new(private_ip, 443),
                health_check: Some(HealthCheck::TCP(None)),
            },
            LoadBalancerService {
                backend_servers: Self::topology_to_backend_server(topology, 22623),
                listening_port: SocketAddr::new(private_ip, 22623),
                health_check: Some(HealthCheck::TCP(None)),
            },
            LoadBalancerService {
                backend_servers: Self::topology_to_backend_server(topology, 6443),
                listening_port: SocketAddr::new(private_ip, 6443),
                health_check: Some(HealthCheck::HTTP(
                    "/readyz".to_string(),
                    HttpMethod::GET,
                    HttpStatusCode::Success2xx,
                )),
            },
        ];
        Self {
            load_balancer_score: LoadBalancerScore {
                public_services: vec![],
                private_services,
            },
        }
    }
    fn topology_to_backend_server(topology: &HAClusterTopology, port: u16) -> Vec<BackendServer> {
        let mut backend: Vec<_> = topology
            .control_plane
            .iter()
            .map(|cp| BackendServer {
                address: cp.ip.to_string(),
                port,
            })
            .collect();
        backend.push(BackendServer {
            address: topology.bootstrap_host.ip.to_string(),
            port,
        });
        backend
    }
}

impl<T: Topology + LoadBalancer> Score<T> for OKDBootstrapLoadBalancerScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        self.load_balancer_score.create_interpret()
    }

    fn name(&self) -> String {
        "OKDBootstrapLoadBalancerScore".to_string()
    }
}
