use std::collections::{BTreeMap, HashMap};

use async_trait::async_trait;
use serde::Serialize;
use serde_yaml::{Mapping, Sequence, Value};

use crate::modules::monitoring::alert_rule::prometheus_alert_rule::AlertManagerRuleGroup;

#[async_trait]
pub trait AlertChannelConfig {
    async fn get_config(&self) -> AlertManagerChannelConfig;
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertManagerValues {
    pub alertmanager: AlertManager,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AlertManager {
    pub enabled: bool,
    pub config: AlertManagerConfig,
    pub alertmanager_spec: AlertManagerSpec,
    pub init_config_reloader: ConfigReloader,
}

#[derive(Debug, Clone, Serialize)]
pub struct ConfigReloader {
    pub resources: Resources,
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertManagerConfig {
    pub global: Mapping,
    pub route: AlertManagerRoute,
    pub receivers: Sequence,
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertManagerRoute {
    pub routes: Sequence,
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertManagerChannelConfig {
    ///expecting an option that contains two values
    ///if necessary for the alertchannel
    ///[ jira_api_url: <string> ]
    pub channel_global_config: Option<(Value, Value)>,
    pub channel_route: Value,
    pub channel_receiver: Value,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AlertManagerSpec {
    pub(crate) resources: Resources,
}

#[derive(Debug, Clone, Serialize)]
pub struct Resources {
    pub limits: Limits,
    pub requests: Requests,
}

#[derive(Debug, Clone, Serialize)]
pub struct Limits {
    pub memory: String,
    pub cpu: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct Requests {
    pub memory: String,
    pub cpu: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertManagerAdditionalPromRules {
    #[serde(flatten)]
    pub rules: BTreeMap<String, AlertGroup>,
}

#[derive(Debug, Clone, Serialize)]
pub struct AlertGroup {
    pub groups: Vec<AlertManagerRuleGroup>,
}

#[derive(Debug, Clone, Serialize)]
pub enum HTTPScheme {
    #[serde(rename = "http")]
    HTTP,
    #[serde(rename = "https")]
    HTTPS,
}

#[derive(Debug, Clone, Serialize)]
pub enum Operator {
    In,
    NotIn,
    Exists,
    DoesNotExist,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PrometheusConfigValues {
    pub prometheus: PrometheusConfig,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PrometheusConfig {
    pub prometheus: bool,
    pub additional_service_monitors: Vec<ServiceMonitor>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ServiceMonitorTLSConfig {
    // ## Path to the CA file
    // ##
    pub ca_file: Option<String>,

    // ## Path to client certificate file
    // ##
    pub cert_file: Option<String>,

    // ## Skip certificate verification
    // ##
    pub insecure_skip_verify: Option<bool>,

    // ## Path to client key file
    // ##
    pub key_file: Option<String>,

    // ## Server name used to verify host name
    // ##
    pub server_name: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ServiceMonitorEndpoint {
    // ## Name of the endpoint's service port
    // ## Mutually exclusive with targetPort
    pub port: Option<String>,

    // ## Name or number of the endpoint's target port
    // ## Mutually exclusive with port
    pub target_port: Option<String>,

    // ## File containing bearer token to be used when scraping targets
    // ##
    pub bearer_token_file: Option<String>,

    // ## Interval at which metrics should be scraped
    // ##
    pub interval: Option<String>,

    // ## HTTP path to scrape for metrics
    // ##
    pub path: String,

    // ## HTTP scheme to use for scraping
    // ##
    pub scheme: HTTPScheme,

    // ## TLS configuration to use when scraping the endpoint
    // ##
    pub tls_config: Option<ServiceMonitorTLSConfig>,

    // ## MetricRelabelConfigs to apply to samples after scraping, but before ingestion.
    // ## ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#relabelconfig
    // ##
    // # - action: keep
    // #   regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+'
    // #   sourceLabels: [__name__]
    pub metric_relabelings: Vec<Mapping>,

    // ## RelabelConfigs to apply to samples before scraping
    // ## ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#relabelconfig
    // ##
    // # - sourceLabels: [__meta_kubernetes_pod_node_name]
    // #   separator: ;
    // #   regex: ^(.*)$
    // #   targetLabel: nodename
    // #   replacement: $1
    // #   action: replace
    pub relabelings: Vec<Mapping>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MatchExpression {
    pub key: String,
    pub operator: Operator,
    pub values: Vec<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Selector {
    //   # label selector for services
    pub match_labels: HashMap<String, String>,
    pub match_expressions: Vec<MatchExpression>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ServiceMonitor {
    pub name: String,

    // # Additional labels to set used for the ServiceMonitorSelector. Together with standard labels from the chart
    pub additional_labels: Option<Mapping>,

    // # Service label for use in assembling a job name of the form <label value>-<port>
    // # If no label is specified, the service name is used.
    pub job_label: Option<String>,

    // # labels to transfer from the kubernetes service to the target
    pub target_labels: Vec<String>,

    // # labels to transfer from the kubernetes pods to the target
    pub pod_target_labels: Vec<String>,

    // # Label selector for services to which this ServiceMonitor applies
    //   # Example which selects all services to be monitored
    //   # with label "monitoredby" with values any of "example-service-1" or "example-service-2"
    //   matchExpressions:
    //     - key: "monitoredby"
    //       operator: In
    //       values:
    //         - example-service-1
    //         - example-service-2
    pub selector: Selector,

    // # Namespaces from which services are selected
    //   # Match any namespace
    //   any: bool,
    //   # Explicit list of namespace names to select
    //   matchNames: Vec,
    pub namespace_selector: Option<Mapping>,

    // # Endpoints of the selected service to be monitored
    pub endpoints: Vec<ServiceMonitorEndpoint>,

    // # Fallback scrape protocol used by Prometheus for scraping metrics
    // # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#monitoring.coreos.com/v1.ScrapeProtocol
    pub fallback_scrape_protocol: Option<String>,
}

impl Default for ServiceMonitor {
    fn default() -> Self {
        Self {
            name: Default::default(),
            additional_labels: Default::default(),
            job_label: Default::default(),
            target_labels: Default::default(),
            pod_target_labels: Default::default(),
            selector: Selector {
                match_labels: HashMap::new(),
                match_expressions: vec![],
            },
            namespace_selector: Default::default(),
            endpoints: Default::default(),
            fallback_scrape_protocol: Default::default(),
        }
    }
}

impl Default for ServiceMonitorEndpoint {
    fn default() -> Self {
        Self {
            port: Some("80".to_string()),
            target_port: Default::default(),
            bearer_token_file: Default::default(),
            interval: Default::default(),
            path: "/metrics".to_string(),
            scheme: HTTPScheme::HTTP,
            tls_config: Default::default(),
            metric_relabelings: Default::default(),
            relabelings: Default::default(),
        }
    }
}
