---
to: "generated/src/services/{{ name | snake }}.rs"
vars:
  name: "{{ name }}"
  description: "{{ description }}"
  version: "{{ version }}"
  author: "{{ author }}"
prefixes:
  ex: "http://example.org/advanced-rust-project/"
base: "http://example.org/advanced-rust-project/"
rdf:
  - "data/domain.ttl"
sparql:
  find_entities: "SELECT ?entity WHERE { ?entity a ex:Entity }"
  find_properties: "SELECT ?property WHERE { ?property a ex:Property }"
  find_relationships: "SELECT ?rel WHERE { ?rel a ex:Relationship }"
  find_endpoints: "SELECT ?endpoint WHERE { ?endpoint a ex:APIEndpoint }"
---

//! {{ name | title }} Service
//! 
//! {{ description }}
//! 
//! Generated by ggen on {{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}
//! Version: {{ version }}
//! Author: {{ author }}

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

/// {{ name | title }} Service Configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ name | pascal }}Config {
    pub host: String,
    pub port: u16,
    pub database_url: String,
    pub redis_url: Option<String>,
    pub log_level: String,
}

impl Default for {{ name | pascal }}Config {
    fn default() -> Self {
        Self {
            host: "0.0.0.0".to_string(),
            port: 8080,
            database_url: "postgresql://localhost/{{ name | snake }}".to_string(),
            redis_url: None,
            log_level: "info".to_string(),
        }
    }
}

/// {{ name | title }} Service State
#[derive(Debug, Clone)]
pub struct {{ name | pascal }}State {
    pub config: {{ name | pascal }}Config,
    pub cache: RwLock<HashMap<String, String>>,
}

impl {{ name | pascal }}State {
    pub fn new(config: {{ name | pascal }}Config) -> Self {
        Self {
            config,
            cache: RwLock::new(HashMap::new()),
        }
    }
}

/// {{ name | title }} Service Implementation
pub struct {{ name | pascal }}Service {
    state: {{ name | pascal }}State,
}

impl {{ name | pascal }}Service {
    pub fn new(config: {{ name | pascal }}Config) -> Self {
        Self {
            state: {{ name | pascal }}State::new(config),
        }
    }

    pub fn router(&self) -> Router {
        Router::new()
            .route("/health", get(Self::health_check))
            .route("/metrics", get(Self::metrics))
            .route("/{{ name | kebab }}", get(Self::list_{{ name | snake }}))
            .route("/{{ name | kebab }}/:id", get(Self::get_{{ name | snake }}))
            .route("/{{ name | kebab }}", post(Self::create_{{ name | snake }}))
            .route("/{{ name | kebab }}/:id", put(Self::update_{{ name | snake }}))
            .route("/{{ name | kebab }}/:id", delete(Self::delete_{{ name | snake }}))
            .with_state(self.state.clone())
    }

    /// Health check endpoint
    pub async fn health_check(
        State(state): State<{{ name | pascal }}State>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        let health = serde_json::json!({
            "status": "healthy",
            "service": "{{ name | snake }}",
            "version": "{{ version }}",
            "timestamp": chrono::Utc::now().to_rfc3339(),
            "config": {
                "host": state.config.host,
                "port": state.config.port,
                "log_level": state.config.log_level,
            }
        });
        Ok(Json(health))
    }

    /// Metrics endpoint
    pub async fn metrics(
        State(state): State<{{ name | pascal }}State>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        let cache_size = state.cache.read().await.len();
        let metrics = serde_json::json!({
            "service": "{{ name | snake }}",
            "cache_size": cache_size,
            "uptime": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}",
            "memory_usage": "N/A",
            "cpu_usage": "N/A"
        });
        Ok(Json(metrics))
    }

    /// List all {{ name | snake }}
    pub async fn list_{{ name | snake }}(
        State(state): State<{{ name | pascal }}State>,
        Query(params): Query<HashMap<String, String>>,
    ) -> Result<Json<serde_json::Value>, 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 database query
        let {{ name | snake }}_list = serde_json::json!({
            "{{ name | snake }}": [],
            "pagination": {
                "limit": limit,
                "offset": offset,
                "total": 0
            }
        });
        
        Ok(Json({{ name | snake }}_list))
    }

    /// Get specific {{ name | snake }}
    pub async fn get_{{ name | snake }}(
        State(state): State<{{ name | pascal }}State>,
        Path(id): Path<String>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        // TODO: Implement actual database query
        let {{ name | snake }} = serde_json::json!({
            "id": id,
            "name": "{{ name | title }}",
            "description": "{{ description }}",
            "created_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}",
            "updated_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}"
        });
        
        Ok(Json({{ name | snake }}))
    }

    /// Create new {{ name | snake }}
    pub async fn create_{{ name | snake }}(
        State(state): State<{{ name | pascal }}State>,
        Json(payload): Json<serde_json::Value>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        // TODO: Implement actual database insert
        let {{ name | snake }} = serde_json::json!({
            "id": "generated-id",
            "name": payload.get("name").unwrap_or(&serde_json::Value::String("{{ name | title }}".to_string())),
            "description": payload.get("description").unwrap_or(&serde_json::Value::String("{{ description }}".to_string())),
            "created_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}",
            "updated_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}"
        });
        
        Ok(Json({{ name | snake }}))
    }

    /// Update existing {{ name | snake }}
    pub async fn update_{{ name | snake }}(
        State(state): State<{{ name | pascal }}State>,
        Path(id): Path<String>,
        Json(payload): Json<serde_json::Value>,
    ) -> Result<Json<serde_json::Value>, StatusCode> {
        // TODO: Implement actual database update
        let {{ name | snake }} = serde_json::json!({
            "id": id,
            "name": payload.get("name").unwrap_or(&serde_json::Value::String("{{ name | title }}".to_string())),
            "description": payload.get("description").unwrap_or(&serde_json::Value::String("{{ description }}".to_string())),
            "created_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}",
            "updated_at": "{{ "now" | date(format="%Y-%m-%d %H:%M:%S") }}"
        });
        
        Ok(Json({{ name | snake }}))
    }

    /// Delete {{ name | snake }}
    pub async fn delete_{{ name | snake }}(
        State(state): State<{{ name | pascal }}State>,
        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 }} Service Error Types
#[derive(Debug, thiserror::Error)]
pub enum {{ name | pascal }}Error {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),
    
    #[error("Validation error: {0}")]
    Validation(String),
    
    #[error("Not found: {0}")]
    NotFound(String),
    
    #[error("Internal error: {0}")]
    Internal(#[from] anyhow::Error),
}

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

/// {{ name | title }} Service Main Function
#[tokio::main]
async fn main() -> Result<()> {
    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter("{{ name | snake }}=debug,tower_http=debug")
        .init();

    // Load configuration
    let config = {{ name | pascal }}Config::default();
    
    // Create service
    let service = {{ name | pascal }}Service::new(config.clone());
    
    // Create router
    let app = service.router();
    
    // Start server
    let listener = tokio::net::TcpListener::bind(format!("{}:{}", config.host, config.port)).await?;
    tracing::info!("{{ name | title }} service listening on {}:{}", config.host, config.port);
    
    axum::serve(listener, app).await?;
    
    Ok(())
}

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

    #[tokio::test]
    async fn test_health_check() {
        let config = {{ name | pascal }}Config::default();
        let service = {{ name | pascal }}Service::new(config);
        let app = service.router();

        let request = Request::builder()
            .uri("/health")
            .body(Body::empty())
            .unwrap();

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

    #[tokio::test]
    async fn test_metrics() {
        let config = {{ name | pascal }}Config::default();
        let service = {{ name | pascal }}Service::new(config);
        let app = service.router();

        let request = Request::builder()
            .uri("/metrics")
            .body(Body::empty())
            .unwrap();

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

    #[tokio::test]
    async fn test_list_{{ name | snake }}() {
        let config = {{ name | pascal }}Config::default();
        let service = {{ name | pascal }}Service::new(config);
        let app = service.router();

        let request = Request::builder()
            .uri("/{{ name | kebab }}")
            .body(Body::empty())
            .unwrap();

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