---
to: "generated/src/api/{{ name | snake }}.rs"
vars:
  name: "{{ name }}"
  method: "{{ method }}"
  path: "{{ path }}"
  description: "{{ description }}"
prefixes:
  ex: "http://example.org/advanced-rust-project/"
base: "http://example.org/advanced-rust-project/"
rdf:
  - "data/domain.ttl"
sparql:
  find_endpoints: "SELECT ?endpoint WHERE { ?endpoint a ex:Endpoint }"
  find_methods: "SELECT ?method WHERE { ?endpoint ex:method ?method }"
  find_paths: "SELECT ?path WHERE { ?endpoint ex:path ?path }"
---

//! {{ name | title }} API Endpoint
//! 
//! {{ description }}
//! 
//! Method: {{ method }}
//! Path: {{ path }}
//! 
//! Generated by ggen on {{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}

use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::Json,
    routing::{get, post, put, delete},
    Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use anyhow::Result;

/// {{ name | title }} Request/Response Types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ name | pascal }}Request {
    pub name: String,
    pub description: Option<String>,
    pub metadata: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ name | pascal }}Response {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub metadata: Option<HashMap<String, serde_json::Value>>,
    pub created_at: String,
    pub updated_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ name | pascal }}ListResponse {
    pub {{ name | snake }}: Vec<{{ name | pascal }}Response>,
    pub pagination: PaginationInfo,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginationInfo {
    pub limit: u32,
    pub offset: u32,
    pub total: u32,
    pub has_next: bool,
    pub has_prev: bool,
}

/// {{ name | title }} API Handler
pub struct {{ name | pascal }}Handler;

impl {{ name | pascal }}Handler {
    /// Create router for {{ name | title }} endpoints
    pub fn router() -> Router {
        Router::new()
            .route("{{ path }}", {{ method | lower }}::handler(Self::handle_{{ method | lower }}))
            .route("{{ path }}/:id", get(Self::get_{{ name | snake }}))
            .route("{{ path }}", post(Self::create_{{ name | snake }}))
            .route("{{ path }}/:id", put(Self::update_{{ name | snake }}))
            .route("{{ path }}/:id", delete(Self::delete_{{ name | snake }}))
    }

    /// Handle {{ method }} request for {{ name | title }}
    pub async fn handle_{{ method | lower }}(
        Query(params): Query<HashMap<String, String>>,
    ) -> Result<Json<{{ name | pascal }}ListResponse>, StatusCode> {
        let limit = params.get("limit").and_then(|s| s.parse().ok()).unwrap_or(10);
        let offset = params.get("offset").and_then(|s| s.parse().ok()).unwrap_or(0);
        
        // TODO: Implement actual business logic
        let {{ name | snake }}_list = {{ name | pascal }}ListResponse {
            {{ name | snake }}: vec![],
            pagination: PaginationInfo {
                limit,
                offset,
                total: 0,
                has_next: false,
                has_prev: false,
            },
        };
        
        Ok(Json({{ name | snake }}_list))
    }

    /// Get specific {{ name | snake }} by ID
    pub async fn get_{{ name | snake }}(
        Path(id): Path<String>,
    ) -> Result<Json<{{ name | pascal }}Response>, StatusCode> {
        // TODO: Implement actual database query
        let {{ name | snake }} = {{ name | pascal }}Response {
            id: id.clone(),
            name: "{{ name | title }}".to_string(),
            description: Some("{{ description }}".to_string()),
            metadata: None,
            created_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
            updated_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
        };
        
        Ok(Json({{ name | snake }}))
    }

    /// Create new {{ name | snake }}
    pub async fn create_{{ name | snake }}(
        Json(payload): Json<{{ name | pascal }}Request>,
    ) -> Result<Json<{{ name | pascal }}Response>, StatusCode> {
        // TODO: Implement actual database insert
        let {{ name | snake }} = {{ name | pascal }}Response {
            id: "generated-id".to_string(),
            name: payload.name,
            description: payload.description,
            metadata: payload.metadata,
            created_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
            updated_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
        };
        
        Ok(Json({{ name | snake }}))
    }

    /// Update existing {{ name | snake }}
    pub async fn update_{{ name | snake }}(
        Path(id): Path<String>,
        Json(payload): Json<{{ name | pascal }}Request>,
    ) -> Result<Json<{{ name | pascal }}Response>, StatusCode> {
        // TODO: Implement actual database update
        let {{ name | snake }} = {{ name | pascal }}Response {
            id: id.clone(),
            name: payload.name,
            description: payload.description,
            metadata: payload.metadata,
            created_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
            updated_at: "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}".to_string(),
        };
        
        Ok(Json({{ name | snake }}))
    }

    /// Delete {{ name | snake }}
    pub async fn delete_{{ name | snake }}(
        Path(id): Path<String>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        // TODO: Implement actual database delete
        let result = serde_json::json!({
            "id": id,
            "deleted": true,
            "timestamp": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}"
        });
        
        Ok(Json(result))
    }
}

/// {{ name | title }} API Error Types
#[derive(Debug, thiserror::Error)]
pub enum {{ name | pascal }}Error {
    #[error("Validation error: {0}")]
    Validation(String),
    
    #[error("Not found: {0}")]
    NotFound(String),
    
    #[error("Internal error: {0}")]
    Internal(String),
}

impl {{ name | pascal }}Error {
    pub fn status_code(&self) -> StatusCode {
        match self {
            {{ name | pascal }}Error::Validation(_) => StatusCode::BAD_REQUEST,
            {{ name | pascal }}Error::NotFound(_) => StatusCode::NOT_FOUND,
            {{ name | pascal }}Error::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }
}

/// {{ name | title }} API Validation
pub struct {{ name | pascal }}Validator;

impl {{ name | pascal }}Validator {
    pub fn validate_request(request: &{{ name | pascal }}Request) -> Result<(), {{ name | pascal }}Error> {
        if request.name.is_empty() {
            return Err({{ name | pascal }}Error::Validation("Name is required".to_string()));
        }
        
        if request.name.len() > 255 {
            return Err({{ name | pascal }}Error::Validation("Name too long".to_string()));
        }
        
        Ok(())
    }
}

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

    #[tokio::test]
    async fn test_get_{{ name | snake }}() {
        let app = {{ name | pascal }}Handler::router();

        let request = Request::builder()
            .uri("{{ path }}/test-id")
            .body(Body::empty())
            .unwrap();

        let response = app.oneshot(request).await.unwrap();
        assert_eq!(response.status(), StatusCode::OK);
    }

    #[tokio::test]
    async fn test_create_{{ name | snake }}() {
        let app = {{ name | pascal }}Handler::router();

        let request_body = serde_json::json!({
            "name": "Test {{ name | title }}",
            "description": "Test description"
        });

        let request = Request::builder()
            .method("POST")
            .uri("{{ path }}")
            .header("content-type", "application/json")
            .body(Body::from(request_body.to_string()))
            .unwrap();

        let response = app.oneshot(request).await.unwrap();
        assert_eq!(response.status(), StatusCode::OK);
    }

    #[tokio::test]
    async fn test_update_{{ name | snake }}() {
        let app = {{ name | pascal }}Handler::router();

        let request_body = serde_json::json!({
            "name": "Updated {{ name | title }}",
            "description": "Updated description"
        });

        let request = Request::builder()
            .method("PUT")
            .uri("{{ path }}/test-id")
            .header("content-type", "application/json")
            .body(Body::from(request_body.to_string()))
            .unwrap();

        let response = app.oneshot(request).await.unwrap();
        assert_eq!(response.status(), StatusCode::OK);
    }

    #[tokio::test]
    async fn test_delete_{{ name | snake }}() {
        let app = {{ name | pascal }}Handler::router();

        let request = Request::builder()
            .method("DELETE")
            .uri("{{ path }}/test-id")
            .body(Body::empty())
            .unwrap();

        let response = app.oneshot(request).await.unwrap();
        assert_eq!(response.status(), StatusCode::OK);
    }

    #[test]
    fn test_validation() {
        let valid_request = {{ name | pascal }}Request {
            name: "Valid Name".to_string(),
            description: Some("Valid description".to_string()),
            metadata: None,
        };

        assert!({{ name | pascal }}Validator::validate_request(&valid_request).is_ok());

        let invalid_request = {{ name | pascal }}Request {
            name: "".to_string(),
            description: None,
            metadata: None,
        };

        assert!({{ name | pascal }}Validator::validate_request(&invalid_request).is_err());
    }
}
