use super::types::{EvokSender, UnipiObjects};
use crate::APP_STATE;
use log::{debug, error};
use serde::Serialize;
use std::env;
use tungstenite::Message;
use url::Url;

/// Obtain the url from the environment variables and parse it
pub fn get_ws_url() -> Result<Url, ()> {
    let ip = match env::var("EVOK_IP_ADDRESS") {
        Ok(val) => {
            if val.is_empty() {
                "localhost".to_string()
            } else {
                val
            }
        }
        Err(_) => "localhost".to_string(),
    };

    let port = match env::var("EVOK_PORT") {
        Ok(val) => {
            if val.is_empty() {
                "8086".to_string()
            } else {
                val
            }
        }
        Err(_) => "8086".to_string(),
    };

    let url = Url::parse(&format!("ws://{}:{}/ws", ip, port)).unwrap();
    Ok(url)
}

/// Process the message received from the websocket
pub fn process_ws_message(msg: Message) {
    match msg {
        Message::Text(x) => {
            let mut err = None;
            // if the message is array of objects
            let mut devices: Vec<UnipiObjects> = match serde_json::from_str(&x) {
                Ok(val) => val,
                Err(e) => {
                    err = Some(e);
                    Vec::new()
                }
            };
            // if the message is a single object
            match serde_json::from_str::<UnipiObjects>(&x) {
                Ok(val) => {
                    devices.push(val);
                }
                Err(e) => {
                    err = Some(e);
                }
            };

            if devices.is_empty() {
                error!("Could not parse received state from WS: {:?}", err);
                return;
            }

            // TODO -> parse objects (devs)
            let mut lock = APP_STATE.lock().unwrap();
            devices.into_iter().for_each(|dev| match dev {
                UnipiObjects::Register(_register) => {}
                UnipiObjects::OneWire(ow) => {
                    lock.evok_state.temperature = ow.value;
                    debug!("{} temperature: {}°C", ow.address, ow.value.unwrap());
                }
                _ => {}
            });
        }
        Message::Binary(x) => debug!("Binary {:?}", x),
        Message::Ping(x) => debug!("Ping {:?}", x),
        Message::Pong(x) => debug!("Pong {:?}", x),
        Message::Close(x) => debug!("Close {:?}", x),
        Message::Frame(_) => debug!("frame"),
    }
}

pub async fn send_command_using_transmitter<T>(tx: &EvokSender, cmd: &T)
where
    T: Serialize,
{
    tx.send(serde_json::to_vec(cmd).unwrap()).await.unwrap();
}
