const MODULE_NAME = 'SDK-SUPPLIER-PRODUCT-QUOTA.REDIS-UTIL';
const DEFAULT_TTL = 3600 * 24 * 90;
const LIMITED_SET_NAME = 'SPQ_8FB2B6EC_MANAGED';

const redis = require('redis');
const { orderBy } = require('natural-orderby');

const config = require('komodo-sdk/config');
const logger = require('tektrans-logger');

const redisClient = redis.createClient();

const composeKeyword = (product) => `SPQ_F7D526DB_COUNTER_${config.handler_name}_${product.trim().toUpperCase()}`;

const set = (product, val, xid) => new Promise((resolve) => {
    if (!(product || '').trim()) {
        resolve();
        return;
    }

    const keyword = composeKeyword(product);
    redisClient.set(keyword, Number(val), 'EX', DEFAULT_TTL, (err) => {
        if (err) {
            logger.warn(`F7D526DB ${MODULE_NAME}: Error on set value on redis`, {
                xid, product, val, eCode: err.code, eMessage: err.message,
            });
            resolve();
            return;
        }

        resolve(val);
    });
});

const get = (product, xid) => new Promise((resolve) => {
    if (!(product || '').trim()) {
        resolve();
        return;
    }

    const keyword = composeKeyword(product);

    redisClient.get(keyword, (err, reply) => {
        if (err) {
            logger.warn(`277CFDD4 ${MODULE_NAME}: Error on get value on redis`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            resolve(null);
            return;
        }

        if (!reply) {
            resolve();
            return;
        }

        resolve(Number(reply));
    });
});
exports.get = get;

const isLimited = (product, xid) => new Promise((resolve) => {
    const supplier = config.handler_name;
    const val = JSON.stringify({ supplier, product });

    redisClient.sismember(LIMITED_SET_NAME, val, (err, reply) => {
        if (err) {
            logger.warn(`20D042AC ${MODULE_NAME}: Error on checking if on managed set`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            resolve(null);
            return;
        }

        resolve(!!reply);
    });
});
exports.isLimited = isLimited;

const addToLimited = (product, xid) => new Promise((resolve) => {
    if (!(product || '').trim()) {
        resolve();
        return;
    }

    const supplier = config.handler_name;
    const val = JSON.stringify({ supplier, product: product.trim().toUpperCase() });

    redisClient.sadd(LIMITED_SET_NAME, val, (err, reply) => {
        if (err) {
            logger.warn(`E5AA0994 ${MODULE_NAME}: Error on add to managed set`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            resolve();
            return;
        }

        resolve(reply);
    });
});
exports.addToLimited = addToLimited;

const removeFromLimited = (product, xid) => new Promise((resolve) => {
    const supplier = config.handler_name;
    const val = JSON.stringify({ supplier, product });

    redisClient.srem(LIMITED_SET_NAME, val, (err, reply) => {
        if (err) {
            logger.warn(`DD5CBDF2 ${MODULE_NAME}: Error on remove from managed set`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            resolve();
            return;
        }

        resolve(reply);
    });
});
exports.removeFromLimited = removeFromLimited;

const setLimit = async (product, newValue, xid) => {
    await addToLimited(product, xid);
    await set(product, Number(newValue));
};
exports.setLimit = setLimit;

const decrement = (product, xid) => new Promise((resolve) => {
    const keyword = composeKeyword(product);

    redisClient.decr(keyword, (err, reply) => {
        if (err) {
            logger.warn(`BA176849 ${MODULE_NAME}: Error on decrementing value`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            resolve(null);
            return;
        }

        redisClient.expire(keyword, DEFAULT_TTL);

        resolve(reply);
    });
});

const increment = (product, xid) => new Promise((resolve) => {
    const keyword = composeKeyword(product);

    redisClient.incr(keyword, (err, reply) => {
        if (err) {
            logger.warn(`8DA99C28 ${MODULE_NAME}: Error on incrementing`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            redisClient.expire(keyword, DEFAULT_TTL);

            resolve(null);
            return;
        }

        resolve(reply);
    });
});

const incrementBy = (product, val, xid) => new Promise((resolve) => {
    const keyword = composeKeyword(product);

    redisClient.incrby(keyword, Math.trunc(Number(val)), (err, reply) => {
        if (err) {
            logger.warn(`83038699 ${MODULE_NAME}: Error on incrementing by`, {
                xid, product, eCode: err.code, eMessage: err.message,
            });

            redisClient.expire(keyword, DEFAULT_TTL);

            resolve(null);
            return;
        }

        resolve(reply);
    });
});
exports.incrementBy = incrementBy;

const dispose = async (product, xid) => {
    if (!await isLimited(product, xid)) {
        return true;
    }

    const remaining = await decrement(product, xid);
    if (!remaining && remaining !== 0) {
        return true;
    }

    if (remaining < 0) {
        await set(product, 0, xid);
        return 0;
    }

    return remaining + 1;
};
exports.dispose = dispose;

const putBack = async (product, xid) => {
    if (!await isLimited(product, xid)) {
        return;
    }

    await increment(product, xid);
};
exports.putBack = putBack;

const limitedList = (xid) => new Promise((resolve, reject) => {
    redisClient.smembers(LIMITED_SET_NAME, (err, reply) => {
        if (err) {
            const newError = new Error('Error getting limited list');
            newError.code = 'BA176849';
            logger.warn(`BA176849 ${MODULE_NAME}: ${newError.message}`, {
                xid, eCode: err.code, eMessage: err.message, newErrCode: newError.code,
            });

            reject(newError);
            return;
        }

        const result = (reply || [])
            .map((item) => JSON.parse(item));

        resolve(
            orderBy(
                result,
                [(item) => item.supplier, (item) => item.product],
                ['asc', 'asc'],
            ),
        );
    });
});
exports.limitedList = limitedList;

const setMaxValueForIndicator = (product, val, xid) => new Promise((resolve) => {
    if (!(product || '').trim()) {
        logger.warn(`${MODULE_NAME}: Skip saving max value for empty product`, {
            xid, product, val,
        });
        resolve();
        return;
    }

    const keyword = `${composeKeyword(product)}_MAX_VALUE`;
    logger.info(`${MODULE_NAME}: Saving max value`, {
        xid, product, val, keyword,
    });

    redisClient.set(keyword, Number(val), 'EX', DEFAULT_TTL, (err) => {
        if (err) {
            logger.warn(`9E4965F4 ${MODULE_NAME}: Error on set max value on redis`, {
                xid, product, val, eCode: err.code, eMessage: err.message,
            });
            resolve();
            return;
        }

        resolve(val);
    });
});
exports.setMaxValueForIndicator = setMaxValueForIndicator;

const getMaxValuesForIndicator = (products, xid) => new Promise((resolve, reject) => {
    const keywords = (products || []).map((item) => `${composeKeyword(item)}_MAX_VALUE`);

    if (!keywords || !keywords.length) {
        resolve([]);
        return;
    }

    redisClient.mget(...keywords, (err, reply) => {
        if (err) {
            logger.warn(`63BE7706 ${MODULE_NAME}: Error on getting max values on redis`, {
                xid, eCode: err.code, products, keywords, eMessage: err.message,
            });
            reject(err);
            return;
        }

        resolve(reply);
    });
});
exports.getMaxValuesForIndicator = getMaxValuesForIndicator;
