//! Thread WebSocket qui écoute les connexions de l'éditeur.
//!
//! Écoute sur `0.0.0.0:{port}` (accessible en Wi-Fi pour Android).
//! Une seule connexion à la fois. Si l'éditeur se reconnecte, la précédente
//! est abandonnée et une nouvelle session commence.
//!
//! Transport : WebSocket (tungstenite), messages texte newline-délimités JSON.
//! Protocole JSON identique à l'ancienne version TCP → compatibilité éditeur.

use std::net::TcpListener;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::time::Duration;

use san_motor::debug_protocol::{EditorToGame, GameToEditor};
use tungstenite::{Message, accept};

use crate::DebugChannel;

pub fn start_listener(host: &str, port: u16) -> DebugChannel {
    // canal A : éditeur → jeu (listener envoie, handler Bevy reçoit)
    let (tx_cmd, rx_cmd) = channel::<EditorToGame>();
    // canal B : jeu → éditeur (handler Bevy envoie, listener thread lit et écrit sur le socket)
    let (tx_resp, rx_resp) = channel::<GameToEditor>();

    let addr = format!("{host}:{port}");
    let handle = thread::Builder::new()
        .name("san-debug-listener".into())
        .spawn(move || listener_thread(addr, tx_cmd, rx_resp))
        .expect("spawn san-debug-listener");

    DebugChannel {
        rx: std::sync::Mutex::new(rx_cmd),
        tx: tx_resp,
        _thread: handle,
    }
}

fn listener_thread(
    addr: String,
    tx_cmd: Sender<EditorToGame>,
    rx_resp: Receiver<GameToEditor>,
) {
    let listener = match TcpListener::bind(&addr) {
        Ok(l) => l,
        Err(e) => {
            eprintln!("[san-debug] Bind {addr} échoué : {e}");
            return;
        }
    };
    info_log(&format!("[san-debug] Écoute WebSocket sur {addr}"));

    for stream in listener.incoming() {
        let Ok(tcp_stream) = stream else { continue };
        info_log(&format!(
            "[san-debug] Connexion TCP entrante : {:?}",
            tcp_stream.peer_addr()
        ));

        // Délai de lecture non-bloquant pour permettre de vider rx_resp régulièrement
        let _ = tcp_stream.set_read_timeout(Some(Duration::from_millis(50)));

        // Handshake WebSocket
        let ws = match accept(tcp_stream) {
            Ok(ws) => ws,
            Err(e) => {
                eprintln!("[san-debug] Handshake WebSocket échoué : {e}");
                continue;
            }
        };

        info_log("[san-debug] Éditeur connecté via WebSocket");
        handle_connection(ws, &tx_cmd, &rx_resp);
        info_log("[san-debug] Éditeur déconnecté");
    }
}

fn handle_connection(
    mut ws: tungstenite::WebSocket<std::net::TcpStream>,
    tx_cmd: &Sender<EditorToGame>,
    rx_resp: &Receiver<GameToEditor>,
) {
    loop {
        // Vider la file des réponses en attente → écrire sur le WebSocket
        while let Ok(msg) = rx_resp.try_recv() {
            if let Ok(json) = serde_json::to_string(&msg) {
                if ws.send(Message::Text(json.into())).is_err() {
                    return;
                }
            }
        }

        // Lire un message entrant (non-bloquant grâce au read_timeout)
        match ws.read() {
            Ok(Message::Text(text)) => {
                let text = text.trim().to_string();
                if text.is_empty() {
                    continue;
                }
                match serde_json::from_str::<EditorToGame>(&text) {
                    Ok(cmd) => {
                        let _ = tx_cmd.send(cmd);
                    }
                    Err(e) => {
                        eprintln!("[san-debug] Message invalide: {text} — {e}");
                    }
                }
            }
            Ok(Message::Close(_)) => {
                break;
            }
            Ok(_) => {} // Ping/Pong/Binary ignorés
            Err(tungstenite::Error::Io(ref e))
                if e.kind() == std::io::ErrorKind::WouldBlock
                    || e.kind() == std::io::ErrorKind::TimedOut =>
            {
                // Timeout de lecture — continuer pour vider rx_resp
                continue;
            }
            Err(e) => {
                eprintln!("[san-debug] Erreur WebSocket : {e}");
                break;
            }
        }
    }
}

fn info_log(msg: &str) {
    println!("{msg}");
}
