'use strict';

const util = require('util');

const db = require('../database');
const plugins = require('../plugins');

const rewards = module.exports;

rewards.checkConditionAndRewardUser = async function (params) {
	const { uid, condition, method } = params;
	const isActive = await isConditionActive(condition);
	if (!isActive) {
		return;
	}
	const ids = await getIDsByCondition(condition);
	let rewardData = await getRewardDataByIDs(ids);
	rewardData = await filterCompletedRewards(uid, rewardData);
	rewardData = rewardData.filter(Boolean);
	if (!rewardData || !rewardData.length) {
		return;
	}
	const eligible = await Promise.all(rewardData.map(reward => checkCondition(reward, method)));
	const eligibleRewards = rewardData.filter((reward, index) => eligible[index]);
	await giveRewards(uid, eligibleRewards);
};

async function isConditionActive(condition) {
	return await db.isSetMember('conditions:active', condition);
}

async function getIDsByCondition(condition) {
	return await db.getSetMembers(`condition:${condition}:rewards`);
}

async function filterCompletedRewards(uid, rewards) {
	const data = await db.getSortedSetRangeByScoreWithScores(`uid:${uid}:rewards`, 0, -1, 1, '+inf');
	const userRewards = {};

	data.forEach((obj) => {
		userRewards[obj.value] = parseInt(obj.score, 10);
	});

	return rewards.filter((reward) => {
		if (!reward) {
			return false;
		}

		const claimable = parseInt(reward.claimable, 10);
		return claimable === 0 || (!userRewards[reward.id] || userRewards[reward.id] < reward.claimable);
	});
}

async function getRewardDataByIDs(ids) {
	return await db.getObjects(ids.map(id => `rewards:id:${id}`));
}

async function getRewardsByRewardData(rewards) {
	return await db.getObjects(rewards.map(reward => `rewards:id:${reward.id}:rewards`));
}

async function checkCondition(reward, method) {
	if (method.constructor && method.constructor.name !== 'AsyncFunction') {
		method = util.promisify(method);
	}
	const value = await method();
	const bool = await plugins.hooks.fire(`filter:rewards.checkConditional:${reward.conditional}`, { left: value, right: reward.value });
	return bool;
}

async function giveRewards(uid, rewards) {
	const rewardData = await getRewardsByRewardData(rewards);
	for (let i = 0; i < rewards.length; i++) {
		/* eslint-disable no-await-in-loop */
		await plugins.hooks.fire(`action:rewards.award:${rewards[i].rid}`, { uid: uid, reward: rewardData[i] });
		await db.sortedSetIncrBy(`uid:${uid}:rewards`, 1, rewards[i].id);
	}
}

require('../promisify')(rewards);
