use encodings::FromHex;
use extended_primitives::Buffer;
use serde::de::{self, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use serde_tuple::*;
use std::fmt;

// #[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Serialize_tuple, Clone, Debug, Default)]
pub struct Submit {
    pub username: String,
    pub job_id: String,
    pub nonce_2: Buffer,
    pub timestamp: u64,
    pub nonce: u32,
    pub mask: Option<Buffer>,
}

impl<'de> Deserialize<'de> for Submit {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct SubmitVisitor;
        use serde::de::Error;

        #[derive(Deserialize)]
        #[serde(untagged)]
        enum StringOrInt {
            String(String),
            Int(u64),
        }

        // match StringOrInt::deserialize(deserializer)? {
        //     StringOrInt::String(s) => s.parse().map_err(de::Error::custom),
        //     StringOrInt::Int(i) => Ok(i),
        // }

        impl<'de> Visitor<'de> for SubmitVisitor {
            type Value = Submit;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct Duration")
            }

            fn visit_seq<V>(self, mut seq: V) -> Result<Submit, V::Error>
            where
                V: SeqAccess<'de>,
            {
                let username = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
                let job_id = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
                let nonce_2 = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(2, &self))?;
                let timestamp: StringOrInt = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(3, &self))?;
                let nonce: StringOrInt = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(4, &self))?;

                let mask = seq.next_element()?;

                if mask.is_some() {
                    let real_timestamp = match timestamp {
                        StringOrInt::String(hex) => {
                            let bytes = Vec::from_hex(hex).map_err(Error::custom)?;

                            let mut byte_array = [0u8; 4];
                            byte_array.copy_from_slice(&bytes);

                            u32::from_be_bytes(byte_array) as u64
                        }
                        StringOrInt::Int(raw) => raw,
                    };

                    let real_nonce = match nonce {
                        StringOrInt::String(hex) => {
                            let bytes = Vec::from_hex(hex).map_err(Error::custom)?;

                            let mut byte_array = [0u8; 4];
                            byte_array.copy_from_slice(&bytes);

                            u32::from_be_bytes(byte_array)
                        }
                        StringOrInt::Int(raw) => raw as u32,
                    };

                    // let bytes = Vec::from_hex(timestamp).map_err(Error::custom)?;

                    // let mut byte_array = [0u8; 4];
                    // byte_array.copy_from_slice(&bytes);

                    // let timestamp = u32::from_be_bytes(byte_array) as u64;

                    // let bytes = Vec::from_hex(nonce).map_err(Error::custom)?;

                    // let mut byte_array = [0u8; 4];
                    // byte_array.copy_from_slice(&bytes);

                    // let nonce = u32::from_be_bytes(byte_array);

                    Ok(Submit {
                        username,
                        job_id,
                        nonce_2,
                        timestamp: real_timestamp,
                        nonce: real_nonce,
                        mask,
                    })
                } else {
                    let real_timestamp = match timestamp {
                        StringOrInt::String(raw_time) => raw_time.parse().map_err(Error::custom)?,
                        StringOrInt::Int(raw) => raw,
                    };

                    let real_nonce = match nonce {
                        StringOrInt::String(raw_nonce) => {
                            raw_nonce.parse().map_err(Error::custom)?
                        }
                        StringOrInt::Int(raw) => raw as u32,
                    };

                    Ok(Submit {
                        username,
                        job_id,
                        nonce_2,
                        timestamp: real_timestamp,
                        nonce: real_nonce,
                        mask,
                    })
                }
            }
        }

        deserializer.deserialize_seq(SubmitVisitor)
    }
}
