use crate::persist::PersistenceError;
use std::fmt::Display;
use std::marker::PhantomData;
use std::str::FromStr;

pub trait PersistCodec<T>: Clone + 'static {
    fn encode(&self, value: &T) -> Result<String, String>;
    fn decode(&self, raw: &str) -> Result<T, String>;

    fn should_remove(&self, _value: &T) -> bool {
        false
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct StringCodec;

impl PersistCodec<String> for StringCodec {
    fn encode(&self, value: &String) -> Result<String, String> {
        Ok(value.clone())
    }

    fn decode(&self, raw: &str) -> Result<String, String> {
        Ok(raw.to_string())
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ParseCodec<T>(pub PhantomData<T>);

impl<T> ParseCodec<T> {
    pub fn new() -> Self {
        Self(PhantomData)
    }
}

impl<T> PersistCodec<T> for ParseCodec<T>
where
    T: Display + FromStr + Clone + 'static,
    <T as FromStr>::Err: std::fmt::Display,
{
    fn encode(&self, value: &T) -> Result<String, String> {
        Ok(value.to_string())
    }

    fn decode(&self, raw: &str) -> Result<T, String> {
        raw.parse::<T>().map_err(|err| err.to_string())
    }
}

#[cfg(feature = "json")]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PersistJsonCodec<T>(pub PhantomData<T>);

#[cfg(feature = "json")]
impl<T> PersistJsonCodec<T> {
    pub fn new() -> Self {
        Self(PhantomData)
    }
}

#[cfg(feature = "json")]
impl<T> PersistCodec<T> for PersistJsonCodec<T>
where
    T: serde::Serialize + serde::de::DeserializeOwned + Clone + 'static,
{
    fn encode(&self, value: &T) -> Result<String, String> {
        serde_json::to_string(value).map_err(|err| err.to_string())
    }

    fn decode(&self, raw: &str) -> Result<T, String> {
        serde_json::from_str(raw).map_err(|err| err.to_string())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OptionCodec<C, T> {
    inner: C,
    marker: PhantomData<T>,
}

impl<C, T> OptionCodec<C, T> {
    pub fn new(inner: C) -> Self {
        Self {
            inner,
            marker: PhantomData,
        }
    }
}

impl<C, T> PersistCodec<Option<T>> for OptionCodec<C, T>
where
    C: PersistCodec<T>,
    T: Clone + 'static,
{
    fn encode(&self, value: &Option<T>) -> Result<String, String> {
        match value {
            Some(value) => self.inner.encode(value),
            None => Err("OptionCodec cannot encode None directly".to_string()),
        }
    }

    fn decode(&self, raw: &str) -> Result<Option<T>, String> {
        self.inner.decode(raw).map(Some)
    }

    fn should_remove(&self, value: &Option<T>) -> bool {
        value.is_none()
    }
}

pub(crate) fn map_encode_error(message: String) -> PersistenceError {
    PersistenceError::EncodeFailed(message)
}

pub(crate) fn map_decode_error(raw: &str, message: String) -> PersistenceError {
    PersistenceError::DecodeFailed {
        raw: raw.to_string(),
        message,
    }
}
