use crate::Bytes;
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::convert::TryInto as _;
use core::fmt::{self, Debug};
use core::hash::{Hash, Hasher};
use core::ops::{Deref, DerefMut};

use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
use serde::ser::{Serialize, Serializer};

/// Wrapper around `[u8; N]` to serialize and deserialize efficiently.
///
/// ```
/// use std::collections::HashMap;
/// use std::io;
///
/// use serde_bytes::ByteArray;
///
/// fn deserialize_bytearrays() -> bincode::Result<()> {
///     let example_data = [
///         2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 116,
///         119, 111, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 111, 110, 101
///     ];
///
///     let map: HashMap<u32, ByteArray<3>> = bincode::deserialize(&example_data[..])?;
///
///     println!("{:?}", map);
///
///     Ok(())
/// }
/// #
/// # fn main() {
/// #     deserialize_bytearrays().unwrap();
/// # }
/// ```
#[derive(Copy, Clone, Eq, Ord)]
#[repr(transparent)]
pub struct ByteArray<const N: usize> {
    bytes: [u8; N],
}

impl<const N: usize> ByteArray<N> {
    /// Wrap an existing [array] into a `ByteArray`.
    pub const fn new(bytes: [u8; N]) -> Self {
        ByteArray { bytes }
    }

    /// Unwrap the byte array underlying this `ByteArray`.
    pub const fn into_array(self) -> [u8; N] {
        self.bytes
    }

    fn from_ref(bytes: &[u8; N]) -> &Self {
        unsafe { &*(bytes as *const [u8; N] as *const ByteArray<N>) }
    }
}

impl<const N: usize> Debug for ByteArray<N> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.bytes, f)
    }
}

impl<const N: usize> Default for ByteArray<N> {
    fn default() -> Self {
        ByteArray { bytes: [0; N] }
    }
}

impl<const N: usize> AsRef<[u8; N]> for ByteArray<N> {
    fn as_ref(&self) -> &[u8; N] {
        &self.bytes
    }
}

impl<const N: usize> AsMut<[u8; N]> for ByteArray<N> {
    fn as_mut(&mut self) -> &mut [u8; N] {
        &mut self.bytes
    }
}

impl<const N: usize> Borrow<[u8; N]> for ByteArray<N> {
    fn borrow(&self) -> &[u8; N] {
        &self.bytes
    }
}

impl<const N: usize> BorrowMut<[u8; N]> for ByteArray<N> {
    fn borrow_mut(&mut self) -> &mut [u8; N] {
        &mut self.bytes
    }
}

impl<const N: usize> Deref for ByteArray<N> {
    type Target = [u8; N];

    fn deref(&self) -> &Self::Target {
        &self.bytes
    }
}

impl<const N: usize> DerefMut for ByteArray<N> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.bytes
    }
}

impl<const N: usize> Borrow<Bytes> for ByteArray<N> {
    fn borrow(&self) -> &Bytes {
        Bytes::new(&self.bytes)
    }
}

impl<const N: usize> BorrowMut<Bytes> for ByteArray<N> {
    fn borrow_mut(&mut self) -> &mut Bytes {
        unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }
    }
}

impl<const N: usize> From<[u8; N]> for ByteArray<N> {
    fn from(bytes: [u8; N]) -> Self {
        ByteArray { bytes }
    }
}

impl<Rhs, const N: usize> PartialEq<Rhs> for ByteArray<N>
where
    Rhs: ?Sized + Borrow<[u8; N]>,
{
    fn eq(&self, other: &Rhs) -> bool {
        self.as_ref().eq(other.borrow())
    }
}

impl<Rhs, const N: usize> PartialOrd<Rhs> for ByteArray<N>
where
    Rhs: ?Sized + Borrow<[u8; N]>,
{
    fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.borrow())
    }
}

impl<const N: usize> Hash for ByteArray<N> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.bytes.hash(state);
    }
}

impl<const N: usize> IntoIterator for ByteArray<N> {
    type Item = u8;
    type IntoIter = <[u8; N] as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        IntoIterator::into_iter(self.bytes)
    }
}

impl<'a, const N: usize> IntoIterator for &'a ByteArray<N> {
    type Item = &'a u8;
    type IntoIter = <&'a [u8; N] as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.bytes.iter()
    }
}

impl<'a, const N: usize> IntoIterator for &'a mut ByteArray<N> {
    type Item = &'a mut u8;
    type IntoIter = <&'a mut [u8; N] as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.bytes.iter_mut()
    }
}

impl<const N: usize> Serialize for ByteArray<N> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&self.bytes)
    }
}

struct ByteArrayVisitor<const N: usize>;

impl<'de, const N: usize> Visitor<'de> for ByteArrayVisitor<N> {
    type Value = ByteArray<N>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "a byte array of length {}", N)
    }

    fn visit_seq<V>(self, mut seq: V) -> Result<ByteArray<N>, V::Error>
    where
        V: SeqAccess<'de>,
    {
        let mut bytes = [0; N];

        for (idx, byte) in bytes.iter_mut().enumerate() {
            *byte = seq
                .next_element()?
                .ok_or_else(|| V::Error::invalid_length(idx, &self))?;
        }

        Ok(ByteArray::new(bytes))
    }

    fn visit_bytes<E>(self, v: &[u8]) -> Result<ByteArray<N>, E>
    where
        E: Error,
    {
        Ok(ByteArray {
            bytes: v
                .try_into()
                .map_err(|_| E::invalid_length(v.len(), &self))?,
        })
    }

    fn visit_str<E>(self, v: &str) -> Result<ByteArray<N>, E>
    where
        E: Error,
    {
        self.visit_bytes(v.as_bytes())
    }
}

impl<'de, const N: usize> Deserialize<'de> for ByteArray<N> {
    fn deserialize<D>(deserializer: D) -> Result<ByteArray<N>, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_bytes(ByteArrayVisitor::<N>)
    }
}

struct BorrowedByteArrayVisitor<const N: usize>;

impl<'de, const N: usize> Visitor<'de> for BorrowedByteArrayVisitor<N> {
    type Value = &'de ByteArray<N>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "a borrowed byte array of length {}", N)
    }

    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
    where
        E: Error,
    {
        let borrowed_byte_array: &'de [u8; N] = v
            .try_into()
            .map_err(|_| E::invalid_length(v.len(), &self))?;
        Ok(ByteArray::from_ref(borrowed_byte_array))
    }

    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        self.visit_borrowed_bytes(v.as_bytes())
    }
}

impl<'a, 'de: 'a, const N: usize> Deserialize<'de> for &'a ByteArray<N> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_bytes(BorrowedByteArrayVisitor::<N>)
    }
}
