use tokio::io::{ErrorKind, Interest};
use tokio::net::{TcpListener, TcpStream};

pub(crate) struct TcpServer;

impl TcpServer {
    pub(crate) async fn init() {
        match TcpListener::bind("127.0.0.1:12345").await {
            Ok(listener) => loop {
                match listener.accept().await {
                    Ok((socket, addr)) => {
                        log::info!("New client connected: {addr}");

                        tokio::spawn(async { Self::process(socket).await });
                    }
                    Err(e) => log::error!("Couldn't get client: {e:?}"),
                }
            },
            Err(e) => log::error!("Failed to bind port: {e:?}"),
        }
    }

    async fn process(socket: TcpStream) {
        loop {
            match socket.ready(Interest::READABLE | Interest::WRITABLE).await {
                Ok(ready) => {
                    if ready.is_readable() {
                        let mut buffer = Vec::with_capacity(1024);
                        match socket.try_read_buf(&mut buffer) {
                            // We need to write to client after reading from that,
                            // so we use `continue` here instead if `break`.
                            Ok(_) => {
                                log::info!("Received contents");

                                // TODO: Start dispatching messages here.

                                continue;
                            }
                            Err(e) if e.kind() == ErrorKind::WouldBlock => continue,
                            Err(e) => {
                                log::error!("Failed to read from TCP client socket: {e}");

                                break;
                            }
                        }
                    }
                    if ready.is_writable() {
                        match socket.try_write(b"Hello from server written in Rust!") {
                            Ok(_) => break,
                            Err(e) if e.kind() == ErrorKind::WouldBlock => continue,
                            Err(e) => {
                                log::error!("Failed to write to TCP client socket: {e}");

                                break;
                            }
                        }
                    }
                }
                Err(e) => log::error!("Failed to wait for any states: {e}"),
            }
        }
    }
}
