use async_trait::async_trait;
use log::info;
use serde::Serialize;

use crate::{
    data::{Id, Version},
    interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
    inventory::Inventory,
    score::Score,
    topology::{LoadBalancer, LoadBalancerService, Topology},
};

#[derive(Debug, Clone, Serialize)]
pub struct LoadBalancerScore {
    pub public_services: Vec<LoadBalancerService>,
    pub private_services: Vec<LoadBalancerService>,
    // TODO public and private services are likely wrong, should be a single list of
    // (listen_interface, LoadBalancerService) tuples or something like that
    // I am not sure what to use as listen_interface, should it be interface name, ip address,
    // uuid?
}

impl<T: Topology + LoadBalancer> Score<T> for LoadBalancerScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        Box::new(LoadBalancerInterpret::new(self.clone()))
    }

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

#[derive(Debug)]
pub struct LoadBalancerInterpret {
    version: Version,
    status: InterpretStatus,
    score: LoadBalancerScore,
}

impl LoadBalancerInterpret {
    pub fn new(score: LoadBalancerScore) -> Self {
        Self {
            version: Version::from("1.0.0").expect("Version should be valid"),
            status: InterpretStatus::QUEUED,
            score,
        }
    }
}

#[async_trait]
impl<T: Topology + LoadBalancer> Interpret<T> for LoadBalancerInterpret {
    async fn execute(
        &self,
        _inventory: &Inventory,
        load_balancer: &T,
    ) -> Result<Outcome, InterpretError> {
        info!(
            "Making sure Load Balancer is initialized: {:?}",
            load_balancer.ensure_initialized().await?
        );

        for service in self.score.public_services.iter() {
            info!("Ensuring service exists {service:?}");

            load_balancer.ensure_service_exists(service).await?;
        }

        for service in self.score.private_services.iter() {
            info!("Ensuring private service exists {service:?}");
            load_balancer.ensure_service_exists(service).await?;
        }

        info!("Applying load balancer configuration");
        load_balancer.commit_config().await?;
        info!("Making a full reload and restart of haproxy");
        load_balancer.reload_restart().await?;
        Ok(Outcome::success(format!(
            "Load balancer successfully configured {} services",
            self.score.public_services.len() + self.score.private_services.len()
        )))
    }
    fn get_name(&self) -> InterpretName {
        InterpretName::LoadBalancer
    }
    fn get_version(&self) -> Version {
        self.version.clone()
    }
    fn get_status(&self) -> InterpretStatus {
        self.status.clone()
    }

    fn get_children(&self) -> Vec<Id> {
        todo!()
    }
}
