use super::{
    configuration::WS_ALLOWED_DEV,
    helpers::{get_ws_url, process_ws_message, send_command_using_transmitter},
    models::command::FilterCommand,
    types::{EvokReceiver, EvokSender},
};
use futures::{SinkExt, StreamExt};
use log::{debug, info, warn};
use std::{process, time::Duration};
use tokio_tungstenite::connect_async;
use tungstenite::protocol::Message;

pub async fn run(channel: (EvokSender, EvokReceiver)) {
    // Create a channel for sending messages to the websocket
    let (tx, mut rx) = channel;
    let interval_ping_tx = tx.clone();

    let temp_filter: Vec<u8> =
        String::from("{\"cmd\":\"filter\",\"devices\":[\"relay\",\"temp\",\"led\"]}")
            .as_bytes()
            .to_vec();
    tx.send(temp_filter).await.unwrap();

    // filter devices that are allowed to send messages
    send_command_using_transmitter(&tx, &FilterCommand::new(&WS_ALLOWED_DEV)).await;
    // get all data and initialize app state
    // send_command_using_transmitter(&tx, &AllCommand::new()).await;

    let ws = async move {
        let url = get_ws_url().expect("Could not obtain URL!");
        // Connect to WS
        let (mut ws_stream, _) = connect_async(url).await.expect("Failed to connect WS");
        info!("WebSocket handshake has been successfully completed");

        loop {
            tokio::select! {
                ws_msg = ws_stream.next() => {
                    match ws_msg {
                        Some(msg) => match msg {
                            Ok(msg) => process_ws_message(msg),
                            Err(_) => {
                                warn!("Server went down");
                                process::exit(1);
                            }
                        },
                        None => {
                            info!("Websocket closed by message during the runtime");
                            break;
                        },
                    }
                },
                stdin_msg = rx.recv() => {
                    match stdin_msg {
                        Some(msg) => {
                            let message_to_send = match msg.is_empty() {
                                true => Message::Ping(msg),
                                false => Message::Binary(msg),
                            };
                            let _ = ws_stream.send(message_to_send);
                        },
                        None => break
                    }
                }
                // Pinging the websocket to keep it alive
                _ = tokio::time::sleep(Duration::from_secs(30)) => {
                    debug!("Ping websocket");
                    interval_ping_tx.send(Vec::new()).await.unwrap();
                }
            }
        }
    };

    tokio::task::spawn(ws);

    // let _ = ws_stream.send(Message::Close(None)).await;
    // let close = ws_stream.try_next().await;
    // println!("Server close msg: {:?}", close);
}
