//! Per-client connection handler.
//!
//! Each accepted TCP connection is managed by [`handle`], which performs the
//! protocol handshake, registers the client in shared state, relays broadcasts,
//! and processes incoming messages until disconnect or kick.

use std::sync::Arc;

use tokio::net::TcpStream;
use tokio::sync::{broadcast, mpsc};

use super::SharedClients;
use crate::core::protocol::*;

/// Manages a single client connection from handshake through disconnect.
pub async fn handle(
    id: u64,
    stream: TcpStream,
    tx: broadcast::Sender<ServerMessage>,
    mut rx: broadcast::Receiver<ServerMessage>,
    clients: SharedClients,
    kick_tx: mpsc::UnboundedSender<()>,
    mut kick_rx: mpsc::UnboundedReceiver<()>,
) {
    let (mut reader, mut writer) = stream.into_split();

    // --- Handshake ---

    let hello: ClientMessage = match read_frame(&mut reader).await {
        Ok(msg) => msg,
        Err(_) => return,
    };

    match hello {
        ClientMessage::Hello { protocol_version } if protocol_version == PROTOCOL_VERSION => {}
        ClientMessage::Hello { protocol_version } => {
            let _ = write_frame(
                &mut writer,
                &ServerMessage::ProtocolError {
                    reason: format!(
                        "version mismatch: expected {PROTOCOL_VERSION}, got {protocol_version}"
                    ),
                },
            )
            .await;
            return;
        }
        _ => return,
    }

    if write_frame(&mut writer, &ServerMessage::Welcome)
        .await
        .is_err()
    {
        return;
    }

    // --- Registration ---

    let mut my_name = format!("Player{id}");
    {
        let mut clients = clients.lock().unwrap();
        clients.insert(
            id,
            super::ClientInfo {
                name: my_name.clone(),
                kick_tx,
            },
        );
    }

    let _ = tx.send(ServerMessage::System {
        content: format!("{my_name} joined"),
    });

    // --- Broadcast relay (server -> this client) ---

    let writer = Arc::new(tokio::sync::Mutex::new(writer));
    let writer_clone = writer.clone();
    let writer_handle = tokio::spawn(async move {
        while let Ok(msg) = rx.recv().await {
            let mut w = writer_clone.lock().await;
            if write_frame(&mut *w, &msg).await.is_err() {
                break;
            }
        }
    });

    // --- Message loop (this client -> server) ---

    loop {
        tokio::select! {
            result = read_frame::<_, ClientMessage>(&mut reader) => {
                match result {
                    Ok(ClientMessage::SetName { name }) => {
                        let old_name = my_name.clone();
                        my_name = name.clone();
                        if let Ok(mut clients) = clients.lock() {
                            if let Some(client) = clients.get_mut(&id) {
                                client.name = name.clone();
                            }
                        }
                        let _ = tx.send(ServerMessage::System {
                            content: format!("{old_name} is now known as {name}"),
                        });
                    }
                    Ok(ClientMessage::Chat { content }) => {
                        let _ = tx.send(ServerMessage::Chat {
                            sender: my_name.clone(),
                            content,
                        });
                    }
                    Ok(ClientMessage::Hello { .. }) => {}
                    Err(_) => break,
                }
            }
            _ = kick_rx.recv() => {
                break;
            }
        }
    }

    // --- Cleanup ---

    writer_handle.abort();
    // Only send "left" if still in the map (kicked clients are already removed)
    if let Some(client) = clients.lock().unwrap().remove(&id) {
        let _ = tx.send(ServerMessage::System {
            content: format!("{} left", client.name),
        });
    }
}
