//! Data structures that wrap search responses from Typesense for compatibility [`serde`].
use serde::{Deserialize, Serialize};
use std::num::NonZero;

use crate::{FieldName, FilterBy, SearchParameters, SortBy};

/// Redefinition of [`SearchParameters`] from [`omry-search`]
/// to work around bincode <-> serde compat issues.
///
/// Specifically, bincode doesn't support conditionally skipping fields.
/// See <https://docs.rs/bincode/2.0.1/bincode/serde/index.html#known-issues>.
#[allow(
    missing_docs,
    clippy::missing_docs_in_private_items,
    reason = "Redefinition of already documented type."
)]
#[derive(Serialize, Deserialize)]
#[serde(remote = "SearchParameters")]
struct SearchParametersDef {
    query: String,
    query_by: Vec<FieldName>,
    num_typos: Option<u8>,
    filter_by: Option<FilterBy>,
    sort_by: Option<SortBy>,
    page: Option<NonZero<usize>>,
    per_page: Option<NonZero<u8>>,
}

#[allow(
    missing_docs,
    reason = "Redefinition of type from omry-search to work around the orphan rule"
)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SearchParametersWrapper(#[serde(with = "SearchParametersDef")] SearchParameters);

impl From<SearchParameters> for SearchParametersWrapper {
    fn from(value: SearchParameters) -> Self {
        Self(value)
    }
}

impl From<SearchParametersWrapper> for SearchParameters {
    fn from(SearchParametersWrapper(value): SearchParametersWrapper) -> Self {
        value
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{SortByField, SortOrder};
    use anyhow::Context;
    use omry_archiving::postcard;

    #[test]
    fn can_serialize_deserialize_search_parameters() -> anyhow::Result<()> {
        let sort_by = SortBy::new()
            .add_field(SortByField::text_match(Some(3), SortOrder::Desc))?
            .add_field(SortByField::regular(
                "text_body".to_string(),
                SortOrder::Desc,
            ))?;

        let search_params: SearchParametersWrapper =
            SearchParameters::new_query("example".into(), vec!["text-body".into()])
                .sort_by(sort_by)
                .into();

        let bytes = postcard::to_allocvec(&search_params)?;
        let deser: SearchParametersWrapper =
            postcard::from_bytes(&bytes).context("Failed to deserialize with postcard")?;

        let deser_search_params: SearchParameters = deser.into();
        insta::assert_debug_snapshot!(deser_search_params);
        Ok(())
    }
}
