use std::collections::BTreeMap;

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

use super::crd_prometheuses::LabelSelector;

#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(
    group = "grafana.integreatly.org",
    version = "v1beta1",
    kind = "Grafana",
    plural = "grafanas",
    namespaced
)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaSpec {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub config: Option<GrafanaConfig>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub admin_user: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub admin_password: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ingress: Option<GrafanaIngress>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub persistence: Option<GrafanaPersistence>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub resources: Option<ResourceRequirements>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub log: Option<GrafanaLogConfig>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub security: Option<GrafanaSecurityConfig>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaLogConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mode: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub level: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaSecurityConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub admin_user: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub admin_password: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaIngress {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enabled: Option<bool>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub hosts: Option<Vec<String>>,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaPersistence {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enabled: Option<bool>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub storage_class_name: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub size: Option<String>,
}

// ------------------------------------------------------------------------------------------------

#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(
    group = "grafana.integreatly.org",
    version = "v1beta1",
    kind = "GrafanaDashboard",
    plural = "grafanadashboards",
    namespaced
)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaDashboardSpec {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub resync_period: Option<String>,

    pub instance_selector: LabelSelector,

    pub json: String,
}

// ------------------------------------------------------------------------------------------------

#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(
    group = "grafana.integreatly.org",
    version = "v1beta1",
    kind = "GrafanaDatasource",
    plural = "grafanadatasources",
    namespaced
)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaDatasourceSpec {
    pub instance_selector: LabelSelector,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub allow_cross_namespace_import: Option<bool>,

    pub datasource: GrafanaDatasourceConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GrafanaDatasourceConfig {
    pub access: String,
    pub database: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub json_data: Option<BTreeMap<String, String>>,
    pub name: String,
    pub r#type: String,
    pub url: String,
}

// ------------------------------------------------------------------------------------------------

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceRequirements {
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub limits: BTreeMap<String, String>,

    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub requests: BTreeMap<String, String>,
}
