---
to: generated/src/api/{{name}}.rs
vars:
  name: "user-api"
  description: "User API endpoints"
  version: "1.0.0"
  base_path: "/api/v1"
sparql:
  find_endpoints: "SELECT ?endpoint ?path ?method WHERE { ?endpoint a ex:APIEndpoint ; ex:path ?path ; ex:method ?method }"
  find_parameters: "SELECT ?endpoint ?param ?name ?type WHERE { ?endpoint ex:parameters ?param . ?param ex:name ?name ; ex:type ?type }"
  find_responses: "SELECT ?endpoint ?response ?statusCode ?contentType WHERE { ?endpoint ex:response ?response . ?response ex:statusCode ?statusCode ; ex:contentType ?contentType }"
freeze_policy: "checksum"
freeze_slots_dir: "generated/.ggen/freeze"
---

//! {{description}}
//! 
//! This module provides REST API endpoints for {{name}} including:
//! - Comprehensive CRUD operations
//! - Request/response validation
//! - Error handling and status codes
//! - OpenAPI documentation
//! - Rate limiting and security
//! - Performance monitoring
//!
//! Generated by ggen on {{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}

use anyhow::{Context, Result};
use axum::{
    extract::{Path, Query, State},
    http::{HeaderMap, StatusCode},
    response::{Json, Response},
    routing::{get, post, put, delete, patch},
    Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio::time::{timeout, Duration};
use tracing::{info, warn, error, instrument};
use uuid::Uuid;
use tower::ServiceBuilder;
use tower_http::{
    cors::CorsLayer,
    trace::TraceLayer,
    timeout::TimeoutLayer,
    compression::CompressionLayer,
    rate_limit::RateLimitLayer,
};

/// API configuration
#[derive(Debug, Clone)]
pub struct {{name | title}}ApiConfig {
    pub base_path: String,
    pub timeout_seconds: u64,
    pub rate_limit_requests: u64,
    pub rate_limit_window_seconds: u64,
    pub enable_cors: bool,
    pub enable_compression: bool,
    pub enable_tracing: bool,
    pub max_request_size: usize,
}

impl Default for {{name | title}}ApiConfig {
    fn default() -> Self {
        Self {
            base_path: "{{base_path}}".to_string(),
            timeout_seconds: 30,
            rate_limit_requests: 100,
            rate_limit_window_seconds: 60,
            enable_cors: true,
            enable_compression: true,
            enable_tracing: true,
            max_request_size: 1024 * 1024, // 1MB
        }
    }
}

/// API state
#[derive(Debug, Clone)]
pub struct {{name | title}}ApiState {
    pub config: {{name | title}}ApiConfig,
    pub service: crate::services::{{name | replace(from="api", to="service") | title}}Service,
    pub metrics: HashMap<String, u64>,
}

/// Request models
#[derive(Debug, Deserialize, Serialize)]
pub struct Create{{name | title}}Request {
    pub name: String,
    pub email: String,
    pub metadata: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Update{{name | title}}Request {
    pub name: Option<String>,
    pub email: Option<String>,
    pub metadata: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct {{name | title}}Query {
    pub page: Option<u32>,
    pub limit: Option<u32>,
    pub sort: Option<String>,
    pub search: Option<String>,
    pub filter: Option<HashMap<String, serde_json::Value>>,
}

/// Response models
#[derive(Debug, Serialize)]
pub struct {{name | title}}Response<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub request_id: Uuid,
}

#[derive(Debug, Serialize)]
pub struct {{name | title}}Error {
    pub error: String,
    pub message: String,
    pub code: String,
    pub details: Option<HashMap<String, serde_json::Value>>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub request_id: Uuid,
}

#[derive(Debug, Serialize)]
pub struct Paginated{{name | title}}Response {
    pub items: Vec<crate::services::{{name | replace(from="api", to="service") | title}}Service::{{name | replace(from="api", to="service") | title}}>,
    pub pagination: PaginationInfo,
}

#[derive(Debug, Serialize)]
pub struct PaginationInfo {
    pub total: u64,
    pub page: u32,
    pub limit: u32,
    pub pages: u32,
    pub has_next: bool,
    pub has_prev: bool,
}

/// Health check response
#[derive(Debug, Serialize)]
pub struct HealthCheckResponse {
    pub status: String,
    pub version: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub uptime: u64,
    pub dependencies: HashMap<String, String>,
}

/// OpenAPI specification
#[derive(Debug, Serialize)]
pub struct OpenApiSpec {
    pub openapi: String,
    pub info: OpenApiInfo,
    pub paths: HashMap<String, OpenApiPath>,
    pub components: OpenApiComponents,
}

#[derive(Debug, Serialize)]
pub struct OpenApiInfo {
    pub title: String,
    pub version: String,
    pub description: String,
}

#[derive(Debug, Serialize)]
pub struct OpenApiPath {
    pub get: Option<OpenApiOperation>,
    pub post: Option<OpenApiOperation>,
    pub put: Option<OpenApiOperation>,
    pub delete: Option<OpenApiOperation>,
    pub patch: Option<OpenApiOperation>,
}

#[derive(Debug, Serialize)]
pub struct OpenApiOperation {
    pub summary: String,
    pub description: String,
    pub parameters: Option<Vec<OpenApiParameter>>,
    pub request_body: Option<OpenApiRequestBody>,
    pub responses: HashMap<String, OpenApiResponse>,
}

#[derive(Debug, Serialize)]
pub struct OpenApiParameter {
    pub name: String,
    pub r#in: String,
    pub required: bool,
    pub schema: OpenApiSchema,
}

#[derive(Debug, Serialize)]
pub struct OpenApiRequestBody {
    pub required: bool,
    pub content: HashMap<String, OpenApiContent>,
}

#[derive(Debug, Serialize)]
pub struct OpenApiResponse {
    pub description: String,
    pub content: Option<HashMap<String, OpenApiContent>>,
}

#[derive(Debug, Serialize)]
pub struct OpenApiContent {
    pub schema: OpenApiSchema,
}

#[derive(Debug, Serialize)]
pub struct OpenApiSchema {
    pub r#type: String,
    pub format: Option<String>,
    pub example: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct OpenApiComponents {
    pub schemas: HashMap<String, OpenApiSchema>,
}

/// API handlers
#[instrument(skip(state))]
pub async fn create_{{name}}_handler(
    State(state): State<{{name | title}}ApiState>,
    headers: HeaderMap,
    Json(request): Json<Create{{name | title}}Request>,
) -> Result<Json<{{name | title}}Response<crate::services::{{name | replace(from="api", to="service") | title}}Service::{{name | replace(from="api", to="service") | title}}>>, StatusCode> {
    let request_id = Uuid::new_v4();
    info!("Creating {{name}} with request ID: {}", request_id);

    // Validate request
    if let Err(e) = validate_create_request(&request) {
        error!("Validation failed for request {}: {}", request_id, e);
        return Err(StatusCode::BAD_REQUEST);
    }

    // Rate limiting check
    if let Err(_) = check_rate_limit(&state, &headers).await {
        warn!("Rate limit exceeded for request {}", request_id);
        return Err(StatusCode::TOO_MANY_REQUESTS);
    }

    // Execute with timeout
    match timeout(
        Duration::from_secs(state.config.timeout_seconds),
        state.service.create_{{name | replace(from="api", to="service")}}(request),
    )
    .await
    {
        Ok(Ok({{name}})) => {
            info!("Successfully created {{name}} with ID: {}", {{name}}.id);
            Ok(Json({{name | title}}Response {
                success: true,
                data: Some({{name}}),
                message: "{{name | title}} created successfully".to_string(),
                timestamp: chrono::Utc::now(),
                request_id,
            }))
        }
        Ok(Err(e)) => {
            error!("Failed to create {{name}} for request {}: {}", request_id, e);
            Err(StatusCode::BAD_REQUEST)
        }
        Err(_) => {
            error!("Timeout creating {{name}} for request {}", request_id);
            Err(StatusCode::REQUEST_TIMEOUT)
        }
    }
}

#[instrument(skip(state))]
pub async fn get_{{name}}_handler(
    State(state): State<{{name | title}}ApiState>,
    Path(id): Path<Uuid>,
    headers: HeaderMap,
) -> Result<Json<{{name | title}}Response<crate::services::{{name | replace(from="api", to="service") | title}}Service::{{name | replace(from="api", to="service") | title}}>>, StatusCode> {
    let request_id = Uuid::new_v4();
    info!("Getting {{name}} with ID: {} and request ID: {}", id, request_id);

    // Rate limiting check
    if let Err(_) = check_rate_limit(&state, &headers).await {
        warn!("Rate limit exceeded for request {}", request_id);
        return Err(StatusCode::TOO_MANY_REQUESTS);
    }

    // Execute with timeout
    match timeout(
        Duration::from_secs(state.config.timeout_seconds),
        state.service.get_{{name | replace(from="api", to="service")}}(id),
    )
    .await
    {
        Ok(Ok(Some({{name}}))) => {
            info!("Successfully retrieved {{name}} with ID: {}", {{name}}.id);
            Ok(Json({{name | title}}Response {
                success: true,
                data: Some({{name}}),
                message: "{{name | title}} retrieved successfully".to_string(),
                timestamp: chrono::Utc::now(),
                request_id,
            }))
        }
        Ok(Ok(None)) => {
            warn!("{{name | title}} not found with ID: {}", id);
            Err(StatusCode::NOT_FOUND)
        }
        Ok(Err(e)) => {
            error!("Failed to get {{name}} for request {}: {}", request_id, e);
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
        Err(_) => {
            error!("Timeout getting {{name}} for request {}", request_id);
            Err(StatusCode::REQUEST_TIMEOUT)
        }
    }
}

#[instrument(skip(state))]
pub async fn get_{{name}}s_handler(
    State(state): State<{{name | title}}ApiState>,
    Query(query): Query<{{name | title}}Query>,
    headers: HeaderMap,
) -> Result<Json<{{name | title}}Response<Paginated{{name | title}}Response>>, StatusCode> {
    let request_id = Uuid::new_v4();
    info!("Getting {{name}}s with query: {:?} and request ID: {}", query, request_id);

    // Rate limiting check
    if let Err(_) = check_rate_limit(&state, &headers).await {
        warn!("Rate limit exceeded for request {}", request_id);
        return Err(StatusCode::TOO_MANY_REQUESTS);
    }

    // Execute with timeout
    match timeout(
        Duration::from_secs(state.config.timeout_seconds),
        state.service.get_{{name | replace(from="api", to="service")}}s(query),
    )
    .await
    {
        Ok(Ok(response)) => {
            info!("Successfully retrieved {} {{name}}s", response.items.len());
            Ok(Json({{name | title}}Response {
                success: true,
                data: Some(Paginated{{name | title}}Response {
                    items: response.items,
                    pagination: PaginationInfo {
                        total: response.total,
                        page: response.page,
                        limit: response.limit,
                        pages: response.pages,
                        has_next: response.page < response.pages,
                        has_prev: response.page > 1,
                    },
                }),
                message: "{{name | title}}s retrieved successfully".to_string(),
                timestamp: chrono::Utc::now(),
                request_id,
            }))
        }
        Ok(Err(e)) => {
            error!("Failed to get {{name}}s for request {}: {}", request_id, e);
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
        Err(_) => {
            error!("Timeout getting {{name}}s for request {}", request_id);
            Err(StatusCode::REQUEST_TIMEOUT)
        }
    }
}

#[instrument(skip(state))]
pub async fn update_{{name}}_handler(
    State(state): State<{{name | title}}ApiState>,
    Path(id): Path<Uuid>,
    headers: HeaderMap,
    Json(request): Json<Update{{name | title}}Request>,
) -> Result<Json<{{name | title}}Response<crate::services::{{name | replace(from="api", to="service") | title}}Service::{{name | replace(from="api", to="service") | title}}>>, StatusCode> {
    let request_id = Uuid::new_v4();
    info!("Updating {{name}} with ID: {} and request ID: {}", id, request_id);

    // Validate request
    if let Err(e) = validate_update_request(&request) {
        error!("Validation failed for request {}: {}", request_id, e);
        return Err(StatusCode::BAD_REQUEST);
    }

    // Rate limiting check
    if let Err(_) = check_rate_limit(&state, &headers).await {
        warn!("Rate limit exceeded for request {}", request_id);
        return Err(StatusCode::TOO_MANY_REQUESTS);
    }

    // Execute with timeout
    match timeout(
        Duration::from_secs(state.config.timeout_seconds),
        state.service.update_{{name | replace(from="api", to="service")}}(id, request),
    )
    .await
    {
        Ok(Ok({{name}})) => {
            info!("Successfully updated {{name}} with ID: {}", {{name}}.id);
            Ok(Json({{name | title}}Response {
                success: true,
                data: Some({{name}}),
                message: "{{name | title}} updated successfully".to_string(),
                timestamp: chrono::Utc::now(),
                request_id,
            }))
        }
        Ok(Err(e)) => {
            error!("Failed to update {{name}} for request {}: {}", request_id, e);
            Err(StatusCode::BAD_REQUEST)
        }
        Err(_) => {
            error!("Timeout updating {{name}} for request {}", request_id);
            Err(StatusCode::REQUEST_TIMEOUT)
        }
    }
}

#[instrument(skip(state))]
pub async fn delete_{{name}}_handler(
    State(state): State<{{name | title}}ApiState>,
    Path(id): Path<Uuid>,
    headers: HeaderMap,
) -> Result<Json<{{name | title}}Response<()>>, StatusCode> {
    let request_id = Uuid::new_v4();
    info!("Deleting {{name}} with ID: {} and request ID: {}", id, request_id);

    // Rate limiting check
    if let Err(_) = check_rate_limit(&state, &headers).await {
        warn!("Rate limit exceeded for request {}", request_id);
        return Err(StatusCode::TOO_MANY_REQUESTS);
    }

    // Execute with timeout
    match timeout(
        Duration::from_secs(state.config.timeout_seconds),
        state.service.delete_{{name | replace(from="api", to="service")}}(id),
    )
    .await
    {
        Ok(Ok(())) => {
            info!("Successfully deleted {{name}} with ID: {}", id);
            Ok(Json({{name | title}}Response {
                success: true,
                data: Some(()),
                message: "{{name | title}} deleted successfully".to_string(),
                timestamp: chrono::Utc::now(),
                request_id,
            }))
        }
        Ok(Err(e)) => {
            error!("Failed to delete {{name}} for request {}: {}", request_id, e);
            Err(StatusCode::NOT_FOUND)
        }
        Err(_) => {
            error!("Timeout deleting {{name}} for request {}", request_id);
            Err(StatusCode::REQUEST_TIMEOUT)
        }
    }
}

#[instrument(skip(state))]
pub async fn health_check_handler(
    State(state): State<{{name | title}}ApiState>,
) -> Result<Json<HealthCheckResponse>, StatusCode> {
    let mut dependencies = HashMap::new();
    
    // Check database connection
    match state.service.db_pool().acquire().await {
        Ok(_) => {
            dependencies.insert("database".to_string(), "healthy".to_string());
        }
        Err(e) => {
            error!("Database health check failed: {}", e);
            dependencies.insert("database".to_string(), "unhealthy".to_string());
        }
    }

    Ok(Json(HealthCheckResponse {
        status: "healthy".to_string(),
        version: "{{version}}".to_string(),
        timestamp: chrono::Utc::now(),
        uptime: 0, // TODO: Implement uptime tracking
        dependencies,
    }))
}

#[instrument(skip(state))]
pub async fn openapi_spec_handler(
    State(state): State<{{name | title}}ApiState>,
) -> Result<Json<OpenApiSpec>, StatusCode> {
    let mut paths = HashMap::new();
    let mut schemas = HashMap::new();

    // Add {{name}} endpoints
    paths.insert(
        format!("{}/{{name}}s", state.config.base_path),
        OpenApiPath {
            get: Some(OpenApiOperation {
                summary: "Get {{name}}s".to_string(),
                description: "Retrieve a paginated list of {{name}}s".to_string(),
                parameters: Some(vec![
                    OpenApiParameter {
                        name: "page".to_string(),
                        r#in: "query".to_string(),
                        required: false,
                        schema: OpenApiSchema {
                            r#type: "integer".to_string(),
                            format: None,
                            example: Some(serde_json::Value::Number(1.into())),
                        },
                    },
                    OpenApiParameter {
                        name: "limit".to_string(),
                        r#in: "query".to_string(),
                        required: false,
                        schema: OpenApiSchema {
                            r#type: "integer".to_string(),
                            format: None,
                            example: Some(serde_json::Value::Number(20.into())),
                        },
                    },
                ]),
                request_body: None,
                responses: HashMap::from([
                    ("200".to_string(), OpenApiResponse {
                        description: "Success".to_string(),
                        content: Some(HashMap::from([
                            ("application/json".to_string(), OpenApiContent {
                                schema: OpenApiSchema {
                                    r#type: "object".to_string(),
                                    format: None,
                                    example: None,
                                },
                            }),
                        ])),
                    }),
                ]),
            }),
            post: Some(OpenApiOperation {
                summary: "Create {{name}}".to_string(),
                description: "Create a new {{name}}".to_string(),
                parameters: None,
                request_body: Some(OpenApiRequestBody {
                    required: true,
                    content: HashMap::from([
                        ("application/json".to_string(), OpenApiContent {
                            schema: OpenApiSchema {
                                r#type: "object".to_string(),
                                format: None,
                                example: None,
                            },
                        }),
                    ]),
                }),
                responses: HashMap::from([
                    ("201".to_string(), OpenApiResponse {
                        description: "Created".to_string(),
                        content: Some(HashMap::from([
                            ("application/json".to_string(), OpenApiContent {
                                schema: OpenApiSchema {
                                    r#type: "object".to_string(),
                                    format: None,
                                    example: None,
                                },
                            }),
                        ])),
                    }),
                ]),
            }),
            put: None,
            delete: None,
            patch: None,
        },
    );

    Ok(Json(OpenApiSpec {
        openapi: "3.0.0".to_string(),
        info: OpenApiInfo {
            title: "{{name | title}} API".to_string(),
            version: "{{version}}".to_string(),
            description: "{{description}}".to_string(),
        },
        paths,
        components: OpenApiComponents { schemas },
    }))
}

/// Validation functions
fn validate_create_request(request: &Create{{name | title}}Request) -> Result<()> {
    if request.name.trim().is_empty() {
        return Err(anyhow::anyhow!("Name cannot be empty"));
    }

    if request.name.len() > 100 {
        return Err(anyhow::anyhow!("Name too long"));
    }

    if !is_valid_email(&request.email) {
        return Err(anyhow::anyhow!("Invalid email format"));
    }

    Ok(())
}

fn validate_update_request(request: &Update{{name | title}}Request) -> Result<()> {
    if let Some(ref name) = request.name {
        if name.trim().is_empty() {
            return Err(anyhow::anyhow!("Name cannot be empty"));
        }

        if name.len() > 100 {
            return Err(anyhow::anyhow!("Name too long"));
        }
    }

    if let Some(ref email) = request.email {
        if !is_valid_email(email) {
            return Err(anyhow::anyhow!("Invalid email format"));
        }
    }

    Ok(())
}

fn is_valid_email(email: &str) -> bool {
    email.contains('@') && email.len() > 5 && email.len() < 255
}

/// Rate limiting
async fn check_rate_limit(
    state: &{{name | title}}ApiState,
    headers: &HeaderMap,
) -> Result<()> {
    // TODO: Implement rate limiting logic
    // For now, always allow
    Ok(())
}

/// Create the router
pub fn create_router(state: {{name | title}}ApiState) -> Router {
    let mut router = Router::new()
        .route("/{{name}}s", post(create_{{name}}_handler))
        .route("/{{name}}s", get(get_{{name}}s_handler))
        .route("/{{name}}s/:id", get(get_{{name}}_handler))
        .route("/{{name}}s/:id", put(update_{{name}}_handler))
        .route("/{{name}}s/:id", delete(delete_{{name}}_handler))
        .route("/health", get(health_check_handler))
        .route("/openapi.json", get(openapi_spec_handler))
        .with_state(state);

    // Add middleware
    if state.config.enable_cors {
        router = router.layer(CorsLayer::permissive());
    }

    if state.config.enable_compression {
        router = router.layer(CompressionLayer::new());
    }

    if state.config.enable_tracing {
        router = router.layer(TraceLayer::new_for_http());
    }

    router = router.layer(TimeoutLayer::new(Duration::from_secs(state.config.timeout_seconds)));

    router
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::{
        body::Body,
        http::{Request, StatusCode},
    };
    use tower::ServiceExt;

    #[tokio::test]
    async fn test_create_{{name}}_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_get_{{name}}_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_get_{{name}}s_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_update_{{name}}_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_delete_{{name}}_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_health_check_handler() {
        // TODO: Implement test
    }

    #[tokio::test]
    async fn test_openapi_spec_handler() {
        // TODO: Implement test
    }
}
