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

use async_trait::async_trait;
use schemars::JsonSchema;
use serde::{Deserialize, 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,
    pub replicas: u32,
    pub alert_manager_config_selector: AlertManagerConfigSelector,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AlertManagerConfigSelector {
    pub match_labels: BTreeMap<String, String>,
}

#[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, Deserialize, JsonSchema)]
pub enum HTTPScheme {
    #[serde(rename = "http")]
    HTTP,
    #[serde(rename = "https")]
    HTTPS,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
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(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct ServiceMonitorEndpoint {
    /// Name of the service port this endpoint refers to.
    pub port: Option<String>,

    /// Interval at which metrics should be scraped.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub interval: Option<String>,

    /// The HTTP path to scrape for metrics.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub path: Option<String>,

    /// HTTP scheme to use for scraping.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub scheme: Option<HTTPScheme>,

    /// Relabelings to apply to samples before scraping.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub relabelings: Vec<RelabelConfig>,

    /// MetricRelabelings to apply to samples after scraping, but before ingestion.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub metric_relabelings: Vec<RelabelConfig>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct RelabelConfig {
    /// The action to perform based on the regex matching.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub action: Option<String>,

    /// A list of labels from which to extract values.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub source_labels: Vec<String>,

    /// Separator to be used when concatenating source_labels.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub separator: Option<String>,

    /// The label to which the resulting value is written.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub target_label: Option<String>,

    /// A regular expression to match against the concatenated source label values.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub regex: Option<String>,

    /// The replacement value to use.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub replacement: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct MatchExpression {
    pub key: String,
    pub operator: Operator, // "In", "NotIn", "Exists", "DoesNotExist"
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub values: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct Selector {
    /// A map of key-value pairs to match.
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub match_labels: HashMap<String, String>,

    /// A list of label selector requirements.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub match_expressions: Vec<MatchExpression>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[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<HashMap<String, String>>,

    // # 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<NamespaceSelector>,

    // # 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>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct NamespaceSelector {
    /// Select all namespaces.
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub any: bool,

    /// List of namespace names to select from.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub match_names: Vec<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(),
        }
    }
}
