// Axum middleware demo example
// Run with: cargo run --example middleware_axum_demo --features axum
//
// This example demonstrates the api_result_middleware that automatically
// injects traceId into ApiResult JSON responses.

#![cfg(feature = "axum")]

use anycms_core::axum::ApiResultMiddlewareConfig;
use anycms_core::{ApiResult, FieldError, ResultPagination};
use axum::{
    Router,
    extract::{Json, Path},
    middleware,
    routing::get,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct CreateUserRequest {
    name: String,
    email: String,
}

// Mock user data
async fn get_users() -> Vec<User> {
    vec![
        User {
            id: 1,
            name: "Alice".to_string(),
            email: "alice@example.com".to_string(),
        },
        User {
            id: 2,
            name: "Bob".to_string(),
            email: "bob@example.com".to_string(),
        },
        User {
            id: 3,
            name: "Charlie".to_string(),
            email: "charlie@example.com".to_string(),
        },
    ]
}

async fn get_user_by_id(id: u32) -> Option<User> {
    let users = get_users().await;
    users.into_iter().find(|u| u.id == id)
}

async fn index() -> ApiResult<()> {
    ApiResult::ok().with_message("Service is running")
}

async fn list_users() -> ApiResult<User> {
    let users = get_users().await;
    let pagination = ResultPagination::new(3, 1, 10);

    ApiResult::list(users)
        .with_pagination(pagination)
        .with_extra("total_count", serde_json::json!(3))
}

async fn get_user(Path(id): Path<u32>) -> ApiResult<User> {
    match get_user_by_id(id).await {
        Some(user) => ApiResult::value(user),
        None => ApiResult::fail("User not found").with_code(404),
    }
}

async fn create_user(Json(user_data): Json<CreateUserRequest>) -> ApiResult<User> {
    let new_user = User {
        id: 999,
        name: user_data.name.clone(),
        email: user_data.email.clone(),
    };

    ApiResult::value(new_user).with_extra("created_at", serde_json::json!("2024-01-01T00:00:00Z"))
}

async fn validate_demo() -> ApiResult<()> {
    ApiResult::validation_errors(vec![
        FieldError::new("email", "Invalid email format"),
        FieldError::new("name", "Name cannot be empty"),
    ])
}

async fn error_demo() -> ApiResult<()> {
    ApiResult::fail("Database connection timeout").with_code(500)
}

async fn custom_trace_demo() -> ApiResult<()> {
    // Handler-set trace_id is preserved; middleware won't overwrite it
    ApiResult::ok()
        .with_message("This response has a manually set trace_id")
        .with_trace_id("manual-trace-abc123")
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    println!("Starting Axum middleware demo server...");
    println!();
    println!("api_result_middleware automatically injects traceId into responses.");
    println!();
    println!("Endpoints:");
    println!("  GET  /              - Basic success response");
    println!("  GET  /users         - User list with pagination");
    println!("  GET  /users/1       - Get single user (found)");
    println!("  GET  /users/999     - Get single user (not found)");
    println!("  POST /users         - Create user");
    println!("  GET  /validate      - Validation errors");
    println!("  GET  /error         - Internal server error");
    println!("  GET  /custom-trace  - Manually set trace_id (not overwritten)");
    println!();
    println!("Try:");
    println!("  curl http://localhost:8081/");
    println!("  curl http://localhost:8081/users");
    println!("  curl http://localhost:8081/users/1");
    println!("  curl http://localhost:8081/users/999");
    println!("  curl http://localhost:8081/validate");
    println!("  curl http://localhost:8081/error");
    println!("  curl http://localhost:8081/custom-trace");
    println!();
    println!("Pass a custom trace ID via header:");
    println!("  curl -H 'x-request-id: my-custom-id' http://localhost:8081/");
    println!("  curl -H 'x-trace-id: another-id' http://localhost:8081/users");

    let config = ApiResultMiddlewareConfig {
        trace_id_headers: vec!["x-request-id".to_string(), "x-trace-id".to_string()],
        inject_timestamp: false,
    };

    let app = Router::new()
        .route("/", get(index))
        .route("/users", get(list_users).post(create_user))
        .route("/users/{id}", get(get_user))
        .route("/validate", get(validate_demo))
        .route("/error", get(error_demo))
        .route("/custom-trace", get(custom_trace_demo))
        .layer(middleware::from_fn_with_state(
            config,
            anycms_core::axum::api_result_middleware_with_config,
        ));

    let addr = SocketAddr::from(([127, 0, 0, 1], 8081));
    let listener = tokio::net::TcpListener::bind(addr).await?;

    println!();
    println!("Server listening on http://127.0.0.1:8081");
    axum::serve(listener, app).await?;

    Ok(())
}
