use crate::error::{Error, Result};
use reqwest::Url;
use serenity::{
  all::UserId,
  client::Cache,
  model::prelude::{ChannelId, GuildId},
};
use songbird::Songbird;
use std::sync::Arc;

pub mod mute;
pub mod pause;
pub mod play;
pub mod queue;
pub mod resume;
pub mod skip;
pub mod stop;
pub mod volume;

/**
 * Finds a voice channel that the user is currently in, and attempts to join it.
 */
pub async fn join_voice_channel(
  cache: &Arc<Cache>,
  manager: &Arc<Songbird>,
  guild_id: &GuildId,
  user_id: &UserId,
) -> Result<ChannelId> {
  let channel_id = find_voice_channel(cache, guild_id, user_id)?;
  log::debug!("<{}> Joining channel {}", guild_id.get(), channel_id.get());
  match manager
    .join(guild_id.to_owned(), channel_id.to_owned())
    .await
  {
    Ok(_) => Ok(channel_id),
    Err(e) => {
      if e.should_leave_server() || e.should_reconnect_driver() {
        log::debug!("<{}> Cleaning up failed voice connection", guild_id.get());
        let _ = manager.remove(*guild_id).await;
      }
      Err(e.into())
    }
  }
}

/**
 * Leaves a voice channel.
 */
pub async fn leave_voice_channel(manager: &Arc<Songbird>, guild_id: &GuildId) -> Result<()> {
  if manager.get(guild_id.to_owned()).is_some() {
    log::debug!("<{}> Disconnecting from channel", guild_id.get());
    manager.remove(*guild_id).await?;
  }
  Ok(())
}

/**
 * Validates whether the given string is a properly formatted URL.
 *
 * Returns `true` if the input string is a valid URL, otherwise `false`.
 */
fn is_valid_url(url: &str) -> bool {
  Url::parse(url).is_ok()
}

/**
 * Finds a voice channel that the user is currently in.
 */
fn find_voice_channel(
  cache: &Arc<Cache>,
  guild_id: &GuildId,
  user_id: &UserId,
) -> Result<ChannelId> {
  let guild = match guild_id.to_guild_cached(cache) {
    Some(g) => g,
    None => return Err(Error::new(404, "Guild not found".to_string())),
  };

  match guild
    .voice_states
    .get(user_id)
    .and_then(|voice_state| voice_state.channel_id)
  {
    Some(channel) => Ok(channel),
    None => Err(Error::new(
      400,
      "User is not in a voice channel".to_string(),
    )),
  }
}
