use std::sync::{Arc, Mutex};

use async_trait::async_trait;
use log::debug;
use serde::Serialize;

use crate::{
    interpret::{InterpretError, Outcome},
    inventory::Inventory,
    modules::monitoring::alert_rule::prometheus_alert_rule::AlertManagerRuleGroup,
    score,
    topology::{
        HelmCommand, Topology,
        installable::Installable,
        oberservability::monitoring::{AlertReceiver, AlertRule, AlertSender},
    },
};

use score::Score;

use super::{
    helm::{
        config::KubePrometheusConfig, kube_prometheus_helm_chart::kube_prometheus_helm_chart_score,
    },
    types::{AlertManagerAdditionalPromRules, AlertManagerChannelConfig},
};

#[async_trait]
impl AlertSender for Prometheus {
    fn name(&self) -> String {
        "HelmKubePrometheus".to_string()
    }
}

#[async_trait]
impl<T: Topology + HelmCommand> Installable<T> for Prometheus {
    async fn ensure_installed(
        &self,
        inventory: &Inventory,
        topology: &T,
    ) -> Result<(), InterpretError> {
        self.install_prometheus(inventory, topology).await?;
        Ok(())
    }
}

#[derive(Debug)]
pub struct Prometheus {
    pub config: Arc<Mutex<KubePrometheusConfig>>,
}

impl Prometheus {
    pub async fn install_receiver(
        &self,
        prometheus_receiver: &dyn PrometheusReceiver,
    ) -> Result<Outcome, InterpretError> {
        let prom_receiver = prometheus_receiver.configure_receiver().await;
        debug!(
            "adding alert receiver to prometheus config: {:#?}",
            &prom_receiver
        );
        let mut config = self.config.lock().unwrap();

        config.alert_receiver_configs.push(prom_receiver);
        let prom_receiver_name = prometheus_receiver.name();
        debug!("installed alert receiver {}", &prom_receiver_name);
        Ok(Outcome::success(format!(
            "Sucessfully installed receiver {}",
            prom_receiver_name
        )))
    }

    pub async fn install_rule(
        &self,
        prometheus_rule: &AlertManagerRuleGroup,
    ) -> Result<Outcome, InterpretError> {
        let prometheus_rule = prometheus_rule.configure_rule().await;
        let mut config = self.config.lock().unwrap();

        config.alert_rules.push(prometheus_rule.clone());
        Ok(Outcome::success(format!(
            "Successfully installed alert rule: {:#?},",
            prometheus_rule
        )))
    }

    pub async fn install_prometheus<T: Topology + HelmCommand + Send + Sync>(
        &self,
        inventory: &Inventory,
        topology: &T,
    ) -> Result<Outcome, InterpretError> {
        kube_prometheus_helm_chart_score(self.config.clone())
            .create_interpret()
            .execute(inventory, topology)
            .await
    }
}

#[async_trait]
pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug {
    fn name(&self) -> String;
    async fn configure_receiver(&self) -> AlertManagerChannelConfig;
}

impl Serialize for Box<dyn AlertReceiver<Prometheus>> {
    fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        todo!()
    }
}

impl Clone for Box<dyn AlertReceiver<Prometheus>> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}

#[async_trait]
pub trait PrometheusRule: Send + Sync + std::fmt::Debug {
    fn name(&self) -> String;
    async fn configure_rule(&self) -> AlertManagerAdditionalPromRules;
}

impl Serialize for Box<dyn AlertRule<Prometheus>> {
    fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        todo!()
    }
}

impl Clone for Box<dyn AlertRule<Prometheus>> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}
