use hex::encode;
use std::fmt::{Display, Formatter, Result as FmtResult};
use thiserror::Error;

pub const CLIENT_COOKIE_LENGTH: usize = 8;
pub const MINIMUM_SERVER_COOKIE_LENGTH: usize = 8;
pub const MAXIMUM_SERVER_COOKIE_LENGTH: usize = 32;

#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Cookie {
    pub client_cookie: [u8; 8],
    server_cookie: Option<Vec<u8>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
pub enum CookieError {
    #[error("Server cookie length is not between {MINIMUM_SERVER_COOKIE_LENGTH} and {MAXIMUM_SERVER_COOKIE_LENGTH}: {0}]")]
    ServerCookieLength(usize),
}

impl Cookie {
    pub fn new(
        client_cookie: [u8; 8],
        server_cookie: Option<Vec<u8>>,
    ) -> Result<Cookie, CookieError> {
        let mut cookie = Cookie {
            client_cookie,
            server_cookie: None,
        };
        cookie.set_server_cookie(server_cookie)?;
        Ok(cookie)
    }

    pub fn set_server_cookie(&mut self, server_cookie: Option<Vec<u8>>) -> Result<(), CookieError> {
        match server_cookie {
            Some(server_cookie) => {
                let server_cookie_len = server_cookie.len();
                if (MINIMUM_SERVER_COOKIE_LENGTH..MAXIMUM_SERVER_COOKIE_LENGTH)
                    .contains(&server_cookie_len)
                {
                    self.server_cookie.replace(server_cookie);
                    Ok(())
                } else {
                    Err(CookieError::ServerCookieLength(server_cookie_len))
                }
            }
            None => {
                self.server_cookie.take();
                Ok(())
            }
        }
    }

    pub fn get_server_cookie(&self) -> Option<&[u8]> {
        self.server_cookie.as_deref()
    }
}

impl Display for Cookie {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        write!(f, "Cookie {}", encode(self.client_cookie))?;
        match &self.server_cookie {
            Some(server_cookie) => write!(f, " {}", encode(server_cookie)),
            None => Ok(()),
        }
    }
}
