//! REST HTTP API handlers for PrismNET
//!
//! Implements REST endpoints as specified in T050.S8:
//! - GET /api/v1/vpcs - List VPCs
//! - POST /api/v1/vpcs - Create VPC
//! - GET /api/v1/vpcs/{id} - Get VPC
//! - DELETE /api/v1/vpcs/{id} - Delete VPC
//! - GET /api/v1/subnets - List Subnets
//! - POST /api/v1/subnets - Create Subnet
//! - DELETE /api/v1/subnets/{id} - Delete Subnet
//! - GET /health - Health check

use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    http::HeaderMap,
    routing::{delete, get},
    Json, Router,
};
use prismnet_api::{
    vpc_service_server::VpcService,
    subnet_service_server::SubnetService,
    CreateVpcRequest, GetVpcRequest, ListVpcsRequest, DeleteVpcRequest,
    CreateSubnetRequest, ListSubnetsRequest, DeleteSubnetRequest,
    Vpc as ProtoVpc, Subnet as ProtoSubnet,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tonic::Request;
use tonic::Code;

use iam_service_auth::{resolve_tenant_ids_from_context, AuthService, TenantContext};
use crate::{VpcServiceImpl, SubnetServiceImpl};

/// REST API state
#[derive(Clone)]
pub struct RestApiState {
    pub vpc_service: Arc<VpcServiceImpl>,
    pub subnet_service: Arc<SubnetServiceImpl>,
    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(),
        }
    }
}

/// Create VPC request
#[derive(Debug, Deserialize)]
pub struct CreateVpcRequestRest {
    pub name: String,
    pub org_id: Option<String>,
    pub project_id: Option<String>,
    pub cidr_block: String,
    pub description: Option<String>,
}

/// Create Subnet request
#[derive(Debug, Deserialize)]
pub struct CreateSubnetRequestRest {
    pub name: String,
    pub vpc_id: String,
    pub cidr_block: String,
    pub gateway_ip: Option<String>,
    pub description: Option<String>,
}

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

/// VPC response
#[derive(Debug, Serialize)]
pub struct VpcResponse {
    pub id: String,
    pub name: String,
    pub org_id: String,
    pub project_id: String,
    pub cidr_block: String,
    pub description: String,
    pub status: String,
}

impl From<ProtoVpc> for VpcResponse {
    fn from(vpc: ProtoVpc) -> Self {
        let status = match vpc.status {
            1 => "provisioning",
            2 => "active",
            3 => "updating",
            4 => "deleting",
            5 => "error",
            _ => "unknown",
        };
        Self {
            id: vpc.id,
            name: vpc.name,
            org_id: vpc.org_id,
            project_id: vpc.project_id,
            cidr_block: vpc.cidr_block,
            description: vpc.description,
            status: status.to_string(),
        }
    }
}

/// Subnet response
#[derive(Debug, Serialize)]
pub struct SubnetResponse {
    pub id: String,
    pub name: String,
    pub vpc_id: String,
    pub cidr_block: String,
    pub gateway_ip: String,
    pub description: String,
    pub status: String,
}

impl From<ProtoSubnet> for SubnetResponse {
    fn from(s: ProtoSubnet) -> Self {
        let status = match s.status {
            1 => "provisioning",
            2 => "active",
            3 => "updating",
            4 => "deleting",
            5 => "error",
            _ => "unknown",
        };
        Self {
            id: s.id,
            name: s.name,
            vpc_id: s.vpc_id,
            cidr_block: s.cidr_block,
            gateway_ip: s.gateway_ip,
            description: s.description,
            status: status.to_string(),
        }
    }
}

/// VPCs list response
#[derive(Debug, Serialize)]
pub struct VpcsResponse {
    pub vpcs: Vec<VpcResponse>,
}

/// Subnets list response
#[derive(Debug, Serialize)]
pub struct SubnetsResponse {
    pub subnets: Vec<SubnetResponse>,
}

/// Build the REST API router
pub fn build_router(state: RestApiState) -> Router {
    Router::new()
        .route("/api/v1/vpcs", get(list_vpcs).post(create_vpc))
        .route("/api/v1/vpcs/{id}", get(get_vpc).delete(delete_vpc))
        .route("/api/v1/subnets", get(list_subnets).post(create_subnet))
        .route("/api/v1/subnets/{id}", delete(delete_subnet))
        .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/vpcs - List VPCs
async fn list_vpcs(
    State(state): State<RestApiState>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<VpcsResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, params.org_id.as_deref(), params.project_id.as_deref())
            .await?;
    let mut req = Request::new(ListVpcsRequest {
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
        page_size: 100,
        page_token: String::new(),
    });
    req.extensions_mut().insert(tenant);

    let response = state.vpc_service.list_vpcs(req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "LIST_FAILED", &e.message()))?;

    let vpcs: Vec<VpcResponse> = response.into_inner().vpcs.into_iter()
        .map(VpcResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(VpcsResponse { vpcs })))
}

/// POST /api/v1/vpcs - Create VPC
async fn create_vpc(
    State(state): State<RestApiState>,
    headers: HeaderMap,
    Json(req): Json<CreateVpcRequestRest>,
) -> Result<(StatusCode, Json<SuccessResponse<VpcResponse>>), (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, req.org_id.as_deref(), req.project_id.as_deref())
            .await?;
    let mut grpc_req = Request::new(CreateVpcRequest {
        name: req.name,
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
        cidr_block: req.cidr_block,
        description: req.description.unwrap_or_default(),
    });
    grpc_req.extensions_mut().insert(tenant);

    let response = state.vpc_service.create_vpc(grpc_req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "CREATE_FAILED", &e.message()))?;

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

    Ok((
        StatusCode::CREATED,
        Json(SuccessResponse::new(VpcResponse::from(vpc))),
    ))
}

/// GET /api/v1/vpcs/{id} - Get VPC
async fn get_vpc(
    State(state): State<RestApiState>,
    Path(id): Path<String>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<VpcResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, params.org_id.as_deref(), params.project_id.as_deref())
            .await?;
    let mut req = Request::new(GetVpcRequest {
        id,
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
    });
    req.extensions_mut().insert(tenant);

    let response = state.vpc_service.get_vpc(req)
        .await
        .map_err(|e| {
            if e.code() == tonic::Code::NotFound {
                error_response(StatusCode::NOT_FOUND, "NOT_FOUND", "VPC not found")
            } else {
                error_response(StatusCode::INTERNAL_SERVER_ERROR, "GET_FAILED", &e.message())
            }
        })?;

    let vpc = response.into_inner().vpc
        .ok_or_else(|| error_response(StatusCode::NOT_FOUND, "NOT_FOUND", "VPC not found"))?;

    Ok(Json(SuccessResponse::new(VpcResponse::from(vpc))))
}

/// DELETE /api/v1/vpcs/{id} - Delete VPC
async fn delete_vpc(
    State(state): State<RestApiState>,
    Path(id): Path<String>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, params.org_id.as_deref(), params.project_id.as_deref())
            .await?;
    let mut req = Request::new(DeleteVpcRequest {
        id: id.clone(),
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
    });
    req.extensions_mut().insert(tenant);

    state.vpc_service.delete_vpc(req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "DELETE_FAILED", &e.message()))?;

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

/// GET /api/v1/subnets - List Subnets
async fn list_subnets(
    State(state): State<RestApiState>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<Json<SuccessResponse<SubnetsResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, params.org_id.as_deref(), params.project_id.as_deref())
            .await?;
    let mut req = Request::new(ListSubnetsRequest {
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
        vpc_id: params.vpc_id.unwrap_or_default(),
        page_size: 100,
        page_token: String::new(),
    });
    req.extensions_mut().insert(tenant);

    let response = state.subnet_service.list_subnets(req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "LIST_FAILED", &e.message()))?;

    let subnets: Vec<SubnetResponse> = response.into_inner().subnets.into_iter()
        .map(SubnetResponse::from)
        .collect();

    Ok(Json(SuccessResponse::new(SubnetsResponse { subnets })))
}

/// POST /api/v1/subnets - Create Subnet
async fn create_subnet(
    State(state): State<RestApiState>,
    headers: HeaderMap,
    Json(req): Json<CreateSubnetRequestRest>,
) -> Result<(StatusCode, Json<SuccessResponse<SubnetResponse>>), (StatusCode, Json<ErrorResponse>)> {
    let tenant = resolve_rest_tenant(&state, &headers, None, None).await?;
    let mut grpc_req = Request::new(CreateSubnetRequest {
        vpc_id: req.vpc_id,
        name: req.name,
        cidr_block: req.cidr_block,
        gateway_ip: req.gateway_ip.unwrap_or_default(),
        description: req.description.unwrap_or_default(),
        dhcp_enabled: true,
    });
    grpc_req.extensions_mut().insert(tenant);

    let response = state.subnet_service.create_subnet(grpc_req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "CREATE_FAILED", &e.message()))?;

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

    Ok((
        StatusCode::CREATED,
        Json(SuccessResponse::new(SubnetResponse::from(subnet))),
    ))
}

/// DELETE /api/v1/subnets/{id} - Delete Subnet
async fn delete_subnet(
    State(state): State<RestApiState>,
    Path(id): Path<String>,
    Query(params): Query<ListParams>,
    headers: HeaderMap,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)> {
    let tenant =
        resolve_rest_tenant(&state, &headers, params.org_id.as_deref(), params.project_id.as_deref())
            .await?;
    let mut req = Request::new(DeleteSubnetRequest {
        id: id.clone(),
        org_id: tenant.org_id.clone(),
        project_id: tenant.project_id.clone(),
        vpc_id: params.vpc_id.unwrap_or_default(),
    });
    req.extensions_mut().insert(tenant);

    state.subnet_service.delete_subnet(req)
        .await
        .map_err(|e| error_response(StatusCode::INTERNAL_SERVER_ERROR, "DELETE_FAILED", &e.message()))?;

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

/// 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,
    req_org_id: Option<&str>,
    req_project_id: Option<&str>,
) -> Result<TenantContext, (StatusCode, Json<ErrorResponse>)> {
    let tenant = state
        .auth_service
        .authenticate_headers(headers)
        .await
        .map_err(map_auth_status)?;
    resolve_tenant_ids_from_context(
        &tenant,
        req_org_id.unwrap_or(""),
        req_project_id.unwrap_or(""),
    )
    .map_err(map_auth_status)?;

    Ok(tenant)
}

fn map_auth_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,
        _ => StatusCode::INTERNAL_SERVER_ERROR,
    };
    let code = match status.code() {
        Code::Unauthenticated => "UNAUTHENTICATED",
        Code::PermissionDenied => "FORBIDDEN",
        Code::InvalidArgument => "INVALID_ARGUMENT",
        Code::NotFound => "NOT_FOUND",
        _ => "INTERNAL",
    };

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