//! Game server: accepts client connections, manages state, and broadcasts messages.
//!
//! The server runs a TUI for administration. Commands like `/players` and
//! `/kick <name>` are processed through a Rhai scripting engine.
//!
//! ## Module layout
//!
//! - [`connection`]: Per-client TCP handler (handshake, message relay, cleanup).
//! - [`scripting`]: Server-side Rhai engine and admin command definitions.

mod connection;
mod scripting;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind, KeyModifiers};
use futures::StreamExt;
use tokio::net::TcpListener;
use tokio::sync::{broadcast, mpsc};

use crate::core::protocol::*;
use crate::script::dispatch_command;
use crate::ui;

/// Per-client metadata stored in shared state.
struct ClientInfo {
    name: String,
    kick_tx: mpsc::UnboundedSender<()>,
}

/// Thread-safe handle to the connected clients map.
type SharedClients = Arc<Mutex<HashMap<u64, ClientInfo>>>;

pub async fn run(host: &str, port: u16) {
    let listener = TcpListener::bind((host, port)).await.unwrap();

    let (tx, _) = broadcast::channel::<ServerMessage>(256);
    let clients: SharedClients = Arc::new(Mutex::new(HashMap::new()));
    let (log_tx, mut log_rx) = mpsc::unbounded_channel::<String>();

    log_tx
        .send(format!("Server listening on port {port}"))
        .unwrap();

    // --- Accept loop (runs in background) ---

    let tx_accept = tx.clone();
    let clients_accept = clients.clone();
    let log_tx_accept = log_tx.clone();
    tokio::spawn(async move {
        let mut next_id: u64 = 0;
        loop {
            let (stream, addr) = listener.accept().await.unwrap();
            let _ = log_tx_accept.send(format!("Connection from {addr}"));

            let id = next_id;
            next_id += 1;

            let (kick_tx, kick_rx) = mpsc::unbounded_channel();
            let tx = tx_accept.clone();
            let rx = tx.subscribe();
            let clients = clients_accept.clone();

            tokio::spawn(connection::handle(
                id, stream, tx, rx, clients, kick_tx, kick_rx,
            ));
        }
    });

    // --- Rhai engine ---

    let (engine, ast) = scripting::create_engine(clients.clone(), tx.clone());

    // --- TUI ---

    let mut guard = ui::TerminalGuard::new();
    let mut messages: Vec<String> = Vec::new();
    let mut input = String::new();
    let mut event_stream = EventStream::new();
    let mut broadcast_rx = tx.subscribe();
    let mut confirming_quit = false;

    loop {
        guard
            .terminal
            .draw(|f| {
                ui::render_chat_layout(f, &messages, &input, "Server", "Command");

                if confirming_quit {
                    ui::render_modal(
                        f,
                        " Confirm ",
                        "Shut down server?",
                        &["Yes (Y)", "No (N)"],
                    );
                }
            })
            .unwrap();

        // Modal input loop: blocks normal event handling while visible.
        if confirming_quit {
            while let Some(Ok(event)) = event_stream.next().await {
                if let Event::Key(key) = event {
                    if key.kind != KeyEventKind::Press {
                        continue;
                    }
                    match key.code {
                        KeyCode::Char('y') | KeyCode::Char('Y') => return,
                        KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => {
                            confirming_quit = false;
                            break;
                        }
                        _ => {}
                    }
                }
            }
            continue;
        }

        tokio::select! {
            Some(Ok(event)) = event_stream.next() => {
                if let Event::Key(key) = event {
                    if key.kind != KeyEventKind::Press {
                        continue;
                    }
                    match key.code {
                        KeyCode::Esc
                        | KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                            confirming_quit = true;
                        }
                        KeyCode::Enter => {
                            let line: String = input.drain(..).collect();
                            if line.is_empty() {
                                continue;
                            }

                            if line.starts_with('/') {
                                match dispatch_command(&engine, &ast, &line) {
                                    Ok(Some(feedback)) => messages.push(feedback),
                                    Ok(None) => {}
                                    Err(e) => messages.push(format!("[Error] {e}")),
                                }
                            } else {
                                let _ = tx.send(ServerMessage::System { content: line });
                            }
                        }
                        KeyCode::Backspace => {
                            input.pop();
                        }
                        KeyCode::Char(c) => input.push(c),
                        _ => {}
                    }
                }
            }
            Ok(msg) = broadcast_rx.recv() => {
                match &msg {
                    ServerMessage::Chat { sender, content } => {
                        messages.push(format!("{sender}: {content}"));
                    }
                    ServerMessage::System { content } => {
                        messages.push(format!("[System] {content}"));
                    }
                    _ => {}
                }
            }
            Some(log) = log_rx.recv() => {
                messages.push(log);
            }
            else => break,
        }
    }
}
