use super::*;
use api_swgoh_gg::{get_ships, get_units};
use models::unit::{NewUnit, Unit};
use schema::unit::dsl::unit;
use serenity::client::Context;
use serenity::framework::standard::CommandError;

trait UnitBaseId {
    fn base_id(&self) -> String;
}

impl UnitBaseId for api_swgoh_gg::model::Unit {
    fn base_id(&self) -> String {
        self.base_id.clone()
    }
}

impl UnitBaseId for api_swgoh_gg::model::Ship {
    fn base_id(&self) -> String {
        self.base_id.clone()
    }
}

#[command]
#[aliases("upd")]
#[description = "Update unit list"]
#[owners_only]
pub fn update(ctx: &mut Context, msg: &Message, _args: Args) -> CommandResult {
    let conn = establish_connection();
    let db_units: Vec<Unit> = unit.load(&conn)?;

    let api_units = get_units()?;
    let inserted_units = update_units(&conn, &db_units, api_units)?;
    send_embedded(ctx, msg, "units".to_string(), inserted_units)?;

    let api_units = get_ships()?;
    let inserted_units = update_units(&conn, &db_units, api_units)?;
    send_embedded(ctx, msg, "ships".to_string(), inserted_units)?;

    Ok(())
}

fn update_units<T: UnitBaseId>(
    conn: &MysqlConnection,
    db_units: &Vec<Unit>,
    api_units: Vec<T>,
) -> Result<Vec<NewUnit>, CommandError>
where
    NewUnit: std::convert::From<T>,
{
    let to_insert: Vec<NewUnit> = api_units
        .into_iter()
        .filter_map(
            |u| match db_units.iter().find(|dbu| dbu.base_id == u.base_id()) {
                None => Some(NewUnit::from(u)),
                Some(_) => None,
            },
        )
        .collect();

    debug!("To insert: {:?}", to_insert);
    diesel::insert_into(unit).values(&to_insert).execute(conn)?;

    Ok(to_insert)
}

fn send_embedded(
    ctx: &mut Context,
    msg: &Message,
    unit_type: String,
    inserted_units: Vec<NewUnit>,
) -> CommandResult {
    if !inserted_units.is_empty() {
        msg.channel_id.send_message(&ctx, |m| {
            m.embed(|e| {
                e.field(
                    format!("{} {} added", inserted_units.len(), unit_type),
                    inserted_units
                        .iter()
                        .map(|u| u.name.clone())
                        .collect::<Vec<String>>()
                        .join("\n"),
                    true,
                )
            })
        })?;
    } else {
        msg.channel_id
            .say(&ctx, format!("No {} to add!", unit_type))?;
    }

    Ok(())
}
