//! Data structures that wrap search responses from [`omry_search`] for compatibility with bincode.
use omry_search::{FieldName, FilterBy, SearchParameters};
use serde::{Deserialize, Serialize};

/// 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>,
}

#[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 anyhow::Context;
    use omry_archiving::bincode;

    #[test]
    fn can_serialize_deserialize_search_parameters_bincode() -> anyhow::Result<()> {
        let config = bincode::config::legacy().with_variable_int_encoding();

        let search_params: SearchParametersWrapper =
            SearchParameters::new_query("example".into(), vec!["text-body".into()]).into();
        let bytes = bincode::serde::encode_to_vec(search_params, config)?;
        let (deser, _bytes_read): (SearchParametersWrapper, usize) =
            bincode::serde::borrow_decode_from_slice(&bytes, config)
                .context("Failed to deserialize with bincode")?;

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