use std::io::{self, BufRead};

use sgclaw::service::{ClientMessage, ServiceMessage};
use tungstenite::{connect, Message};

fn main() -> std::process::ExitCode {
    match run() {
        Ok(()) => std::process::ExitCode::SUCCESS,
        Err(err) => {
            eprintln!("sg_claw_client failed: {err}");
            std::process::ExitCode::FAILURE
        }
    }
}

fn parse_request(input: &str) -> (ClientMessage, bool) {
    match input.trim() {
        "/connect" => (ClientMessage::Connect, true),
        "/start" => (ClientMessage::Start, true),
        "/stop" => (ClientMessage::Stop, true),
        instruction => (
            ClientMessage::SubmitTask {
                instruction: instruction.to_string(),
                conversation_id: String::new(),
                messages: vec![],
                page_url: String::new(),
                page_title: String::new(),
            },
            false,
        ),
    }
}

fn run() -> Result<(), String> {
    let service_url = std::env::var("SG_CLAW_SERVICE_WS_URL")
        .unwrap_or_else(|_| "ws://127.0.0.1:42321".to_string());
    let (mut socket, _) = connect(service_url.as_str()).map_err(|err| err.to_string())?;

    let stdin = io::stdin();

    loop {
        eprint!("> ");
        let mut input = String::new();
        let bytes_read = stdin
            .lock()
            .read_line(&mut input)
            .map_err(|err| err.to_string())?;
        if bytes_read == 0 {
            break; // EOF — graceful exit
        }
        if input.trim().is_empty() {
            continue;
        }

        let (request, exit_on_status) = parse_request(&input);

        let payload = serde_json::to_string(&request).map_err(|err| err.to_string())?;
        socket
            .send(Message::Text(payload.into()))
            .map_err(|err| err.to_string())?;

        // Inner loop: consume service messages until the task finishes.
        loop {
            match socket.read().map_err(|err| err.to_string())? {
                Message::Text(text) => {
                    let message: ServiceMessage =
                        serde_json::from_str(&text).map_err(|err| err.to_string())?;
                    match message {
                        ServiceMessage::StatusChanged { state } => {
                            println!("status: {state}");
                            if exit_on_status {
                                break;
                            }
                        }
                        ServiceMessage::LogEntry { level: _, message } => {
                            println!("{message}");
                        }
                        ServiceMessage::TaskComplete { success: _, summary } => {
                            println!("{summary}");
                            break;
                        }
                        ServiceMessage::Busy { message } => {
                            eprintln!("busy: {message}");
                            break;
                        }
                        ServiceMessage::Pong => {}
                    }
                }
                Message::Close(_) => {
                    return Err("service disconnected".to_string());
                }
                _ => {}
            }
        }
    }

    Ok(())
}
