use super::super::prelude::*;

use rusqlite::params;

use rand::{seq::SliceRandom, thread_rng};

use serenity::prelude::*;
use serenity::voice::LockedAudio;
use serenity::{
    framework::standard::{macros::command, Args, CommandResult},
    model::channel::Message,
};

use utilities::prelude::*;

#[command]
fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    if let Err(err) = channel_contains_author(ctx, msg) {
        msg.channel_id.say(&ctx.http, err)?;
        return Ok(());
    }

    let mut data = ctx.data.write();
    let media = match data.get_mut::<Media>() {
        Some(media) => media,
        None => {
            msg.channel_id.say(&ctx.http, "could not find media data")?;
            return Ok(());
        }
    };

    if args.len() == 0 {
        if !check_for_continue(media.song_mut()) {
            msg.channel_id
                .say(&ctx.http, "Must provide a URL to a video or audio")?;
        }
    } else {
        let first_arg = args.current().unwrap();

        if first_arg == "--local" {
            handle_local_request(media, ctx, msg)?;
        } else if first_arg.starts_with("http") {
            handle_http_request(media, ctx, msg, first_arg)?;
        } else {
            let mut arg_list = args.single::<String>()?;

            for arg in args.iter::<String>() {
                arg_list += &format!(" {}", arg?.trim());
            }

            handle_song_request(media, ctx, msg, &arg_list)?
        }
    }

    Ok(())
}

fn check_for_continue(song_lock: &Option<LockedAudio>) -> bool {
    match song_lock {
        Some(song) => {
            let song_clone = song.clone();
            let mut audio_lock = song_clone.lock();

            audio_lock.play();
            true
        }
        None => false,
    }
}

fn append_songs(
    media: &mut MediaData,
    ctx: &Context,
    msg: &Message,
    mut source: Vec<Song>,
) -> VerboseResult<()> {
    media.playlist_mut().append(&mut source);

    println!("start playing");
    MediaData::start_playing(ctx, media, msg)?;

    Ok(())
}

fn handle_http_request(
    media: &mut MediaData,
    ctx: &serenity::client::Context,
    msg: &serenity::model::channel::Message,
    url: &str,
) -> VerboseResult<()> {
    let mut names = Vec::new();

    {
        let sql = media.db();

        let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") {
            Ok(statement) => statement,
            Err(_) => create_error!("failed preparing data base access"),
        };

        let rows = match stmt.query_map(&[url], |row| row.get(0) as rusqlite::Result<String>) {
            Ok(rows) => rows,
            Err(_) => create_error!("failed querying rows"),
        };

        for name_result in rows {
            let name = match name_result {
                Ok(name) => name,
                Err(_) => create_error!("failed getting name from row"),
            };

            names.push(name);
        }
    }

    if names.len() > 0 {
        msg.channel_id
            .say(&ctx.http, "song already loaded!")
            .map_err(|err| format!("{}", err))?;

        append_songs(
            media,
            ctx,
            msg,
            names.iter().map(|n| Song { name: n.clone() }).collect(),
        )?;
    } else {
        let source = match youtube_dl(&url) {
            Ok(source) => source,
            Err(why) => {
                println!("Err starting source: {:?}", why);

                msg.channel_id
                    .say(&ctx.http, format!("Error using youtube-dl: {}", why))
                    .map_err(|err| format!("{}", err))?;

                return Ok(());
            }
        };

        let mut info = if source.len() == 1 {
            "Finished downloading song:".to_string()
        } else {
            "Finished downloading songs:".to_string()
        };

        {
            let sql = media.db();

            for song in &source {
                info = format!("{}\n\t{}", info, song.name);

                if sql
                    .execute(
                        "INSERT INTO Vulva3 (name, link)
                    VALUES (?1, ?2)",
                        params![song.name, url],
                    )
                    .is_err()
                {
                    create_error!("failed inserting songs into db");
                }
            }
        }

        msg.channel_id
            .say(&ctx.http, info)
            .map_err(|err| format!("{}", err))?;

        append_songs(media, ctx, msg, source)?;
    }

    Ok(())
}

fn handle_local_request(
    media: &mut MediaData,
    ctx: &serenity::client::Context,
    msg: &serenity::model::channel::Message,
) -> VerboseResult<()> {
    let mut songs = Vec::new();

    {
        let sql = media.db();

        let mut stmt = match sql.prepare("SELECT name FROM Vulva3") {
            Ok(statement) => statement,
            Err(_) => create_error!("failed preparing data base access"),
        };

        let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) {
            Ok(rows) => rows,
            Err(_) => create_error!("failed querying rows"),
        };

        for name_result in rows {
            let name = match name_result {
                Ok(name) => name,
                Err(_) => create_error!("failed getting name from row"),
            };

            songs.push(Song { name });
        }
    }

    let mut rng = thread_rng();
    songs.shuffle(&mut rng);

    append_songs(media, ctx, msg, songs)?;

    Ok(())
}

fn handle_song_request(
    media: &mut MediaData,
    ctx: &serenity::client::Context,
    msg: &serenity::model::channel::Message,
    pattern: &str,
) -> VerboseResult<()> {
    println!("song request ({})", pattern);

    let mut songs = Vec::new();

    {
        let sql = media.db();

        let mut stmt = match sql.prepare(&format!(
            "SELECT name FROM Vulva3 WHERE name LIKE '%{}%'",
            pattern
        )) {
            Ok(statement) => statement,
            Err(_) => create_error!("failed preparing data base access"),
        };

        let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) {
            Ok(rows) => rows,
            Err(_) => create_error!("failed querying rows"),
        };

        for name_result in rows {
            let name = match name_result {
                Ok(name) => name,
                Err(_) => create_error!("failed getting name from row"),
            };

            songs.push(Song { name });
        }
    }

    println!("{:?}", &songs);

    if !songs.is_empty() {
        let mut rng = thread_rng();
        songs.shuffle(&mut rng);

        println!("append songs");

        append_songs(media, ctx, msg, songs)?;
    } else {
        msg.channel_id
            .say(&ctx.http, format!("no song found with pattern {}", pattern))
            .map_err(|err| format!("{}", err))?;
    }

    Ok(())
}