use lapin::message::Delivery;
use lapin::types::{AMQPValue, FieldTable};

/// Message constructed from AmqpMessage
#[derive(Clone)]
pub struct Message {
    pub data: Vec<u8>,
    pub headers: FieldTable,
}

impl Message {
    /// Sets a header for AmqpMessage
    pub fn add_header(&mut self, key: &str, value: String) {
        self.headers
            .insert(key.into(), AMQPValue::LongString(value.into()))
    }

    /// Receives header in Option<String> format
    ///
    /// Parse String and Int values.
    /// If the header is missing or is in different format, returns None
    ///
    /// ```
    /// # use icee_rabbit_rs::Message;
    /// # use lapin::types::FieldTable;
    /// let message = Message { data: vec![], headers: FieldTable::default(), };
    /// let value = message.header_string("key");
    /// # assert_eq!(None, value);
    /// ```
    pub fn header_string(&self, key: &str) -> Option<String> {
        let h = self.headers.inner().get(key);
        if let Some(h) = h {
            return match h {
                AMQPValue::ShortString(s) => Some(s.to_string()),
                AMQPValue::LongString(s) => Some(s.to_string()),
                AMQPValue::ShortInt(s) => Some(s.to_string()),
                AMQPValue::ShortShortInt(s) => Some(s.to_string()),
                AMQPValue::ShortShortUInt(s) => Some(s.to_string()),
                AMQPValue::LongInt(s) => Some(s.to_string()),
                AMQPValue::LongLongInt(s) => Some(s.to_string()),
                _ => None,
            };
        }

        None
    }

    /// Receives header in String format
    ///
    /// Parse String and Int values. If the header or is in different format returns given default
    ///
    /// ```
    /// # use icee_rabbit_rs::Message;
    /// # use lapin::types::FieldTable;
    /// let message = Message { data: vec![], headers: FieldTable::default(), };
    /// let value = message.header_string_default("key", || "default".into());
    /// # assert_eq!("default".to_string(), value);
    /// ```
    pub fn header_string_default<F: FnOnce() -> String>(&self, key: &str, or_else: F) -> String {
        let h = self.headers.inner().get(key);
        if let Some(h) = h {
            return match h {
                AMQPValue::ShortString(s) => s.to_string(),
                AMQPValue::LongString(s) => s.to_string(),
                AMQPValue::ShortInt(s) => s.to_string(),
                AMQPValue::ShortShortInt(s) => s.to_string(),
                AMQPValue::ShortShortUInt(s) => s.to_string(),
                AMQPValue::LongInt(s) => s.to_string(),
                AMQPValue::LongLongInt(s) => s.to_string(),
                _ => or_else(),
            };
        }

        or_else()
    }

    /// Receives header in Option<i32> format
    ///
    /// Parse Int and tries to parse String values.
    /// If the header is missing or is not valid number, returns None
    ///
    /// ```
    /// # use icee_rabbit_rs::Message;
    /// # use lapin::types::FieldTable;
    /// let message = Message { data: vec![], headers: FieldTable::default(), };
    /// let value = message.header_i32("key");
    /// # assert_eq!(None, value);
    /// ```
    pub fn header_i32(&self, key: &str) -> Option<i32> {
        let h = self.headers.inner().get(key);
        if let Some(h) = h {
            return match h {
                AMQPValue::ShortString(i) => i.to_string().parse::<i32>().ok(),
                AMQPValue::LongString(i) => i.to_string().parse::<i32>().ok(),
                AMQPValue::ShortInt(i) => Some(*i as i32),
                AMQPValue::ShortShortInt(i) => Some(*i as i32),
                AMQPValue::ShortShortUInt(i) => Some(*i as i32),
                AMQPValue::LongInt(i) => Some(*i as i32),
                AMQPValue::LongLongInt(i) => Some(*i as i32),
                _ => None,
            };
        }

        None
    }

    /// Receives header in i32 format
    ///
    /// Parse Int and tries to parse String values.
    /// If the header is missing or is not valid number, returns given default
    ///
    /// ```
    /// # use icee_rabbit_rs::Message;
    /// # use lapin::types::FieldTable;
    /// let message = Message { data: vec![], headers: FieldTable::default(), };
    /// let value = message.header_i32_default("key", 1);
    /// # assert_eq!(1, value);
    /// ```
    pub fn header_i32_default(&self, key: &str, default: i32) -> i32 {
        let h = self.headers.inner().get(key);
        if let Some(h) = h {
            return match h {
                AMQPValue::ShortString(i) => i.to_string().parse::<i32>().unwrap_or(0),
                AMQPValue::LongString(i) => i.to_string().parse::<i32>().unwrap_or(0),
                AMQPValue::ShortInt(i) => *i as i32,
                AMQPValue::ShortShortInt(i) => *i as i32,
                AMQPValue::ShortShortUInt(i) => *i as i32,
                AMQPValue::LongInt(i) => *i as i32,
                AMQPValue::LongLongInt(i) => *i as i32,
                _ => default,
            };
        }

        default
    }
}

impl From<&Delivery> for Message {
    fn from(delivery: &Delivery) -> Self {
        let mut headers = FieldTable::default();
        if let Some(props) = delivery.properties.headers() {
            headers = props.clone();
        }

        Message {
            data: delivery.data.clone(),
            headers,
        }
    }
}
