//! Fred-specific trait implementations for the shared types.
//!
//! These compile only when `redis-rs` is **not** active — i.e. when the
//! `fred-rs` feature is enabled instead.  This file is the "else" branch:
//! redis-rs is the primary backend; fred is the fallback.

use crate::types::{
    ConsumerGroupDestroyReply, DelReply, HsetnxReply, MsetnxReply, RedisEntryId, RedisScanType,
    RedisValue, SaddReply, SetnxReply, StreamCapKind, StreamCapTrim, StreamTrimConfig,
};

// ─── RedisValue impls ────────────────────────────────────────────────────────

impl RedisValue {
    pub fn new(value: fred::types::RedisValue) -> Self {
        Self { inner: value }
    }

    pub fn from_bytes(val: Vec<u8>) -> Self {
        Self {
            inner: fred::types::RedisValue::Bytes(val.into()),
        }
    }

    pub fn from_string(value: String) -> Self {
        Self {
            inner: fred::types::RedisValue::String(value.into()),
        }
    }

    pub fn as_bytes(&self) -> Option<&[u8]> {
        match &self.inner {
            fred::types::RedisValue::Bytes(b) => Some(b.as_ref()),
            fred::types::RedisValue::String(s) => Some(s.as_bytes()),
            _ => None,
        }
    }

    pub fn as_string(&self) -> Option<String> {
        match &self.inner {
            fred::types::RedisValue::String(s) => Some(s.to_string()),
            fred::types::RedisValue::Bytes(b) => String::from_utf8(b.to_vec()).ok(),
            _ => None,
        }
    }

    pub(crate) fn into_inner(self) -> fred::types::RedisValue {
        self.inner
    }
}

impl std::ops::Deref for RedisValue {
    type Target = fred::types::RedisValue;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl From<RedisValue> for fred::types::RedisValue {
    fn from(v: RedisValue) -> Self {
        v.inner
    }
}

// ─── RedisEntryId → XID ──────────────────────────────────────────────────────

impl From<RedisEntryId> for fred::types::XID {
    fn from(id: RedisEntryId) -> Self {
        match id {
            RedisEntryId::UserSpecifiedID {
                milliseconds,
                sequence_number,
            } => Self::Manual(fred::bytes_utils::format_bytes!(
                "{milliseconds}-{sequence_number}"
            )),
            RedisEntryId::AutoGeneratedID => Self::Auto,
            RedisEntryId::AfterLastID => Self::Max,
            RedisEntryId::UndeliveredEntryID => Self::NewInGroup,
        }
    }
}

impl From<&RedisEntryId> for fred::types::XID {
    fn from(id: &RedisEntryId) -> Self {
        match id {
            RedisEntryId::UserSpecifiedID {
                milliseconds,
                sequence_number,
            } => Self::Manual(fred::bytes_utils::format_bytes!(
                "{milliseconds}-{sequence_number}"
            )),
            RedisEntryId::AutoGeneratedID => Self::Auto,
            RedisEntryId::AfterLastID => Self::Max,
            RedisEntryId::UndeliveredEntryID => Self::NewInGroup,
        }
    }
}

// ─── Reply type trait impls ──────────────────────────────────────────────────

impl fred::types::FromRedis for SetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::String(_) => Ok(Self::KeySet),
            fred::types::RedisValue::Null => Ok(Self::KeyNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected SETNX reply",
            )),
        }
    }
}

impl fred::types::FromRedis for HsetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeySet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected HSETNX reply",
            )),
        }
    }
}

impl fred::types::FromRedis for MsetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeysSet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeysNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected MSETNX reply",
            )),
        }
    }
}

impl fred::types::FromRedis for DelReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeyDeleted),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotDeleted),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected DEL reply",
            )),
        }
    }
}

impl fred::types::FromRedis for SaddReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet),
            fred::types::RedisValue::Integer(n) => Ok(Self::KeySet(n)),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected SADD reply",
            )),
        }
    }
}

impl fred::types::FromRedis for ConsumerGroupDestroyReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            // XGROUP DESTROY returns 1 if the group was destroyed, 0 if it didn't exist
            fred::types::RedisValue::Integer(1) => Ok(Self::Destroyed),
            fred::types::RedisValue::Integer(0) => Ok(Self::NotFound),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected XGROUP DESTROY reply",
            )),
        }
    }
}

// ─── StreamCapKind / StreamCapTrim conversions ───────────────────────────────

impl From<StreamCapKind> for fred::types::XCapKind {
    fn from(item: StreamCapKind) -> Self {
        match item {
            StreamCapKind::MaxLen => Self::MaxLen,
            StreamCapKind::MinID => Self::MinID,
        }
    }
}

impl From<StreamCapTrim> for fred::types::XCapTrim {
    fn from(item: StreamCapTrim) -> Self {
        match item {
            StreamCapTrim::Exact => Self::Exact,
            StreamCapTrim::AlmostExact => Self::AlmostExact,
        }
    }
}

// ─── StreamTrimConfig → XCap ─────────────────────────────────────────────────

impl TryFrom<StreamTrimConfig> for fred::types::XCap {
    type Error = fred::error::RedisError;

    fn try_from(config: StreamTrimConfig) -> Result<Self, Self::Error> {
        (config.kind, config.trim, config.threshold).try_into()
    }
}

// ─── RedisScanType → fred ScanType ──────────────────────────────────────────

impl From<RedisScanType> for fred::types::ScanType {
    fn from(item: RedisScanType) -> Self {
        match item {
            RedisScanType::String => Self::String,
            RedisScanType::List => Self::List,
            RedisScanType::Set => Self::Set,
            RedisScanType::Zset => Self::ZSet,
            RedisScanType::Hash => Self::Hash,
            RedisScanType::Stream => Self::Stream,
        }
    }
}
