mod response;
mod utils;
mod commands;
mod parameter;
mod cli;

use std::process::exit;
use telnet::Telnet;
use crate::cli::Commands;
use crate::response::channel::ResponseChannel;
use crate::response::client::ResponseClient;

fn channel_or_exit(channel_res: Result<Option<ResponseChannel>, String>) -> ResponseChannel {
    channel_res.unwrap_or_else(|err| {
        println!("Failed to find channel: {}", err);
        exit(1);
    })
        .unwrap_or_else(|| {
            println!("Failed to find channel.");
            exit(1);
        })
}

fn client_or_exit(client_res: Result<Option<ResponseClient>, String>) -> ResponseClient {
    client_res.unwrap_or_else(|err| {
        println!("Failed to find client: {}", err);
        exit(1);
    })
        .unwrap_or_else(|| {
            println!("Failed to find client.");
            exit(1);
        })
}

fn main() {

    let cli = cli::init();

    let connection = Telnet::connect(("127.0.0.1", 25639), 512 * 1024);
    if connection.is_err() {
        println!("Failed to connect to Teamspeak.");
        exit(1);
    }
    let mut connection = connection.unwrap();

    utils::skip_welcome(&mut connection);
    utils::login(&mut connection);

    // You can check for the existence of subcommands, and if found use their
    // matches just as you would the top level cmd
    match &cli {
        Commands::Channels(args) => {
            match utils::get_channels(&mut connection, args.spacers) {
                Ok(channels) => {
                    for channel in channels {
                        println!("{}", channel.channel_name);
                    }
                }
                Err(msg) => {
                    println!("Failed to get channels: {}", msg);
                    exit(1);
                }
            }
        }

        Commands::Clients => {
            match utils::get_clients(&mut connection) {
                Ok(clients) => {
                    for client in clients {
                        println!("{}", client.client_nickname);
                    }
                }
                Err(msg) => {
                    println!("Failed to get clients: {}", msg);
                    exit(1);
                }
            }
        }

        Commands::Fetch(args) => {
            if args.want_client() && args.want_channel() {
                println!("Fetching both clients and channels is not supported.");
                exit(1);
            }
            if !args.want_client() && !args.want_channel() {
                println!("No clients or channels specified.");
                exit(1);
            }

            if args.want_client() {
                let client = client_or_exit(args.client(&mut connection));

                match utils::fetch_client(&mut connection, &[client]) {
                    Ok(_) => println!("Successfully fetched client."),
                    Err(msg) => {
                        println!("Failed to fetch client: {}", msg);
                        exit(1);
                    }
                }
            }

            if args.want_channel() {
                let channel = channel_or_exit(args.channel(&mut connection));

                match utils::fetch_channel(&mut connection, channel) {
                    Ok(_) => println!("Successfully fetched channel."),
                    Err(msg) => {
                        println!("Failed to fetch channel: {}", msg);
                        exit(1);
                    }
                }
            }
        }

        Commands::Move(args) => {
            let channel = channel_or_exit(args.channel(&mut connection));
            let client = client_or_exit(args.client(&mut connection));

            match utils::move_client(&mut connection, &channel, &[client]) {
                Ok(resp) => println!("Successfully moved client: {}", resp),
                Err(msg) => {
                    println!("Failed to move client: {}", msg);
                    exit(1);
                }
            }
        }

        Commands::Update(args) => {
            match utils::update_client(&mut connection, &args.to_parameter_list()) {
                Ok(_) => println!("Successfully updated client."),
                Err(msg) => {
                    println!("Failed to update client: {}", msg);
                    exit(1);
                }
            }
        }
    }
}