//! Standard API response types.

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[cfg(feature = "openapi")]
use utoipa::ToSchema;

/// Standard API response wrapper.
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponse<T> {
    /// Whether the request succeeded
    pub success: bool,

    /// Response data (present on success)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<T>,

    /// Response metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<ResponseMeta>,
}

/// Response metadata.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseMeta {
    /// Unique request ID for tracing
    pub request_id: String,

    /// Response timestamp
    pub timestamp: DateTime<Utc>,

    /// Request processing time in milliseconds
    #[serde(skip_serializing_if = "Option::is_none")]
    pub latency_ms: Option<u64>,

    /// Pagination info (if applicable)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pagination: Option<PaginationMeta>,
}

/// Pagination metadata.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct PaginationMeta {
    /// Total number of items
    pub total: u64,

    /// Current page (1-indexed)
    pub page: u32,

    /// Items per page
    pub per_page: u32,

    /// Total number of pages
    pub total_pages: u32,

    /// Whether there's a next page
    pub has_next: bool,

    /// Whether there's a previous page
    pub has_prev: bool,
}

impl<T: Serialize> ApiResponse<T> {
    /// Create a successful response with data.
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            meta: Some(ResponseMeta {
                request_id: uuid::Uuid::new_v4().to_string(),
                timestamp: Utc::now(),
                latency_ms: None,
                pagination: None,
            }),
        }
    }

    /// Create a successful response with data and pagination.
    pub fn success_paginated(data: T, pagination: PaginationMeta) -> Self {
        Self {
            success: true,
            data: Some(data),
            meta: Some(ResponseMeta {
                request_id: uuid::Uuid::new_v4().to_string(),
                timestamp: Utc::now(),
                latency_ms: None,
                pagination: Some(pagination),
            }),
        }
    }

    /// Set the request ID.
    pub fn with_request_id(mut self, request_id: String) -> Self {
        if let Some(ref mut meta) = self.meta {
            meta.request_id = request_id;
        }
        self
    }

    /// Set the latency.
    pub fn with_latency(mut self, latency_ms: u64) -> Self {
        if let Some(ref mut meta) = self.meta {
            meta.latency_ms = Some(latency_ms);
        }
        self
    }
}

impl<T: Serialize> IntoResponse for ApiResponse<T> {
    fn into_response(self) -> Response {
        (StatusCode::OK, Json(self)).into_response()
    }
}

/// Response for created resources (201).
pub struct Created<T>(pub ApiResponse<T>);

impl<T: Serialize> IntoResponse for Created<T> {
    fn into_response(self) -> Response {
        (StatusCode::CREATED, Json(self.0)).into_response()
    }
}

/// Response for accepted requests (202).
pub struct Accepted<T>(pub ApiResponse<T>);

impl<T: Serialize> IntoResponse for Accepted<T> {
    fn into_response(self) -> Response {
        (StatusCode::ACCEPTED, Json(self.0)).into_response()
    }
}

/// Response for no content (204).
pub struct NoContent;

impl IntoResponse for NoContent {
    fn into_response(self) -> Response {
        StatusCode::NO_CONTENT.into_response()
    }
}

/// Empty success response.
#[derive(Debug, Serialize, Deserialize)]
pub struct EmptyResponse;

impl ApiResponse<EmptyResponse> {
    /// Create an empty success response.
    pub fn empty() -> Self {
        Self {
            success: true,
            data: None,
            meta: Some(ResponseMeta {
                request_id: uuid::Uuid::new_v4().to_string(),
                timestamp: Utc::now(),
                latency_ms: None,
                pagination: None,
            }),
        }
    }
}

/// Pagination query parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginationParams {
    /// Page number (1-indexed, default: 1)
    #[serde(default = "default_page")]
    pub page: u32,

    /// Items per page (default: 20, max: 100)
    #[serde(default = "default_per_page")]
    pub per_page: u32,
}

fn default_page() -> u32 {
    1
}

fn default_per_page() -> u32 {
    20
}

impl Default for PaginationParams {
    fn default() -> Self {
        Self {
            page: 1,
            per_page: 20,
        }
    }
}

impl PaginationParams {
    /// Get the offset for database queries.
    pub fn offset(&self) -> u64 {
        ((self.page.saturating_sub(1)) * self.per_page) as u64
    }

    /// Get the limit for database queries.
    pub fn limit(&self) -> u32 {
        self.per_page.min(100)
    }

    /// Create pagination metadata from results.
    pub fn to_meta(&self, total: u64) -> PaginationMeta {
        let total_pages = ((total as f64) / (self.per_page as f64)).ceil() as u32;
        PaginationMeta {
            total,
            page: self.page,
            per_page: self.per_page,
            total_pages,
            has_next: self.page < total_pages,
            has_prev: self.page > 1,
        }
    }
}

/// Sorting parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortParams {
    /// Field to sort by
    pub sort_by: Option<String>,

    /// Sort direction (asc or desc)
    #[serde(default = "default_sort_order")]
    pub sort_order: SortOrder,
}

fn default_sort_order() -> SortOrder {
    SortOrder::Desc
}

/// Sort direction.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SortOrder {
    Asc,
    Desc,
}

impl Default for SortOrder {
    fn default() -> Self {
        Self::Desc
    }
}

/// Health check response.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct HealthResponse {
    /// Overall health status
    pub status: HealthStatus,

    /// Service version
    pub version: String,

    /// Uptime in seconds
    pub uptime_seconds: u64,

    /// Individual service health
    #[serde(skip_serializing_if = "Option::is_none")]
    pub services: Option<Vec<ServiceHealth>>,
}

/// Health status enum.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum HealthStatus {
    Healthy,
    Degraded,
    Unhealthy,
}

/// Individual service health.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ServiceHealth {
    /// Service name
    pub name: String,

    /// Health status
    pub status: HealthStatus,

    /// Optional message
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,

    /// Response time in milliseconds
    #[serde(skip_serializing_if = "Option::is_none")]
    pub latency_ms: Option<u64>,
}
