//! Game client: connects to the server and provides a TUI for chatting.
//!
//! Input starting with `/` is dispatched to the Rhai scripting engine.
//! All other input is sent as chat messages.

use std::sync::Arc;

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

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

pub async fn run(addr: &str) {
    // --- Connect and handshake ---

    let stream = match TcpStream::connect(addr).await {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Failed to connect to {addr}: {e}");
            return;
        }
    };
    let (mut reader, writer) = stream.into_split();
    let writer = Arc::new(Mutex::new(writer));

    {
        let mut w = writer.lock().await;
        write_frame(
            &mut *w,
            &ClientMessage::Hello {
                protocol_version: PROTOCOL_VERSION,
            },
        )
        .await
        .unwrap();
    }

    let welcome: ServerMessage = read_frame(&mut reader).await.unwrap();
    match welcome {
        ServerMessage::Welcome => {}
        ServerMessage::ProtocolError { reason } => {
            eprintln!("Protocol error: {reason}");
            return;
        }
        _ => {
            eprintln!("Unexpected message during handshake");
            return;
        }
    }

    // --- Channels and scripting ---

    let (out_tx, mut out_rx) = mpsc::unbounded_channel::<ClientMessage>();
    let script_host = ScriptHost::new(out_tx.clone());

    // Outgoing writer task
    let writer_clone = writer.clone();
    tokio::spawn(async move {
        while let Some(msg) = out_rx.recv().await {
            let mut w = writer_clone.lock().await;
            if write_frame(&mut *w, &msg).await.is_err() {
                break;
            }
        }
    });

    // Incoming reader task
    let (net_tx, mut net_rx) = mpsc::unbounded_channel::<ServerMessage>();
    tokio::spawn(async move {
        loop {
            match read_frame::<_, ServerMessage>(&mut reader).await {
                Ok(msg) => {
                    if net_tx.send(msg).is_err() {
                        break;
                    }
                }
                Err(_) => break,
            }
        }
    });

    // --- TUI ---

    let mut guard = ui::TerminalGuard::new();
    let mut messages: Vec<String> = vec![
        "Welcome to Realm. Type /name <name> to set your name. Press Esc to quit.".into(),
    ];
    let mut input = String::new();
    let mut event_stream = EventStream::new();
    let mut disconnected = false;

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

                if disconnected {
                    ui::render_modal(
                        f,
                        " Disconnected ",
                        "Disconnected from server.",
                        &["OK (Enter)"],
                    );
                }
            })
            .unwrap();

        // Modal input loop: wait for acknowledgement before exiting.
        if disconnected {
            while let Some(Ok(event)) = event_stream.next().await {
                if let Event::Key(key) = event {
                    if key.kind == KeyEventKind::Press && key.code == KeyCode::Enter {
                        break;
                    }
                }
            }
            break;
        }

        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 => break,
                        KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => break,
                        KeyCode::Enter => {
                            let line: String = input.drain(..).collect();
                            if line.is_empty() {
                                continue;
                            }

                            if line.starts_with('/') {
                                match script_host.handle_command(&line) {
                                    Ok(Some(feedback)) => messages.push(feedback),
                                    Ok(None) => {}
                                    Err(e) => messages.push(format!("[Error] {e}")),
                                }
                            } else {
                                let _ = out_tx.send(ClientMessage::Chat { content: line });
                            }
                        }
                        KeyCode::Backspace => {
                            input.pop();
                        }
                        KeyCode::Char(c) => input.push(c),
                        _ => {}
                    }
                }
            }
            msg = net_rx.recv() => {
                match msg {
                    Some(ServerMessage::Chat { sender, content }) => {
                        messages.push(format!("{sender}: {content}"));
                    }
                    Some(ServerMessage::System { content }) => {
                        messages.push(format!("[System] {content}"));
                    }
                    Some(_) => {}
                    None => {
                        disconnected = true;
                    }
                }
            }
        }
    }
}
