// SPDX-FileCopyrightText: 2024-2025 The n5i Project
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use k8s_crds_cilium::{
    CiliumNetworkPolicyEgressToPortsPorts, CiliumNetworkPolicyEgressToPortsPortsProtocol,
    CiliumNetworkPolicyIngressToPortsPorts, CiliumNetworkPolicyIngressToPortsPortsProtocol,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct NetworkPolicy {
    pub ingress: NetworkPolicyIngress,
    pub egress: NetworkPolicyEgress,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct NetworkPolicyIngress {
    pub allow_world: CommonComponentConfig,
    pub allow_cluster: CommonComponentConfig,
    pub allow_same_ns: CommonComponentConfig,
    pub cidrs: Vec<Cidr>,
    pub pods: Vec<PodConfig>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct CommonComponentConfig {
    pub allow: bool,
    pub allowed_ports: Option<Vec<PortConfig>>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct NetworkPolicyEgress {
    pub allow_cluster: bool,
    pub allow_same_ns: bool,
    pub allow_kube_api: bool,
    pub allow_world: bool,
    pub allow_local_net: bool,
    pub allow_traefik: bool,
    pub dns: Option<DnsConfig>,
    pub fqdns: Vec<FqdnConfig>,
    pub cidrs: Vec<Cidr>,
    pub services: Vec<ServiceConfig>,
    pub pods: Vec<PodConfig>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct DnsConfig {
    pub allow: bool,
    /// The lookups to allow, if not specified, all lookups are allowed
    pub allowed_lookup_names: Option<Vec<String>>,
    pub allowed_lookup_pattern: Option<Vec<String>>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct FqdnConfig {
    pub match_name: Option<String>,
    pub match_pattern: Option<String>,
    pub allowed_ports: Option<Vec<PortConfig>>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct PortConfig {
    pub port: u16,
    pub protocol: Protocol,
}

impl From<&PortConfig> for CiliumNetworkPolicyIngressToPortsPorts {
    fn from(value: &PortConfig) -> Self {
        CiliumNetworkPolicyIngressToPortsPorts {
            port: value.port.to_string(),
            protocol: Some(match value.protocol {
                Protocol::Tcp => CiliumNetworkPolicyIngressToPortsPortsProtocol::Tcp,
                Protocol::Udp => CiliumNetworkPolicyIngressToPortsPortsProtocol::Udp,
                Protocol::Sctp => CiliumNetworkPolicyIngressToPortsPortsProtocol::Sctp,
                Protocol::Any => CiliumNetworkPolicyIngressToPortsPortsProtocol::Any,
            }),
            end_port: None,
        }
    }
}
impl From<&PortConfig> for CiliumNetworkPolicyEgressToPortsPorts {
    fn from(value: &PortConfig) -> Self {
        CiliumNetworkPolicyEgressToPortsPorts {
            port: value.port.to_string(),
            protocol: Some(match value.protocol {
                Protocol::Tcp => CiliumNetworkPolicyEgressToPortsPortsProtocol::Tcp,
                Protocol::Udp => CiliumNetworkPolicyEgressToPortsPortsProtocol::Udp,
                Protocol::Sctp => CiliumNetworkPolicyEgressToPortsPortsProtocol::Sctp,
                Protocol::Any => CiliumNetworkPolicyEgressToPortsPortsProtocol::Any,
            }),
            end_port: None,
        }
    }
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum Protocol {
    Tcp,
    Udp,
    Sctp,
    Any,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Cidr {
    pub cidr: String,
    pub except: Option<Vec<String>>,
    pub allowed_ports: Option<Vec<PortConfig>>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct PodConfig {
    pub namespace: Option<String>,
    pub app: Option<String>,
    pub match_labels: Option<HashMap<String, String>>,
    pub allowed_ports: Option<Vec<PortConfig>>,
    pub all_namespaces: bool,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct ServiceConfig {
    pub namespace: Option<String>,
    pub service_name: String,
    pub allowed_ports: Option<Vec<PortConfig>>,
}
