use std::sync::Arc;

use crate::modules::application::{Application, ApplicationFeature};
use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore;
use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus;

use crate::{
    inventory::Inventory,
    modules::monitoring::{
        alert_channel::webhook_receiver::WebhookReceiver, ntfy::ntfy::NtfyScore,
    },
    score::Score,
    topology::{HelmCommand, K8sclient, Topology, Url, tenant::TenantManager},
};
use crate::{
    modules::prometheus::prometheus::PrometheusApplicationMonitoring,
    topology::oberservability::monitoring::AlertReceiver,
};
use async_trait::async_trait;
use base64::{Engine as _, engine::general_purpose};
use log::{debug, info};

#[derive(Debug, Clone)]
pub struct Monitoring {
    pub application: Arc<dyn Application>,
    pub alert_receiver: Vec<Box<dyn AlertReceiver<CRDPrometheus>>>,
}

#[async_trait]
impl<
    T: Topology
        + HelmCommand
        + 'static
        + TenantManager
        + K8sclient
        + std::fmt::Debug
        + PrometheusApplicationMonitoring<CRDPrometheus>,
> ApplicationFeature<T> for Monitoring
{
    async fn ensure_installed(&self, topology: &T) -> Result<(), String> {
        info!("Ensuring monitoring is available for application");
        let namespace = topology
            .get_tenant_config()
            .await
            .map(|ns| ns.name.clone())
            .unwrap_or_else(|| self.application.name());

        let mut alerting_score = ApplicationMonitoringScore {
            sender: CRDPrometheus {
                namespace: namespace.clone(),
                client: topology.k8s_client().await.unwrap(),
            },
            application: self.application.clone(),
            receivers: self.alert_receiver.clone(),
        };
        let ntfy = NtfyScore {
            namespace: namespace.clone(),
            host: "localhost".to_string(),
        };
        ntfy.create_interpret()
            .execute(&Inventory::empty(), topology)
            .await
            .expect("couldn't create interpret for ntfy");

        let ntfy_default_auth_username = "harmony";
        let ntfy_default_auth_password = "harmony";
        let ntfy_default_auth_header = format!(
            "Basic {}",
            general_purpose::STANDARD.encode(format!(
                "{ntfy_default_auth_username}:{ntfy_default_auth_password}"
            ))
        );

        debug!("ntfy_default_auth_header: {ntfy_default_auth_header}");

        let ntfy_default_auth_param = general_purpose::STANDARD
            .encode(ntfy_default_auth_header)
            .replace("=", "");

        debug!("ntfy_default_auth_param: {ntfy_default_auth_param}");

        let ntfy_receiver = WebhookReceiver {
            name: "ntfy-webhook".to_string(),
            url: Url::Url(
                url::Url::parse(
                    format!(
                        "http://ntfy.{}.svc.cluster.local/rust-web-app?auth={ntfy_default_auth_param}",
                        namespace.clone()
                    )
                    .as_str(),
                )
                .unwrap(),
            ),
        };

        alerting_score.receivers.push(Box::new(ntfy_receiver));
        alerting_score
            .create_interpret()
            .execute(&Inventory::empty(), topology)
            .await
            .unwrap();
        Ok(())
    }
    fn name(&self) -> String {
        "Monitoring".to_string()
    }
}
