//! Core Kubernetes types for k8shost
//!
//! This module defines the core K8s resource types used throughout the k8shost system.
//! Types are designed to be minimal for MVP while maintaining compatibility with K8s API.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// ============================================================================
// Common Types
// ============================================================================

/// ObjectMeta contains metadata about a Kubernetes object
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ObjectMeta {
    pub name: String,
    pub namespace: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resource_version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub creation_timestamp: Option<DateTime<Utc>>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub labels: HashMap<String, String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub annotations: HashMap<String, String>,

    // Multi-tenant fields for UltraCloud integration
    #[serde(skip_serializing_if = "Option::is_none")]
    pub org_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
}

/// LabelSelector for selecting objects by labels
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LabelSelector {
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub match_labels: HashMap<String, String>,
}

/// ResourceRequirements specify compute resource requirements
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ResourceRequirements {
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub requests: HashMap<String, String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub limits: HashMap<String, String>,
}

// ============================================================================
// Pod Types
// ============================================================================

/// Pod is a collection of containers that can run on a host
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pod {
    pub metadata: ObjectMeta,
    pub spec: PodSpec,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<PodStatus>,
}

/// PodSpec describes the desired state of a Pod
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PodSpec {
    pub containers: Vec<Container>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub restart_policy: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub node_name: Option<String>,
}

/// Container represents a single container that is part of a pod
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Container {
    pub name: String,
    pub image: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub command: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub args: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub ports: Vec<ContainerPort>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub env: Vec<EnvVar>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resources: Option<ResourceRequirements>,
}

/// ContainerPort represents a network port in a single container
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContainerPort {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    pub container_port: i32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub protocol: Option<String>,
}

/// EnvVar represents an environment variable
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvVar {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub value: Option<String>,
}

/// PodStatus represents the current state of a Pod
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PodStatus {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phase: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pod_ip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub host_ip: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub conditions: Vec<PodCondition>,
}

/// PodCondition contains details for the current condition of this pod
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PodCondition {
    pub r#type: String,
    pub status: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
}

// ============================================================================
// Service Types
// ============================================================================

/// Service is a named abstraction of software service
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Service {
    pub metadata: ObjectMeta,
    pub spec: ServiceSpec,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<ServiceStatus>,
}

/// ServiceSpec describes the attributes that a user creates on a service
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceSpec {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub ports: Vec<ServicePort>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub selector: HashMap<String, String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cluster_ip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub r#type: Option<String>, // ClusterIP, LoadBalancer, etc.
}

/// ServicePort contains information on service's port
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServicePort {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    pub port: i32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub target_port: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub protocol: Option<String>,
}

/// ServiceStatus represents the current status of a service
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceStatus {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub load_balancer: Option<LoadBalancerStatus>,
}

/// LoadBalancerStatus represents the status of a load-balancer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadBalancerStatus {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub ingress: Vec<LoadBalancerIngress>,
}

/// LoadBalancerIngress represents the status of a load-balancer ingress point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadBalancerIngress {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hostname: Option<String>,
}

// ============================================================================
// Deployment Types
// ============================================================================

/// Deployment enables declarative updates for Pods and ReplicaSets
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Deployment {
    pub metadata: ObjectMeta,
    pub spec: DeploymentSpec,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<DeploymentStatus>,
}

/// DeploymentSpec is the specification of the desired behavior of the Deployment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentSpec {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub replicas: Option<i32>,
    pub selector: LabelSelector,
    pub template: PodTemplateSpec,
}

/// PodTemplateSpec describes the data a pod should have when created from a template
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PodTemplateSpec {
    pub metadata: ObjectMeta,
    pub spec: PodSpec,
}

/// DeploymentStatus is the most recently observed status of the Deployment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentStatus {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub replicas: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ready_replicas: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub available_replicas: Option<i32>,
}

// ============================================================================
// Node Types
// ============================================================================

/// Node is a worker node in Kubernetes
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Node {
    pub metadata: ObjectMeta,
    pub spec: NodeSpec,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<NodeStatus>,
}

/// NodeSpec describes the attributes that a node is created with
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeSpec {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pod_cidr: Option<String>,
}

/// NodeStatus is information about the current status of a node
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeStatus {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub addresses: Vec<NodeAddress>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub conditions: Vec<NodeCondition>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub capacity: HashMap<String, String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub allocatable: HashMap<String, String>,
}

/// NodeAddress contains information for the node's address
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeAddress {
    pub r#type: String, // Hostname, InternalIP, ExternalIP
    pub address: String,
}

/// NodeCondition contains condition information for a node
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeCondition {
    pub r#type: String, // Ready, MemoryPressure, DiskPressure, etc.
    pub status: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
}

// ============================================================================
// Namespace Types
// ============================================================================

/// Namespace provides a scope for Names
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Namespace {
    pub metadata: ObjectMeta,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spec: Option<NamespaceSpec>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<NamespaceStatus>,
}

/// NamespaceSpec describes the attributes on a Namespace
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceSpec {}

/// NamespaceStatus describes the current status of a Namespace
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceStatus {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phase: Option<String>,
}

// ============================================================================
// ConfigMap and Secret Types
// ============================================================================

/// ConfigMap holds configuration data for pods to consume
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigMap {
    pub metadata: ObjectMeta,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub data: HashMap<String, String>,
}

/// Secret holds secret data of a certain type
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Secret {
    pub metadata: ObjectMeta,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub r#type: Option<String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub data: HashMap<String, Vec<u8>>, // base64-encoded data
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub string_data: HashMap<String, String>, // non-base64 data
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pod_serialization() {
        let pod = Pod {
            metadata: ObjectMeta {
                name: "test-pod".to_string(),
                namespace: Some("default".to_string()),
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: HashMap::new(),
                annotations: HashMap::new(),
                org_id: Some("org-123".to_string()),
                project_id: Some("proj-456".to_string()),
            },
            spec: PodSpec {
                containers: vec![Container {
                    name: "nginx".to_string(),
                    image: "nginx:latest".to_string(),
                    command: vec![],
                    args: vec![],
                    ports: vec![],
                    env: vec![],
                    resources: None,
                }],
                restart_policy: Some("Always".to_string()),
                node_name: None,
            },
            status: None,
        };

        let json = serde_json::to_string_pretty(&pod).unwrap();
        assert!(json.contains("test-pod"));
        assert!(json.contains("org-123"));
    }

    #[test]
    fn test_service_serialization() {
        let service = Service {
            metadata: ObjectMeta {
                name: "test-service".to_string(),
                namespace: Some("default".to_string()),
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: HashMap::new(),
                annotations: HashMap::new(),
                org_id: None,
                project_id: None,
            },
            spec: ServiceSpec {
                ports: vec![ServicePort {
                    name: Some("http".to_string()),
                    port: 80,
                    target_port: Some(8080),
                    protocol: Some("TCP".to_string()),
                }],
                selector: HashMap::from([("app".to_string(), "nginx".to_string())]),
                cluster_ip: Some("10.96.0.1".to_string()),
                r#type: Some("ClusterIP".to_string()),
            },
            status: None,
        };

        let json = serde_json::to_string_pretty(&service).unwrap();
        assert!(json.contains("test-service"));
        assert!(json.contains("ClusterIP"));
    }
}
