//! [`SortParam`] — type-safe ORDER BY param parsed from query strings.

use serde::{Deserialize, Deserializer};

/// A parsed sort specification from a query string like `"-created_at,email"`.
///
/// Each element is `(column_name, ascending)`.
/// Leading `-` means descending; no prefix means ascending.
///
/// # Example
///
/// ```
/// use rok_validate::SortParam;
///
/// let sp = SortParam::parse("-created_at,email", &["created_at", "email", "name"]);
/// assert_eq!(sp.0, vec![
///     ("created_at".to_string(), false),
///     ("email".to_string(), true),
/// ]);
/// ```
#[derive(Debug, Clone, Default)]
pub struct SortParam(pub Vec<(String, bool)>);

impl SortParam {
    /// Parse a comma-separated sort string.
    ///
    /// Columns prefixed with `-` are descending; all others are ascending.
    /// Columns not in `allowlist` are silently dropped.
    /// If `allowlist` is empty all columns pass through.
    pub fn parse(s: &str, allowlist: &[&str]) -> Self {
        let columns = s
            .split(',')
            .filter_map(|part| {
                let part = part.trim();
                if part.is_empty() {
                    return None;
                }
                let (col, asc) = if let Some(rest) = part.strip_prefix('-') {
                    (rest, false)
                } else {
                    (part, true)
                };
                if allowlist.is_empty() || allowlist.contains(&col) {
                    Some((col.to_string(), asc))
                } else {
                    None
                }
            })
            .collect();
        SortParam(columns)
    }
}

impl<'de> Deserialize<'de> for SortParam {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let s = String::deserialize(d)?;
        Ok(SortParam::parse(&s, &[]))
    }
}
