use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;
use serde_json::json;

/// Standardized success response
pub fn success<T: Serialize>(data: T, request_id: String) -> impl IntoResponse {
    let response = json!({
        "success": true,
        "data": data,
        "request_id": request_id,
        "timestamp": chrono::Utc::now().to_rfc3339()
    });

    (StatusCode::OK, Json(response)).into_response()
}

/// Standardized error response
pub fn error(
    status: StatusCode,
    message: String,
    error_code: String,
    request_id: String,
) -> impl IntoResponse {
    let response = json!({
        "success": false,
        "error": message,
        "error_code": error_code,
        "request_id": request_id,
        "timestamp": chrono::Utc::now().to_rfc3339()
    });

    (status, Json(response)).into_response()
}

/// Validation error response
pub fn validation_error(message: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::BAD_REQUEST,
        message,
        "VAL_001".to_string(),
        request_id,
    )
}

/// Not found error response
pub fn not_found(resource: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::NOT_FOUND,
        format!("{} not found", resource),
        "RES_001".to_string(),
        request_id,
    )
}

/// Unauthorized error response
pub fn unauthorized(message: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::UNAUTHORIZED,
        message,
        "AUTH_001".to_string(),
        request_id,
    )
}

/// Forbidden error response
pub fn forbidden(message: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::FORBIDDEN,
        message,
        "AUTH_002".to_string(),
        request_id,
    )
}

/// Conflict error response
pub fn conflict(message: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::CONFLICT,
        message,
        "CON_001".to_string(),
        request_id,
    )
}

/// Internal server error response
pub fn internal_error(message: String, request_id: String) -> impl IntoResponse {
    error(
        StatusCode::INTERNAL_SERVER_ERROR,
        message,
        "INT_001".to_string(),
        request_id,
    )
}

/// Created response
pub fn created<T: Serialize>(data: T, request_id: String) -> impl IntoResponse {
    let response = json!({
        "success": true,
        "data": data,
        "request_id": request_id,
        "timestamp": chrono::Utc::now().to_rfc3339()
    });

    (StatusCode::CREATED, Json(response)).into_response()
}

/// No content response
pub fn no_content(request_id: String) -> impl IntoResponse {
    (
        StatusCode::NO_CONTENT,
        Json(json!({
            "success": true,
            "request_id": request_id,
            "timestamp": chrono::Utc::now().to_rfc3339()
        })),
    )
        .into_response()
}

/// Paginated response
pub fn paginated<T: Serialize>(
    data: Vec<T>,
    page: u32,
    page_size: u32,
    total: u64,
    request_id: String,
) -> impl IntoResponse {
    let total_pages = total.div_ceil(page_size as u64);

    let response = json!({
        "success": true,
        "data": data,
        "pagination": {
            "page": page,
            "page_size": page_size,
            "total": total,
            "total_pages": total_pages
        },
        "request_id": request_id,
        "timestamp": chrono::Utc::now().to_rfc3339()
    });

    (StatusCode::OK, Json(response)).into_response()
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::response::Response;

    #[test]
    fn test_success_response() {
        let data = json!({"message": "test"});
        let request_id = "req-123".to_string();
        let response = success(data, request_id);
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::OK);
    }

    #[test]
    fn test_error_response() {
        let response = error(
            StatusCode::BAD_REQUEST,
            "Test error".to_string(),
            "TEST_001".to_string(),
            "req-123".to_string(),
        );
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::BAD_REQUEST);
    }

    #[test]
    fn test_validation_error() {
        let response = validation_error("Invalid input".to_string(), "req-123".to_string());
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::BAD_REQUEST);
    }

    #[test]
    fn test_not_found() {
        let response = not_found("User".to_string(), "req-123".to_string());
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_created_response() {
        let data = json!({"id": 1});
        let response = created(data, "req-123".to_string());
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::CREATED);
    }

    #[test]
    fn test_paginated_response() {
        let data = vec![json!({"id": 1}), json!({"id": 2})];
        let response = paginated(data, 1, 10, 2, "req-123".to_string());
        let into_response: Response = response.into_response();
        assert_eq!(into_response.status(), StatusCode::OK);
    }
}
