use super::*;
use models::{farm_entry::FarmEntry, user_farm::UserFarm};
use serenity::framework::standard::CommandResult;
use serenity::{
    framework::standard::{macros::command, CommandError},
    model::{
        guild::Member,
        id::{GuildId, RoleId},
        user::User,
    },
};

#[command]
#[aliases("lst")]
#[description = "List current farmed units"]
#[usage = "[-cf]\n    -c        # print in compact view\n    -f DATE   # set from date for query"]
#[example = "-f 2019-05-01"]
#[only_in("guilds")]
pub fn list(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
    let compact = args.message().contains("-c");

    let users = &msg.mentions;

    let roles = &msg.mention_roles;
    //    TODO refactor this to be user in dm (without gid)
    let gid: GuildId = match msg.guild_id {
        Some(g) => g,
        None => {
            return Err(CommandError(
                "This command must be executed in a guild channel".to_string(),
            ));
        }
    };

    let date = get_from_date(args.clone())?;

    if users.is_empty() && roles.is_empty() {
        show_user_farm(ctx, &[msg.author.clone()], date, msg, !compact)?;
        return Ok(());
    }

    if !roles.is_empty() {
        find_for_roles(ctx, roles, gid, date, msg, false)?;
    }

    debug!("{}", date);

    show_user_farm(ctx, users, date, msg, false)?;

    Ok(())
}

fn find_for_roles(
    ctx: &mut Context,
    roles: &[RoleId],
    gid: GuildId,
    date: DateTime<Utc>,
    msg: &Message,
    detailed: bool,
) -> CommandResult {
    let members: Vec<Member> = gid.members(&ctx, Some(1000), None)?;
    debug!("members: {:?}", members);

    for role in roles {
        let has_role: Vec<User> = members
            .iter()
            .filter(|m| m.user.read().has_role(&ctx, gid, role).unwrap())
            .map(|m| m.user_id().to_user(&ctx).unwrap())
            .collect();

        debug!("role: {}, users: {:?}", role, has_role);

        show_user_farm(ctx, &has_role, date, msg, detailed)?;
    }

    Ok(())
}

fn show_user_farm(
    ctx: &mut Context,
    users: &[User],
    date: DateTime<Utc>,
    msg: &Message,
    detailed: bool,
) -> CommandResult {
    use schema::farm_entry::dsl::date as dsl_date;
    use schema::unit::dsl::unit as unit_dsl;
    use schema::user_farm::dsl::stars_finished as stars_finished_dsl;

    let conn = establish_connection();

    let gid = msg.guild_id.unwrap();

    for discord_user in users {
        let username = discord_user
            .nick_in(&ctx, gid)
            .unwrap_or_else(|| discord_user.name.clone())
            .replace("[", "{")
            .replace("]", "}");

        let mut farming_msg = format!(
            "{}\n{}\n\n",
            username,
            std::iter::repeat("=")
                .take(username.len())
                .collect::<String>()
        );

        let user = match get_user_from_discord_user(discord_user, &conn) {
            Ok(u) => u,
            Err(_) => {
                msg.channel_id
                    .say(
                        &ctx,
                        format!("```asciidoc\n{}Not registered!!!\n```", farming_msg),
                    )
                    .map_err(|e| CommandError(e.to_string()))?;
                continue;
            }
        };
        debug!("user: {:?}", user);

        let farms: Vec<UserFarm> = UserFarm::belonging_to(&user)
            .filter(stars_finished_dsl.eq(false))
            .load(&conn)?;

        if farms.is_empty() {
            farming_msg += "No farm";
        }

        for farm in farms {
            let farmed_unit: Unit = unit_dsl.find(farm.unit_id).first(&conn)?;

            farming_msg += &format!("# {}", farmed_unit.name);

            if let Some(s) = farm.stars_goal {
                let rd = match farm.get_remaining_days() {
                    None => "Unknown".to_string(),
                    Some(d) => d.to_string(),
                };
                farming_msg += &format!(", stars goal => {}, remaining days => {}", s, rd);
            }

            if let Some(g) = farm.gears_goal {
                farming_msg += &format!(", gear => {}", g);
            }
            farming_msg += "\n";

            let entries: Vec<FarmEntry> = FarmEntry::belonging_to(&farm)
                .filter(dsl_date.ge(date.naive_utc()))
                .order(dsl_date.asc())
                .load(&conn)?;

            if entries.is_empty() {
                farming_msg += &format!(
                    "No entry for specified dates. From {} To {}\n",
                    date.date(),
                    Utc::now().date()
                );
            }

            for (i, entry) in entries.iter().enumerate() {
                debug!("{:?}", entry);

                if detailed {
                    let endl = if i != (entries.len() - 1) { "\n" } else { "" };
                    farming_msg += &format!("{}{}", entry.summary(), endl);
                } else {
                    farming_msg += &entry.compact_summary().to_string();
                };
            }
            farming_msg += "\n";

            if 1800 <= farming_msg.len() {
                msg.channel_id
                    .say(&ctx, format!("```asciidoc\n{}\n```", farming_msg))
                    .map_err(|e| CommandError(e.to_string()))?;
                farming_msg = String::new();
            }
        }

        msg.channel_id
            .say(&ctx, format!("```asciidoc\n{}\n```", farming_msg))
            .map_err(|e| CommandError(e.to_string()))?;
    }

    Ok(())
}
