//! Direct wire encoding/decoding traits that bypass serde.
//!
//! These traits are the canonical way to encode/decode Cord values.
//! The serde Serialize/Deserialize impls generated by `#[derive(Cord)]`
//! delegate to these traits when used with the Cord serializer.

use crate::result::{CordError, CordResult};
use crate::Width;

/// Direct wire encoding for Cord types.
pub trait CordEncode {
    /// Encode this value, appending canonical Cord bytes.
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()>;

    /// Encode this value with a specific width for length prefixes.
    ///
    /// The default implementation ignores the width (correct for fixed-size types).
    /// Length-prefixed types (String, Vec, Bytes, etc.) override this to use the
    /// specified width instead of the default 4-byte prefix.
    fn encode_cord_with_width(&self, buf: &mut Vec<u8>, _width: Width) -> CordResult<()> {
        self.encode_cord(buf)
    }
}

/// Direct wire decoding for Cord types.
pub trait CordDecode: Sized {
    /// Decode a value from the input bytes.
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self>;

    /// Decode a value with a specific width for length prefixes.
    ///
    /// The default implementation ignores the width (correct for fixed-size types).
    /// Length-prefixed types (String, Vec, Bytes, etc.) override this to use the
    /// specified width instead of the default 4-byte prefix.
    fn decode_cord_with_width(input: &mut &[u8], _width: Width) -> CordResult<Self> {
        Self::decode_cord(input)
    }
}

// ---------------------------------------------------------------------------
// Primitive CordEncode impls
// ---------------------------------------------------------------------------

impl CordEncode for () {
    fn encode_cord(&self, _buf: &mut Vec<u8>) -> CordResult<()> {
        Ok(())
    }
}

impl CordEncode for bool {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        crate::wire::write_bool(buf, *self);
        Ok(())
    }
}

macro_rules! impl_encode_fixed {
    ($($ty:ty),*) => {
        $(
            impl CordEncode for $ty {
                fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
                    buf.extend_from_slice(&self.to_be_bytes());
                    Ok(())
                }
            }
        )*
    };
}

impl_encode_fixed!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);

impl CordEncode for f32 {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        if self.is_nan() {
            return Err(CordError::NanNotAllowed);
        }
        let v = if *self == 0.0 { 0.0_f32 } else { *self };
        buf.extend_from_slice(&v.to_be_bytes());
        Ok(())
    }
}

impl CordEncode for f64 {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        if self.is_nan() {
            return Err(CordError::NanNotAllowed);
        }
        let v = if *self == 0.0 { 0.0_f64 } else { *self };
        buf.extend_from_slice(&v.to_be_bytes());
        Ok(())
    }
}

impl CordEncode for str {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        crate::wire::write_str(buf, self, Width::W32)
    }

    fn encode_cord_with_width(&self, buf: &mut Vec<u8>, width: Width) -> CordResult<()> {
        crate::wire::write_str(buf, self, width)
    }
}

impl CordEncode for String {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        self.as_str().encode_cord(buf)
    }

    fn encode_cord_with_width(&self, buf: &mut Vec<u8>, width: Width) -> CordResult<()> {
        self.as_str().encode_cord_with_width(buf, width)
    }
}

impl<T: CordEncode> CordEncode for Option<T> {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        match self {
            None => {
                buf.push(0);
                Ok(())
            }
            Some(v) => {
                buf.push(1);
                v.encode_cord(buf)
            }
        }
    }
}

impl<T: CordEncode> CordEncode for Vec<T> {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        self.encode_cord_with_width(buf, Width::W32)
    }

    fn encode_cord_with_width(&self, buf: &mut Vec<u8>, width: Width) -> CordResult<()> {
        crate::wire::write_length(buf, self.len(), width)?;
        for item in self {
            item.encode_cord(buf)?;
        }
        Ok(())
    }
}

impl CordEncode for crate::Bytes {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        crate::wire::write_bytes(buf, &self.0, Width::W32)
    }

    fn encode_cord_with_width(&self, buf: &mut Vec<u8>, width: Width) -> CordResult<()> {
        crate::wire::write_bytes(buf, &self.0, width)
    }
}

// ---------------------------------------------------------------------------
// Primitive CordDecode impls
// ---------------------------------------------------------------------------

impl CordDecode for () {
    fn decode_cord(_input: &mut &[u8]) -> CordResult<Self> {
        Ok(())
    }
}

impl CordDecode for bool {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        crate::wire::read_bool(input)
    }
}

macro_rules! impl_decode_fixed {
    ($(($ty:ty, $n:expr)),*) => {
        $(
            impl CordDecode for $ty {
                fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
                    let bytes = crate::wire::read_bytes(input, $n)?;
                    Ok(<$ty>::from_be_bytes(bytes.try_into().unwrap()))
                }
            }
        )*
    };
}

impl_decode_fixed!(
    (u8, 1),
    (u16, 2),
    (u32, 4),
    (u64, 8),
    (u128, 16),
    (i8, 1),
    (i16, 2),
    (i32, 4),
    (i64, 8),
    (i128, 16)
);

impl CordDecode for f32 {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        let bytes = crate::wire::read_bytes(input, 4)?;
        let v = f32::from_be_bytes(bytes.try_into().unwrap());
        if v.is_nan() {
            return Err(CordError::NanNotAllowed);
        }
        if v.to_bits() == (-0.0_f32).to_bits() {
            return Err(CordError::NegativeZeroNotAllowed);
        }
        Ok(v)
    }
}

impl CordDecode for f64 {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        let bytes = crate::wire::read_bytes(input, 8)?;
        let v = f64::from_be_bytes(bytes.try_into().unwrap());
        if v.is_nan() {
            return Err(CordError::NanNotAllowed);
        }
        if v.to_bits() == (-0.0_f64).to_bits() {
            return Err(CordError::NegativeZeroNotAllowed);
        }
        Ok(v)
    }
}

impl CordDecode for String {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        Self::decode_cord_with_width(input, Width::W32)
    }

    fn decode_cord_with_width(input: &mut &[u8], width: Width) -> CordResult<Self> {
        let s = crate::wire::read_str(input, width, crate::de::DEFAULT_MAX_LENGTH)?;
        Ok(s.to_string())
    }
}

impl<T: CordDecode> CordDecode for Option<T> {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        let discriminant = crate::wire::read_bytes(input, 1)?[0];
        match discriminant {
            0 => Ok(None),
            1 => Ok(Some(T::decode_cord(input)?)),
            _ => Err(CordError::ValidationError("Invalid option discriminant")),
        }
    }
}

impl<T: CordDecode> CordDecode for Vec<T> {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        Self::decode_cord_with_width(input, Width::W32)
    }

    fn decode_cord_with_width(input: &mut &[u8], width: Width) -> CordResult<Self> {
        let len = crate::wire::read_length(input, width, crate::de::DEFAULT_MAX_LENGTH)?;
        let mut result = Vec::with_capacity(len);
        for _ in 0..len {
            result.push(T::decode_cord(input)?);
        }
        Ok(result)
    }
}

impl CordDecode for crate::Bytes {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        Self::decode_cord_with_width(input, Width::W32)
    }

    fn decode_cord_with_width(input: &mut &[u8], width: Width) -> CordResult<Self> {
        let data = crate::wire::read_bytes_prefixed(input, width, crate::de::DEFAULT_MAX_LENGTH)?;
        Ok(crate::Bytes(data.to_vec()))
    }
}

// ---------------------------------------------------------------------------
// CordEncode/CordDecode for feature-gated types
// ---------------------------------------------------------------------------

#[cfg(feature = "datetime")]
impl CordEncode for crate::DateTime {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        buf.extend_from_slice(&self.chrono.timestamp().to_be_bytes());
        buf.extend_from_slice(&self.chrono.timestamp_subsec_nanos().to_be_bytes());
        Ok(())
    }
}

#[cfg(feature = "datetime")]
impl CordDecode for crate::DateTime {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        let seconds = i64::decode_cord(input)?;
        let nanos = u32::decode_cord(input)?;
        use chrono::TimeZone;
        let chrono = chrono::Utc
            .timestamp_opt(seconds, nanos)
            .single()
            .ok_or(CordError::ValidationError("Invalid datetime"))?;
        Ok(crate::DateTime { chrono })
    }
}

#[cfg(feature = "uuid")]
impl CordEncode for crate::Uuid {
    fn encode_cord(&self, buf: &mut Vec<u8>) -> CordResult<()> {
        buf.extend_from_slice(self.inner.as_bytes());
        Ok(())
    }
}

#[cfg(feature = "uuid")]
impl CordDecode for crate::Uuid {
    fn decode_cord(input: &mut &[u8]) -> CordResult<Self> {
        let bytes = crate::wire::read_bytes(input, 16)?;
        Ok(crate::Uuid {
            inner: uuid::Uuid::from_bytes(bytes.try_into().unwrap()),
        })
    }
}

// ---------------------------------------------------------------------------
// Convenience functions
// ---------------------------------------------------------------------------

/// Encode a value to canonical Cord bytes.
pub fn encode<T: CordEncode>(value: &T) -> CordResult<Vec<u8>> {
    let mut buf = Vec::with_capacity(64);
    value.encode_cord(&mut buf)?;
    Ok(buf)
}

/// Decode a value from canonical Cord bytes.
pub fn decode<T: CordDecode>(bytes: &[u8]) -> CordResult<T> {
    let mut input = bytes;
    let value = T::decode_cord(&mut input)?;
    if !input.is_empty() {
        return Err(CordError::ValidationError("Unexpected trailing bytes"));
    }
    Ok(value)
}
