use std::{collections::HashMap, fmt::Display, marker::PhantomData};

use buffers::{ByteBuf, ByteBufOwned};
use bytes::Bytes;
use clone_to_owned::CloneToOwned;
use serde::{Deserialize, Deserializer, Serialize};

use crate::deserialize::from_bytes;

pub fn dyn_from_bytes<'de, BufT>(
    buf: &'de [u8],
) -> Result<BencodeValue<BufT>, crate::deserialize::ErrorWithContext<'de>>
where
    BufT: From<&'de [u8]> + std::hash::Hash + Eq,
{
    from_bytes(buf)
}

impl<BufT: serde::Serialize + Eq + std::hash::Hash> serde::Serialize for BencodeValue<BufT> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            BencodeValue::Bytes(b) => b.serialize(serializer),
            BencodeValue::Integer(v) => v.serialize(serializer),
            BencodeValue::List(l) => l.serialize(serializer),
            BencodeValue::Dict(d) => d.serialize(serializer),
        }
    }
}

impl<'de, BufT> serde::de::Deserialize<'de> for BencodeValue<BufT>
where
    BufT: From<&'de [u8]> + std::hash::Hash + Eq,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct Visitor<BufT> {
            buftype: PhantomData<BufT>,
        }

        impl<'de, BufT> serde::de::Visitor<'de> for Visitor<BufT>
        where
            BufT: From<&'de [u8]> + std::hash::Hash + Eq,
        {
            type Value = BencodeValue<BufT>;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(formatter, "a bencode value")
            }

            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(BencodeValue::Integer(v))
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: serde::de::SeqAccess<'de>,
            {
                let mut v = Vec::new();
                while let Some(value) = seq.next_element()? {
                    v.push(value);
                }
                Ok(BencodeValue::List(v))
            }

            fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(BencodeValue::Bytes(BufT::from(v)))
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: serde::de::MapAccess<'de>,
            {
                let mut hmap = HashMap::new();
                while let Some(key) = map.next_key::<&'de [u8]>()? {
                    let value = map.next_value()?;
                    hmap.insert(BufT::from(key), value);
                }
                Ok(BencodeValue::Dict(hmap))
            }
        }

        deserializer.deserialize_any(Visitor {
            buftype: PhantomData,
        })
    }
}

// A dynamic value when we don't know exactly what we are deserializing.
// Useful for debugging.

#[derive(PartialEq, Eq)]
pub enum BencodeValue<BufT: std::hash::Hash + Eq> {
    Bytes(BufT),
    Integer(i64),
    List(Vec<BencodeValue<BufT>>),
    Dict(HashMap<BufT, BencodeValue<BufT>>),
}

impl<BufT: std::fmt::Debug + std::hash::Hash + Eq> std::fmt::Debug for BencodeValue<BufT> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BencodeValue::Bytes(b) => std::fmt::Debug::fmt(b, f),
            BencodeValue::Integer(i) => std::fmt::Debug::fmt(i, f),
            BencodeValue::List(l) => std::fmt::Debug::fmt(l, f),
            BencodeValue::Dict(d) => std::fmt::Debug::fmt(d, f),
        }
    }
}

impl<BufT> CloneToOwned for BencodeValue<BufT>
where
    BufT: CloneToOwned + std::hash::Hash + Eq,
    <BufT as CloneToOwned>::Target: Eq + std::hash::Hash,
{
    type Target = BencodeValue<<BufT as CloneToOwned>::Target>;

    fn clone_to_owned(&self, within_buffer: Option<&Bytes>) -> Self::Target {
        match self {
            BencodeValue::Bytes(b) => BencodeValue::Bytes(b.clone_to_owned(within_buffer)),
            BencodeValue::Integer(i) => BencodeValue::Integer(*i),
            BencodeValue::List(l) => BencodeValue::List(l.clone_to_owned(within_buffer)),
            BencodeValue::Dict(d) => BencodeValue::Dict(d.clone_to_owned(within_buffer)),
        }
    }
}

pub type BencodeValueBorrowed<'a> = BencodeValue<ByteBuf<'a>>;
pub type BencodeValueOwned = BencodeValue<ByteBufOwned>;

// A wrapper to deserialize dyn values as strings.
#[derive(PartialEq, Eq, Hash)]
pub struct AsDisplay<T>(T);

impl<'de, T> From<&'de [u8]> for AsDisplay<T>
where
    T: From<&'de [u8]>,
{
    fn from(value: &'de [u8]) -> Self {
        Self(T::from(value))
    }
}

impl<T> Serialize for AsDisplay<T>
where
    T: Display,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(&format!("{}", self.0))
    }
}

impl<'de, T> Deserialize<'de> for AsDisplay<T>
where
    T: Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = T::deserialize(deserializer)?;
        Ok(AsDisplay(value))
    }
}

#[cfg(test)]
mod tests {
    use crate::serialize::bencode_serialize_to_writer;

    use super::*;
    use std::io::Read;

    #[test]
    fn test_deserialize_torrent_dyn() {
        let mut buf = Vec::new();
        let filename = "../librqbit/resources/ubuntu-21.04-desktop-amd64.iso.torrent";
        std::fs::File::open(filename)
            .unwrap()
            .read_to_end(&mut buf)
            .unwrap();

        let torrent_borrowed: BencodeValueBorrowed = from_bytes(&buf).unwrap();
        let torrent_owned: BencodeValueOwned = from_bytes(&buf).unwrap();
        dbg!(torrent_borrowed);
        dbg!(torrent_owned);
    }

    #[test]
    fn test_serialize_torrent_dyn() {
        let mut buf = Vec::new();
        let filename = "../librqbit/resources/ubuntu-21.04-desktop-amd64.iso.torrent";
        std::fs::File::open(filename)
            .unwrap()
            .read_to_end(&mut buf)
            .unwrap();

        let torrent: BencodeValueBorrowed = from_bytes(&buf).unwrap();

        let mut buf = Vec::<u8>::new();
        bencode_serialize_to_writer(&torrent, &mut buf).unwrap();

        let new_torrent = from_bytes(&buf).unwrap();
        assert_eq!(torrent, new_torrent);
    }

    #[test]
    fn test_serialize_struct_with_option() {
        #[derive(serde_derive::Serialize)]
        struct Test {
            f1: i64,
            #[serde(skip_serializing_if = "Option::is_none")]
            missing: Option<i64>,
        }
        let test = Test {
            f1: 100,
            missing: None,
        };
        let mut buf = Vec::<u8>::new();
        bencode_serialize_to_writer(&test, &mut buf).unwrap();
        assert_eq!(&buf, b"d2:f1i100ee");
    }
}
