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

use kube::{CustomResource, Resource, api::ObjectMeta};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::interpret::InterpretError;

use super::types::{
    HTTPScheme, MatchExpression, NamespaceSelector, Operator, Selector,
    ServiceMonitor as KubeServiceMonitor, ServiceMonitorEndpoint,
};

/// This is the top-level struct for the ServiceMonitor Custom Resource.
/// The `#[derive(CustomResource)]` macro handles all the boilerplate for you,
/// including the `impl Resource`.
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(
    group = "monitoring.coreos.com",
    version = "v1",
    kind = "ServiceMonitor",
    plural = "servicemonitors",
    namespaced
)]
#[serde(rename_all = "camelCase")]
pub struct ServiceMonitorSpec {
    /// A label selector to select services to monitor.
    pub selector: Selector,

    /// A list of endpoints on the selected services to be monitored.
    pub endpoints: Vec<ServiceMonitorEndpoint>,

    /// Selector to select which namespaces the Kubernetes Endpoints objects
    /// are discovered from.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub namespace_selector: Option<NamespaceSelector>,

    /// The label to use to retrieve the job name from.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub job_label: Option<String>,

    /// Pod-based target labels to transfer from the Kubernetes Pod onto the target.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub pod_target_labels: Vec<String>,

    /// TargetLabels transfers labels on the Kubernetes Service object to the target.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub target_labels: Vec<String>,
}

impl Default for ServiceMonitorSpec {
    fn default() -> Self {
        let mut labels = HashMap::new();
        Self {
            selector: Selector {
                match_labels: { labels },
                match_expressions: vec![MatchExpression {
                    key: "app.kubernetes.io/name".into(),
                    operator: Operator::Exists,
                    values: vec![],
                }],
            },
            endpoints: vec![ServiceMonitorEndpoint {
                port: Some("http".to_string()),
                path: Some("/metrics".into()),
                interval: Some("30s".into()),
                scheme: Some(HTTPScheme::HTTP),
                ..Default::default()
            }],
            namespace_selector: None, // only the same namespace
            job_label: Some("app".into()),
            pod_target_labels: vec![],
            target_labels: vec![],
        }
    }
}

impl From<KubeServiceMonitor> for ServiceMonitorSpec {
    fn from(value: KubeServiceMonitor) -> Self {
        Self {
            selector: value.selector,
            endpoints: value.endpoints,
            namespace_selector: value.namespace_selector,
            job_label: value.job_label,
            pod_target_labels: value.pod_target_labels,
            target_labels: value.target_labels,
        }
    }
}
