//! Query metadata for request context.

use serde::{Deserialize, Serialize};

use crate::filter::Filter;
use crate::pagination::{Fields, Page, Sort};

/// Complete query metadata extracted from a request.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct QueryMeta {
    /// Filter conditions.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub filters: Vec<Filter>,
    /// Pagination information.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub page: Option<Page>,
    /// Sort specification.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sort: Option<Sort>,
    /// Field selection.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fields: Option<Fields>,
}

impl QueryMeta {
    /// Creates empty query metadata.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the filters.
    #[must_use]
    pub fn with_filters(mut self, filters: Vec<Filter>) -> Self {
        self.filters = filters;
        self
    }

    /// Adds a single filter.
    #[must_use]
    pub fn with_filter(mut self, filter: Filter) -> Self {
        self.filters.push(filter);
        self
    }

    /// Sets the pagination.
    #[must_use]
    pub fn with_page(mut self, page: Page) -> Self {
        self.page = Some(page);
        self
    }

    /// Sets the sort specification.
    #[must_use]
    pub fn with_sort(mut self, sort: Sort) -> Self {
        self.sort = Some(sort);
        self
    }

    /// Sets the field selection.
    #[must_use]
    pub fn with_fields(mut self, fields: Fields) -> Self {
        self.fields = Some(fields);
        self
    }

    /// Returns true if pagination is specified.
    #[must_use]
    pub fn has_pagination(&self) -> bool {
        self.page.is_some()
    }

    /// Returns true if sorting is specified.
    #[must_use]
    pub fn has_sort(&self) -> bool {
        self.sort.is_some()
    }

    /// Returns true if field selection is specified.
    #[must_use]
    pub fn has_fields(&self) -> bool {
        self.fields.is_some()
    }

    /// Returns true if any filters are specified.
    #[must_use]
    pub fn has_filters(&self) -> bool {
        !self.filters.is_empty()
    }

    /// Returns the number of filters.
    #[must_use]
    pub fn filter_count(&self) -> usize {
        self.filters.len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[allow(unused_imports)]
    use crate::filter::{FilterOp, FilterValue};
    use crate::pagination::SortDirection;

    #[test]
    fn test_query_meta_new() {
        let meta = QueryMeta::new();
        assert!(meta.filters.is_empty());
        assert!(meta.page.is_none());
        assert!(meta.sort.is_none());
        assert!(meta.fields.is_none());
    }

    #[test]
    fn test_query_meta_with_filters() {
        let filter = Filter::eq("name", FilterValue::String("test".to_string()));
        let meta = QueryMeta::new().with_filter(filter.clone());

        assert_eq!(meta.filters.len(), 1);
        assert_eq!(meta.filters[0], filter);
        assert!(meta.has_filters());
    }

    #[test]
    fn test_query_meta_with_pagination() {
        let page = Page::new(2, 25);
        let meta = QueryMeta::new().with_page(page);

        assert!(meta.has_pagination());
        let p = meta.page.expect("should have page");
        assert_eq!(p.number, 2);
        assert_eq!(p.size, 25);
    }

    #[test]
    fn test_query_meta_with_sort() {
        let sort = Sort::desc("created_at");
        let meta = QueryMeta::new().with_sort(sort);

        assert!(meta.has_sort());
        let s = meta.sort.expect("should have sort");
        assert_eq!(s.field, "created_at");
        assert_eq!(s.direction, SortDirection::Desc);
    }

    #[test]
    fn test_query_meta_with_fields() {
        let fields = Fields::include(vec!["id".to_string(), "name".to_string()]);
        let meta = QueryMeta::new().with_fields(fields);

        assert!(meta.has_fields());
    }

    #[test]
    fn test_query_meta_builder_chain() {
        let meta = QueryMeta::new()
            .with_filter(Filter::eq(
                "status",
                FilterValue::String("active".to_string()),
            ))
            .with_page(Page::new(1, 10))
            .with_sort(Sort::asc("name"))
            .with_fields(Fields::exclude(vec!["password".to_string()]));

        assert!(meta.has_filters());
        assert!(meta.has_pagination());
        assert!(meta.has_sort());
        assert!(meta.has_fields());
        assert_eq!(meta.filter_count(), 1);
    }

    #[test]
    fn test_query_meta_multiple_filters() {
        let meta = QueryMeta::new()
            .with_filter(Filter::eq("name", FilterValue::String("test".to_string())))
            .with_filter(Filter::gt("age", FilterValue::Integer(18)));

        assert_eq!(meta.filter_count(), 2);
    }

    #[test]
    fn test_query_meta_serialization() {
        let meta = QueryMeta::new()
            .with_page(Page::new(1, 10))
            .with_sort(Sort::desc("id"));

        let json = serde_json::to_string(&meta).expect("should serialize");
        assert!(json.contains("\"number\":1"));
        assert!(json.contains("\"direction\":\"desc\""));

        let deserialized: QueryMeta = serde_json::from_str(&json).expect("should deserialize");
        assert_eq!(deserialized.page, meta.page);
        assert_eq!(deserialized.sort, meta.sort);
    }

    #[test]
    fn test_query_meta_default() {
        let meta = QueryMeta::default();
        assert!(!meta.has_filters());
        assert!(!meta.has_pagination());
        assert!(!meta.has_sort());
        assert!(!meta.has_fields());
    }
}
