use crate::utils::SendTextMessageTarget;
use telnet::Event::Data;
use telnet::Telnet;

use crate::parameter::{Parameter, ParameterList};
use crate::response::Response;

fn to_single_response(resp: Response) -> Response {
    match resp {
        Response::DataList(list) => Response::Data(list[0].clone()),
        _ => resp
    }
}

fn read_part(connection: &mut Telnet) -> Result<String, String> {
    match connection.read() {
        Ok(event) => {
            match event {
                Data(bytes) => Ok(String::from_utf8(bytes.to_vec())
                    .map_err(|_| "Teamspeak returned a badly formatted response.")?),
                _ => {
                    Err(String::from("Received unknown event from Teamspeak."))
                }
            }
        }
        Err(_) => {
            Err(String::from("Failed to read from Teamspeak."))
        }
    }
}

fn read_response_buffer(connection: &mut Telnet, buffer: &mut String) -> Result<(String, String), String> {
    loop {
        buffer.push_str(&read_part(connection)?);
        match buffer.split_once("\n\r") {
            None => {}
            Some((response, remaining)) => {
                return Ok((String::from(response), String::from(remaining)));
            }
        }
    }
}

fn send_command(connection: &mut Telnet, command: &str, skip_ok: bool) -> Result<Response, String> {
    let command = format!("{}\n\r", command);
    connection.write(command.as_bytes()).map_err(|_| "Failed to write to Teamspeak.")?;

    read_response(connection, skip_ok, String::new())
}

fn read_response(connection: &mut Telnet, skip_ok: bool, mut buffer: String) -> Result<Response, String> {
    let (response_str, buffer) = read_response_buffer(connection, &mut buffer)?;

    match Response::try_from(response_str) {
        Ok(resp) => {
            Ok(resp)
        }
        Err(err) => {
            if err.is_error_okay() {
                if skip_ok {
                    read_response(connection, skip_ok, buffer)
                } else {
                    Ok(Response::Ok)
                }
            } else {
                Err(format!("Received error response from Teamspeak: {} ({})", err.msg, err.id))
            }
        }
    }
}

pub fn login(connection: &mut Telnet, apikey: &str) -> Result<Response, String> {
    send_command(connection, &format!("auth apikey={}", apikey), false)
}

#[allow(dead_code)]
pub fn set_name(connection: &mut Telnet, name: &str) -> Result<Response, String> {
    send_command(connection, &format!("clientupdate client_nickname={}", name), true)
}

pub fn channellist(connection: &mut Telnet) -> Result<Response, String> {
    send_command(connection, "channellist", true)
}

pub fn clientlist(connection: &mut Telnet) -> Result<Response, String> {
    send_command(connection, "clientlist", true)
}

pub fn whoami(connection: &mut Telnet) -> Result<Response, String> {
    send_command(connection, "whoami", true).map(to_single_response)
}

pub fn clientmove(connection: &mut Telnet, cid: &i32, clid_list: Vec<&i32>) -> Result<Response, String> {
    let clid_param_list = clid_list
        .into_iter()
        .map(|clid| Parameter::new("clid", &clid.to_string()))
        .collect::<Vec<Parameter>>();
    let clid_str = Parameter::list_to_string_sep(clid_param_list, "|");
    send_command(connection, &format!("clientmove cid={} {}", cid, clid_str), false)
}

pub fn clientupdate(connection: &mut Telnet, parameters: ParameterList) -> Result<Response, String> {
    let parameters_str = Parameter::list_to_string(parameters);
    send_command(connection, &format!("clientupdate {}", parameters_str), false)
}

pub fn sendtextmessage(connection: &mut Telnet, target: SendTextMessageTarget, msg: String) -> Result<Response, String> {
    let msg = String::from(Parameter::new("msg", &msg));
    send_command(connection, &format!("sendtextmessage {} {}", msg, String::from(target)), false) }