//! REST HTTP API handlers for k8shost
//!
//! Implements REST endpoints as specified in T050.S6:
//! - GET /api/v1/pods - List pods
//! - POST /api/v1/pods - Create pod
//! - DELETE /api/v1/pods/{namespace}/{name} - Delete pod
//! - GET /api/v1/services - List services
//! - POST /api/v1/services - Create service
//! - GET /api/v1/deployments - List deployments
//! - POST /api/v1/deployments - Create deployment
//! - GET /api/v1/deployments/{namespace}/{name} - Get deployment
//! - PUT /api/v1/deployments/{namespace}/{name} - Update deployment
//! - DELETE /api/v1/deployments/{namespace}/{name} - Delete deployment
//! - GET /api/v1/nodes - List nodes
//! - GET /health - Health check

use axum::{
    extract::{Path, Query, State},
    http::HeaderMap,
    http::StatusCode,
    routing::{delete, get},
    Json, Router,
};
use iam_service_auth::{resolve_tenant_ids_from_context, AuthService, TenantContext};
use k8shost_proto::{
    deployment_service_server::DeploymentService, node_service_server::NodeService,
    pod_service_server::PodService, service_service_server::ServiceService, Container,
    ContainerPort, CreateDeploymentRequest, CreatePodRequest, CreateServiceRequest,
    DeleteDeploymentRequest, DeletePodRequest, DeleteServiceRequest, Deployment as ProtoDeployment,
    DeploymentSpec, EnvVar, GetDeploymentRequest, LabelSelector, ListDeploymentsRequest,
    ListNodesRequest, ListPodsRequest, ListServicesRequest, Node as ProtoNode, ObjectMeta,
    Pod as ProtoPod, PodSpec, PodTemplateSpec, Service as ProtoService, ServicePort, ServiceSpec,
    UpdateDeploymentRequest,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tonic::{Code, Request};

use crate::services::{
    deployment::DeploymentServiceImpl, node::NodeServiceImpl, pod::PodServiceImpl,
    service::ServiceServiceImpl,
};

/// REST API state
#[derive(Clone)]
pub struct RestApiState {
    pub deployment_service: Arc<DeploymentServiceImpl>,
    pub pod_service: Arc<PodServiceImpl>,
    pub service_service: Arc<ServiceServiceImpl>,
    pub node_service: Arc<NodeServiceImpl>,
    pub auth_service: Arc<AuthService>,
}

/// Standard REST error response
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
    pub error: ErrorDetail,
    pub meta: ResponseMeta,
}

#[derive(Debug, Serialize)]
pub struct ErrorDetail {
    pub code: String,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct ResponseMeta {
    pub request_id: String,
    pub timestamp: String,
}

impl ResponseMeta {
    fn new() -> Self {
        Self {
            request_id: uuid::Uuid::new_v4().to_string(),
            timestamp: chrono::Utc::now().to_rfc3339(),
        }
    }
}

/// Standard REST success response
#[derive(Debug, Serialize)]
pub struct SuccessResponse<T> {
    pub data: T,
    pub meta: ResponseMeta,
}

impl<T> SuccessResponse<T> {
    fn new(data: T) -> Self {
        Self {
            data,
            meta: ResponseMeta::new(),
        }
    }
}

/// Pod creation request
#[derive(Debug, Deserialize)]
pub struct CreatePodRequestRest {
    pub name: String,
    pub namespace: Option<String>,
    pub image: String,
    pub command: Option<Vec<String>>,
    pub args: Option<Vec<String>>,
}

/// Service creation request
#[derive(Debug, Deserialize)]
pub struct CreateServiceRequestRest {
    pub name: String,
    pub namespace: Option<String>,
    pub service_type: Option<String>,
    pub port: i32,
    pub target_port: Option<i32>,
    pub selector: Option<HashMap<String, String>>,
}

/// Deployment creation request
#[derive(Debug, Deserialize)]
pub struct CreateDeploymentRequestRest {
    pub name: String,
    pub namespace: Option<String>,
    pub replicas: Option<i32>,
    pub selector: HashMap<String, String>,
    pub template_labels: Option<HashMap<String, String>>,
    pub containers: Vec<DeploymentContainerRequestRest>,
}

/// Deployment update request
#[derive(Debug, Deserialize)]
pub struct UpdateDeploymentRequestRest {
    pub replicas: Option<i32>,
    pub selector: Option<HashMap<String, String>>,
    pub template_labels: Option<HashMap<String, String>>,
    pub containers: Option<Vec<DeploymentContainerRequestRest>>,
}

#[derive(Debug, Deserialize)]
pub struct DeploymentContainerRequestRest {
    pub name: String,
    pub image: String,
    pub command: Option<Vec<String>>,
    pub args: Option<Vec<String>>,
    pub ports: Option<Vec<DeploymentContainerPortRequestRest>>,
    pub env: Option<Vec<DeploymentEnvVarRequestRest>>,
}

#[derive(Debug, Deserialize)]
pub struct DeploymentContainerPortRequestRest {
    pub name: Option<String>,
    pub container_port: i32,
    pub protocol: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct DeploymentEnvVarRequestRest {
    pub name: String,
    pub value: Option<String>,
}

/// Query params for list operations
#[derive(Debug, Deserialize)]
pub struct ListParams {
    pub namespace: Option<String>,
}

/// Pod response
#[derive(Debug, Serialize)]
pub struct PodResponse {
    pub name: String,
    pub namespace: String,
    pub phase: String,
    pub ip: Option<String>,
}

impl From<ProtoPod> for PodResponse {
    fn from(pod: ProtoPod) -> Self {
        let phase = pod
            .status
            .as_ref()
            .and_then(|s| s.phase.clone())
            .unwrap_or_else(|| "Unknown".to_string());
        let ip = pod.status.as_ref().and_then(|s| s.pod_ip.clone());
        let name = pod
            .metadata
            .as_ref()
            .map(|m| m.name.clone())
            .unwrap_or_default();
        let namespace = pod
            .metadata
            .as_ref()
            .and_then(|m| m.namespace.clone())
            .unwrap_or_else(|| "default".to_string());
        Self {
            name,
            namespace,
            phase,
            ip,
        }
    }
}

/// Service response
#[derive(Debug, Serialize)]
pub struct ServiceResponse {
    pub name: String,
    pub namespace: String,
    pub service_type: String,
    pub cluster_ip: Option<String>,
    pub ports: Vec<ServicePortResponse>,
}

#[derive(Debug, Serialize)]
pub struct ServicePortResponse {
    pub port: i32,
    pub target_port: i32,
    pub protocol: String,
}

impl From<ProtoService> for ServiceResponse {
    fn from(svc: ProtoService) -> Self {
        let ports = svc
            .spec
            .as_ref()
            .map(|s| {
                s.ports
                    .iter()
                    .map(|p| ServicePortResponse {
                        port: p.port,
                        target_port: p.target_port.unwrap_or(p.port),
                        protocol: p.protocol.clone().unwrap_or_else(|| "TCP".to_string()),
                    })
                    .collect()
            })
            .unwrap_or_default();

        let name = svc
            .metadata
            .as_ref()
            .map(|m| m.name.clone())
            .unwrap_or_default();
        let namespace = svc
            .metadata
            .as_ref()
            .and_then(|m| m.namespace.clone())
            .unwrap_or_else(|| "default".to_string());
        let service_type = svc
            .spec
            .as_ref()
            .and_then(|s| s.r#type.clone())
            .unwrap_or_else(|| "ClusterIP".to_string());
        let cluster_ip = svc
            .spec
            .as_ref()
            .and_then(|s| s.cluster_ip.as_ref())
            .cloned();

        Self {
            name,
            namespace,
            service_type,
            cluster_ip,
            ports,
        }
    }
}

/// Node response
#[derive(Debug, Serialize)]
pub struct NodeResponse {
    pub name: String,
    pub ready: bool,
    pub cpu_capacity: Option<String>,
    pub memory_capacity: Option<String>,
}

impl From<ProtoNode> for NodeResponse {
    fn from(node: ProtoNode) -> Self {
        let ready = node
            .status
            .as_ref()
            .map(|s| {
                s.conditions
                    .iter()
                    .any(|c| c.r#type == "Ready" && c.status == "True")
            })
            .unwrap_or(false);
        let name = node
            .metadata
            .as_ref()
            .map(|m| m.name.clone())
            .unwrap_or_default();
        let cpu_capacity = node
            .status
            .as_ref()
            .and_then(|s| s.capacity.get("cpu").cloned());
        let memory_capacity = node
            .status
            .as_ref()
            .and_then(|s| s.capacity.get("memory").cloned());
        Self {
            name,
            ready,
            cpu_capacity,
            memory_capacity,
        }
    }
}

/// Pods list response
#[derive(Debug, Serialize)]
pub struct PodsResponse {
    pub pods: Vec<PodResponse>,
}

/// Services list response
#[derive(Debug, Serialize)]
pub struct ServicesResponse {
    pub services: Vec<ServiceResponse>,
}

/// Nodes list response
#[derive(Debug, Serialize)]
pub struct NodesResponse {
    pub nodes: Vec<NodeResponse>,
}

/// Deployment response
#[derive(Debug, Serialize)]
pub struct DeploymentResponse {
    pub name: String,
    pub namespace: String,
    pub replicas: i32,
    pub ready_replicas: i32,
    pub available_replicas: i32,
    pub selector: HashMap<String, String>,
    pub template_labels: HashMap<String, String>,
    pub containers: Vec<DeploymentContainerResponse>,
}

#[derive(Debug, Serialize)]
pub struct DeploymentContainerResponse {
    pub name: String,
    pub image: String,
    pub command: Vec<String>,
    pub args: Vec<String>,
    pub ports: Vec<DeploymentContainerPortResponse>,
    pub env: Vec<DeploymentEnvVarResponse>,
}

#[derive(Debug, Serialize)]
pub struct DeploymentContainerPortResponse {
    pub name: Option<String>,
    pub container_port: i32,
    pub protocol: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct DeploymentEnvVarResponse {
    pub name: String,
    pub value: Option<String>,
}

impl From<ProtoDeployment> for DeploymentResponse {
    fn from(deployment: ProtoDeployment) -> Self {
        let metadata = deployment.metadata.unwrap_or(ObjectMeta {
            name: String::new(),
            namespace: None,
            uid: None,
            resource_version: None,
            creation_timestamp: None,
            labels: HashMap::new(),
            annotations: HashMap::new(),
            org_id: None,
            project_id: None,
        });
        let DeploymentSpec {
            replicas,
            selector,
            template,
        } = deployment.spec.unwrap_or(DeploymentSpec {
            replicas: None,
            selector: None,
            template: None,
        });
        let template = template.unwrap_or(PodTemplateSpec {
            metadata: Some(ObjectMeta {
                name: String::new(),
                namespace: None,
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: HashMap::new(),
                annotations: HashMap::new(),
                org_id: None,
                project_id: None,
            }),
            spec: Some(PodSpec {
                containers: Vec::new(),
                restart_policy: None,
                node_name: None,
            }),
        });
        let template_metadata = template.metadata.unwrap_or(ObjectMeta {
            name: String::new(),
            namespace: None,
            uid: None,
            resource_version: None,
            creation_timestamp: None,
            labels: HashMap::new(),
            annotations: HashMap::new(),
            org_id: None,
            project_id: None,
        });
        let template_spec = template.spec.unwrap_or(PodSpec {
            containers: Vec::new(),
            restart_policy: None,
            node_name: None,
        });
        let status = deployment.status;

        Self {
            name: metadata.name,
            namespace: metadata.namespace.unwrap_or_else(|| "default".to_string()),
            replicas: replicas.unwrap_or(1),
            ready_replicas: status
                .as_ref()
                .and_then(|status| status.ready_replicas)
                .unwrap_or(0),
            available_replicas: status
                .as_ref()
                .and_then(|status| status.available_replicas)
                .unwrap_or(0),
            selector: selector
                .map(|selector| selector.match_labels)
                .unwrap_or_default(),
            template_labels: template_metadata.labels,
            containers: template_spec
                .containers
                .into_iter()
                .map(|container| DeploymentContainerResponse {
                    name: container.name,
                    image: container.image,
                    command: container.command,
                    args: container.args,
                    ports: container
                        .ports
                        .into_iter()
                        .map(|port| DeploymentContainerPortResponse {
                            name: port.name,
                            container_port: port.container_port,
                            protocol: port.protocol,
                        })
                        .collect(),
                    env: container
                        .env
                        .into_iter()
                        .map(|env| DeploymentEnvVarResponse {
                            name: env.name,
                            value: env.value,
                        })
                        .collect(),
                })
                .collect(),
        }
    }
}

/// Deployments list response
#[derive(Debug, Serialize)]
pub struct DeploymentsResponse {
    pub deployments: Vec<DeploymentResponse>,
}

/// Build the REST API router
pub fn build_router(state: RestApiState) -> Router {
    Router::new()
        .route("/api/v1/pods", get(list_pods).post(create_pod))
        .route("/api/v1/pods/{namespace}/{name}", delete(delete_pod))
        .route("/api/v1/services", get(list_services).post(create_service))
        .route(
            "/api/v1/deployments",
            get(list_deployments).post(create_deployment),
        )
        .route(
            "/api/v1/deployments/{namespace}/{name}",
            get(get_deployment)
                .put(update_deployment)
                .delete(delete_deployment),
        )
        .route(
            "/api/v1/services/{namespace}/{name}",
            delete(delete_service),
        )
        .route("/api/v1/nodes", get(list_nodes))
        .route("/health", get(health_check))
        .with_state(state)
}

/// Health check endpoint
async fn health_check() -> (StatusCode, Json<SuccessResponse<serde_json::Value>>) {
    (
        StatusCode::OK,
        Json(SuccessResponse::new(
            serde_json::json!({ "status": "healthy" }),
        )),
    )
}

/// GET /api/v1/pods - List pods
async fn list_pods(
    State(state): State<RestApiState>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<PodsResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(ListPodsRequest {
        namespace: params.namespace,
        label_selector: Default::default(),
    });
    req.extensions_mut().insert(tenant);

    let response = state
        .pod_service
        .list_pods(req)
        .await
        .map_err(map_tonic_status)?;

    let pods: Vec<PodResponse> = response
        .into_inner()
        .items
        .into_iter()
        .map(PodResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(PodsResponse { pods })))
}

/// POST /api/v1/pods - Create pod
async fn create_pod(
    State(state): State<RestApiState>,
    headers: HeaderMap,
    Json(req): Json<CreatePodRequestRest>,
) -> Result<(StatusCode, Json<SuccessResponse<PodResponse>>), (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let namespace = req.namespace.unwrap_or_else(|| "default".to_string());

    let mut grpc_req = Request::new(CreatePodRequest {
        pod: Some(ProtoPod {
            metadata: Some(ObjectMeta {
                name: req.name.clone(),
                namespace: Some(namespace.clone()),
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: Default::default(),
                annotations: Default::default(),
                org_id: None,
                project_id: None,
            }),
            spec: Some(PodSpec {
                containers: vec![Container {
                    name: req.name.clone(),
                    image: req.image,
                    command: req.command.unwrap_or_default(),
                    args: req.args.unwrap_or_default(),
                    ports: vec![],
                    env: vec![],
                }],
                restart_policy: None,
                node_name: None,
            }),
            status: None,
        }),
    });
    grpc_req.extensions_mut().insert(tenant);

    let response = state
        .pod_service
        .create_pod(grpc_req)
        .await
        .map_err(map_tonic_status)?;

    let pod = response.into_inner().pod.ok_or_else(|| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "CREATE_FAILED",
            "No pod returned",
        )
    })?;

    Ok((
        StatusCode::CREATED,
        Json(SuccessResponse::new(PodResponse::from(pod))),
    ))
}

/// DELETE /api/v1/pods/{namespace}/{name} - Delete pod
async fn delete_pod(
    State(state): State<RestApiState>,
    Path((namespace, name)): Path<(String, String)>,
    headers: HeaderMap,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)>
{
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(DeletePodRequest {
        name: name.clone(),
        namespace: namespace.clone(),
    });
    req.extensions_mut().insert(tenant);

    state
        .pod_service
        .delete_pod(req)
        .await
        .map_err(map_tonic_status)?;

    Ok((
        StatusCode::OK,
        Json(SuccessResponse::new(
            serde_json::json!({ "name": name, "namespace": namespace, "deleted": true }),
        )),
    ))
}

/// GET /api/v1/services - List services
async fn list_services(
    State(state): State<RestApiState>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<ServicesResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(ListServicesRequest {
        namespace: params.namespace,
    });
    req.extensions_mut().insert(tenant);

    let response = state
        .service_service
        .list_services(req)
        .await
        .map_err(map_tonic_status)?;

    let services: Vec<ServiceResponse> = response
        .into_inner()
        .items
        .into_iter()
        .map(ServiceResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(ServicesResponse { services })))
}

/// POST /api/v1/services - Create service
async fn create_service(
    State(state): State<RestApiState>,
    headers: HeaderMap,
    Json(req): Json<CreateServiceRequestRest>,
) -> Result<(StatusCode, Json<SuccessResponse<ServiceResponse>>), (StatusCode, Json<ErrorResponse>)>
{
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let namespace = req.namespace.unwrap_or_else(|| "default".to_string());
    let service_type = req.service_type.unwrap_or_else(|| "ClusterIP".to_string());

    let mut grpc_req = Request::new(CreateServiceRequest {
        service: Some(ProtoService {
            metadata: Some(ObjectMeta {
                name: req.name.clone(),
                namespace: Some(namespace.clone()),
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: Default::default(),
                annotations: Default::default(),
                org_id: None,
                project_id: None,
            }),
            spec: Some(ServiceSpec {
                r#type: Some(service_type),
                cluster_ip: None,
                ports: vec![ServicePort {
                    name: None,
                    protocol: Some("TCP".to_string()),
                    port: req.port,
                    target_port: req.target_port,
                }],
                selector: req.selector.unwrap_or_default(),
            }),
            status: None,
        }),
    });
    grpc_req.extensions_mut().insert(tenant);

    let response = state
        .service_service
        .create_service(grpc_req)
        .await
        .map_err(map_tonic_status)?;

    let service = response.into_inner().service.ok_or_else(|| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "CREATE_FAILED",
            "No service returned",
        )
    })?;

    Ok((
        StatusCode::CREATED,
        Json(SuccessResponse::new(ServiceResponse::from(service))),
    ))
}

/// DELETE /api/v1/services/{namespace}/{name} - Delete service
async fn delete_service(
    State(state): State<RestApiState>,
    Path((namespace, name)): Path<(String, String)>,
    headers: HeaderMap,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)>
{
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(DeleteServiceRequest {
        name: name.clone(),
        namespace: namespace.clone(),
    });
    req.extensions_mut().insert(tenant);

    state
        .service_service
        .delete_service(req)
        .await
        .map_err(map_tonic_status)?;

    Ok((
        StatusCode::OK,
        Json(SuccessResponse::new(
            serde_json::json!({ "name": name, "namespace": namespace, "deleted": true }),
        )),
    ))
}

/// GET /api/v1/nodes - List nodes
async fn list_nodes(
    State(state): State<RestApiState>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<NodesResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(ListNodesRequest {});
    req.extensions_mut().insert(tenant);

    let response = state
        .node_service
        .list_nodes(req)
        .await
        .map_err(map_tonic_status)?;

    let nodes: Vec<NodeResponse> = response
        .into_inner()
        .items
        .into_iter()
        .map(NodeResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(NodesResponse { nodes })))
}

/// GET /api/v1/deployments - List deployments
async fn list_deployments(
    State(state): State<RestApiState>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<DeploymentsResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(ListDeploymentsRequest {
        namespace: params.namespace,
    });
    req.extensions_mut().insert(tenant);

    let response = state
        .deployment_service
        .list_deployments(req)
        .await
        .map_err(map_tonic_status)?;

    let deployments = response
        .into_inner()
        .items
        .into_iter()
        .map(DeploymentResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(DeploymentsResponse {
        deployments,
    })))
}

/// GET /api/v1/deployments/{namespace}/{name} - Get deployment
async fn get_deployment(
    State(state): State<RestApiState>,
    Path((namespace, name)): Path<(String, String)>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<DeploymentResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(GetDeploymentRequest { namespace, name });
    req.extensions_mut().insert(tenant);

    let response = state
        .deployment_service
        .get_deployment(req)
        .await
        .map_err(map_tonic_status)?;
    let deployment = response.into_inner().deployment.ok_or_else(|| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "INTERNAL",
            "No deployment returned",
        )
    })?;

    Ok(Json(SuccessResponse::new(DeploymentResponse::from(
        deployment,
    ))))
}

/// POST /api/v1/deployments - Create deployment
async fn create_deployment(
    State(state): State<RestApiState>,
    headers: HeaderMap,
    Json(req): Json<CreateDeploymentRequestRest>,
) -> Result<
    (StatusCode, Json<SuccessResponse<DeploymentResponse>>),
    (StatusCode, Json<ErrorResponse>),
> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut grpc_req = Request::new(CreateDeploymentRequest {
        deployment: Some(build_proto_deployment(
            req.name,
            req.namespace,
            req.replicas,
            req.selector,
            req.template_labels,
            req.containers,
        )),
    });
    grpc_req.extensions_mut().insert(tenant);

    let response = state
        .deployment_service
        .create_deployment(grpc_req)
        .await
        .map_err(map_tonic_status)?;
    let deployment = response.into_inner().deployment.ok_or_else(|| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "INTERNAL",
            "No deployment returned",
        )
    })?;

    Ok((
        StatusCode::CREATED,
        Json(SuccessResponse::new(DeploymentResponse::from(deployment))),
    ))
}

/// PUT /api/v1/deployments/{namespace}/{name} - Update deployment
async fn update_deployment(
    State(state): State<RestApiState>,
    Path((namespace, name)): Path<(String, String)>,
    headers: HeaderMap,
    Json(req): Json<UpdateDeploymentRequestRest>,
) -> Result<Json<SuccessResponse<DeploymentResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers).await?;

    let mut get_req = Request::new(GetDeploymentRequest {
        namespace: namespace.clone(),
        name: name.clone(),
    });
    get_req.extensions_mut().insert(tenant.clone());
    let existing = state
        .deployment_service
        .get_deployment(get_req)
        .await
        .map_err(map_tonic_status)?
        .into_inner()
        .deployment
        .ok_or_else(|| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL",
                "No deployment returned",
            )
        })?;

    let mut deployment = existing;
    let spec = deployment.spec.get_or_insert(DeploymentSpec {
        replicas: None,
        selector: None,
        template: None,
    });

    if let Some(replicas) = req.replicas {
        spec.replicas = Some(replicas);
    }
    if let Some(selector) = req.selector {
        spec.selector = Some(LabelSelector {
            match_labels: selector,
        });
    }

    let template = spec.template.get_or_insert(PodTemplateSpec {
        metadata: Some(ObjectMeta {
            name: String::new(),
            namespace: Some(namespace.clone()),
            uid: None,
            resource_version: None,
            creation_timestamp: None,
            labels: HashMap::new(),
            annotations: HashMap::new(),
            org_id: None,
            project_id: None,
        }),
        spec: Some(PodSpec {
            containers: Vec::new(),
            restart_policy: None,
            node_name: None,
        }),
    });

    if let Some(template_labels) = req.template_labels {
        template
            .metadata
            .get_or_insert_with(|| ObjectMeta {
                name: String::new(),
                namespace: Some(namespace.clone()),
                uid: None,
                resource_version: None,
                creation_timestamp: None,
                labels: HashMap::new(),
                annotations: HashMap::new(),
                org_id: None,
                project_id: None,
            })
            .labels = template_labels;
    }
    if let Some(containers) = req.containers {
        template
            .spec
            .get_or_insert(PodSpec {
                containers: Vec::new(),
                restart_policy: None,
                node_name: None,
            })
            .containers = containers
            .into_iter()
            .map(proto_container_from_rest)
            .collect();
    }

    ensure_selector_labels_on_template(spec);

    let metadata = deployment.metadata.get_or_insert(ObjectMeta {
        name: name.clone(),
        namespace: Some(namespace.clone()),
        uid: None,
        resource_version: None,
        creation_timestamp: None,
        labels: HashMap::new(),
        annotations: HashMap::new(),
        org_id: None,
        project_id: None,
    });
    metadata.name = name;
    metadata.namespace = Some(namespace);

    let mut update_req = Request::new(UpdateDeploymentRequest {
        deployment: Some(deployment),
    });
    update_req.extensions_mut().insert(tenant);

    let response = state
        .deployment_service
        .update_deployment(update_req)
        .await
        .map_err(map_tonic_status)?;
    let deployment = response.into_inner().deployment.ok_or_else(|| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "INTERNAL",
            "No deployment returned",
        )
    })?;

    Ok(Json(SuccessResponse::new(DeploymentResponse::from(
        deployment,
    ))))
}

/// DELETE /api/v1/deployments/{namespace}/{name} - Delete deployment
async fn delete_deployment(
    State(state): State<RestApiState>,
    Path((namespace, name)): Path<(String, String)>,
    headers: HeaderMap,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)>
{
    let tenant = resolve_rest_tenant(&state, &headers).await?;
    let mut req = Request::new(DeleteDeploymentRequest {
        namespace: namespace.clone(),
        name: name.clone(),
    });
    req.extensions_mut().insert(tenant);

    state
        .deployment_service
        .delete_deployment(req)
        .await
        .map_err(map_tonic_status)?;

    Ok((
        StatusCode::OK,
        Json(SuccessResponse::new(serde_json::json!({
            "name": name,
            "namespace": namespace,
            "deleted": true
        }))),
    ))
}

fn build_proto_deployment(
    name: String,
    namespace: Option<String>,
    replicas: Option<i32>,
    selector: HashMap<String, String>,
    template_labels: Option<HashMap<String, String>>,
    containers: Vec<DeploymentContainerRequestRest>,
) -> ProtoDeployment {
    let namespace = namespace.unwrap_or_else(|| "default".to_string());
    let mut labels = template_labels.unwrap_or_else(|| selector.clone());
    for (key, value) in &selector {
        labels.entry(key.clone()).or_insert_with(|| value.clone());
    }

    ProtoDeployment {
        metadata: Some(ObjectMeta {
            name,
            namespace: Some(namespace.clone()),
            uid: None,
            resource_version: None,
            creation_timestamp: None,
            labels: HashMap::new(),
            annotations: HashMap::new(),
            org_id: None,
            project_id: None,
        }),
        spec: Some(DeploymentSpec {
            replicas,
            selector: Some(LabelSelector {
                match_labels: selector,
            }),
            template: Some(PodTemplateSpec {
                metadata: Some(ObjectMeta {
                    name: String::new(),
                    namespace: Some(namespace),
                    uid: None,
                    resource_version: None,
                    creation_timestamp: None,
                    labels,
                    annotations: HashMap::new(),
                    org_id: None,
                    project_id: None,
                }),
                spec: Some(PodSpec {
                    containers: containers
                        .into_iter()
                        .map(proto_container_from_rest)
                        .collect(),
                    restart_policy: Some("Always".to_string()),
                    node_name: None,
                }),
            }),
        }),
        status: None,
    }
}

fn proto_container_from_rest(container: DeploymentContainerRequestRest) -> Container {
    Container {
        name: container.name,
        image: container.image,
        command: container.command.unwrap_or_default(),
        args: container.args.unwrap_or_default(),
        ports: container
            .ports
            .unwrap_or_default()
            .into_iter()
            .map(|port| ContainerPort {
                name: port.name,
                container_port: port.container_port,
                protocol: port.protocol,
            })
            .collect(),
        env: container
            .env
            .unwrap_or_default()
            .into_iter()
            .map(|env| EnvVar {
                name: env.name,
                value: env.value,
            })
            .collect(),
    }
}

fn ensure_selector_labels_on_template(spec: &mut DeploymentSpec) {
    let selector = spec
        .selector
        .as_ref()
        .map(|selector| selector.match_labels.clone())
        .unwrap_or_default();
    let template = spec.template.get_or_insert(PodTemplateSpec {
        metadata: Some(ObjectMeta {
            name: String::new(),
            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: Some(PodSpec {
            containers: Vec::new(),
            restart_policy: None,
            node_name: None,
        }),
    });
    let metadata = template.metadata.get_or_insert_with(|| ObjectMeta {
        name: String::new(),
        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,
    });
    for (key, value) in selector {
        metadata.labels.entry(key).or_insert(value);
    }
}

/// Helper to create error response
fn error_response(
    status: StatusCode,
    code: &str,
    message: &str,
) -> (StatusCode, Json<ErrorResponse>) {
    (
        status,
        Json(ErrorResponse {
            error: ErrorDetail {
                code: code.to_string(),
                message: message.to_string(),
                details: None,
            },
            meta: ResponseMeta::new(),
        }),
    )
}

async fn resolve_rest_tenant(
    state: &RestApiState,
    headers: &HeaderMap,
) -> Result<TenantContext, (StatusCode, Json<ErrorResponse>)> {
    let tenant = state
        .auth_service
        .authenticate_headers(headers)
        .await
        .map_err(map_tonic_status)?;
    resolve_tenant_ids_from_context(&tenant, "", "").map_err(map_tonic_status)?;
    Ok(tenant)
}

fn map_tonic_status(status: tonic::Status) -> (StatusCode, Json<ErrorResponse>) {
    let status_code = match status.code() {
        Code::Unauthenticated => StatusCode::UNAUTHORIZED,
        Code::PermissionDenied => StatusCode::FORBIDDEN,
        Code::InvalidArgument => StatusCode::BAD_REQUEST,
        Code::NotFound => StatusCode::NOT_FOUND,
        Code::AlreadyExists => StatusCode::CONFLICT,
        Code::FailedPrecondition => StatusCode::PRECONDITION_FAILED,
        Code::ResourceExhausted => StatusCode::TOO_MANY_REQUESTS,
        Code::DeadlineExceeded => StatusCode::GATEWAY_TIMEOUT,
        Code::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
        _ => StatusCode::INTERNAL_SERVER_ERROR,
    };
    let code = match status.code() {
        Code::Unauthenticated => "UNAUTHENTICATED",
        Code::PermissionDenied => "FORBIDDEN",
        Code::InvalidArgument => "INVALID_ARGUMENT",
        Code::NotFound => "NOT_FOUND",
        Code::AlreadyExists => "ALREADY_EXISTS",
        Code::FailedPrecondition => "FAILED_PRECONDITION",
        Code::ResourceExhausted => "RESOURCE_EXHAUSTED",
        Code::DeadlineExceeded => "DEADLINE_EXCEEDED",
        Code::Unavailable => "UNAVAILABLE",
        _ => "INTERNAL",
    };

    error_response(status_code, code, status.message())
}
