"use strict";
const crypto = require('crypto');

const ranges = [
    { minRange: 1, maxRange: 2, tag: "Code Card" },
    { minRange: 1, maxRange: 4, tag: "energyCategory" },
    { minRange: 1, maxRange: 8, tag: "Energy" },
    { minRange: 1, maxRange: 8, tag: "Energy , Holofoil" },
    { minRange: 1, maxRange: 66, tag: "Common" },
    { minRange: 1, maxRange: 66, tag: "Common" },
    { minRange: 1, maxRange: 66, tag: "Common" },
    { minRange: 1, maxRange: 66, tag: "Common" },
    { minRange: 1, maxRange: 62, tag: "Uncommon" },
    { minRange: 1, maxRange: 62, tag: "Uncommon" },
    { minRange: 1, maxRange: 62, tag: "Uncommon" },
    { minRange: 1, maxRange: 153, tag: "Reverse Holofoil" },
    { minRange: 1, maxRange: 10000, tag: "slot9" },
    { minRange: 1, maxRange: 153, tag: "Reverse Holofoil" },
    { minRange: 1, maxRange: 16, tag: "Illustration Rare" },
    { minRange: 1, maxRange: 7, tag: "Special Illustration Rare" },
    { minRange: 1, maxRange: 3, tag: "Hyper Rare" },
    { minRange: 1, maxRange: 10000, tag: "slot10" },
    { minRange: 1, maxRange: 25, tag: "Rare , Holofoil" },
    { minRange: 1, maxRange: 12, tag: "Double Rare" },
    { minRange: 1, maxRange: 16, tag: "Ultra Rare" },
    { minRange: 1, maxRange: 1000, tag: "godPackCategory" },
    { minRange: 1, maxRange: 3, tag: "godContentsCategory" },
];

function incrementNonce(nonce, offset) {
    let seconds = parseInt(nonce.slice(0, 2), 10);
    let minutes = parseInt(nonce.slice(2, 4), 10);
    let hours = parseInt(nonce.slice(4, 6), 10);
    let day = parseInt(nonce.slice(6, 8), 10);
    let month = parseInt(nonce.slice(8, 10), 10);
    let year = parseInt(nonce.slice(10, 14), 10);
    seconds += offset;
    while (seconds > 59) {
        seconds -= 60;
        minutes += 1;
        if (minutes > 59) {
            minutes = 0;
            hours += 1;
            if (hours > 23) {
                hours = 0;
                day += 1;
                const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
                if (day > daysInMonth[month - 1]) {
                    day = 1;
                    month += 1;
                    if (month > 12) {
                        month = 1;
                        year += 1;
                    }
                }
            }
        }
    }
    return (
        seconds.toString().padStart(2, '0') +
        minutes.toString().padStart(2, '0') +
        hours.toString().padStart(2, '0') +
        day.toString().padStart(2, '0') +
        month.toString().padStart(2, '0') +
        year.toString().padStart(4, '0')
    );
}

function generateHmacSha512(key, message) {
    const hmac = crypto.createHmac('sha512', key);
    hmac.update(message);
    return hmac.digest();
}

function generateRandomNumbers(ranges, serverSeed, clientSeed, nonce) {
    const randomNumbers = [];
    const numbersPerDigest = 16;
    const digestsNeeded = Math.ceil(ranges.length / numbersPerDigest);
    for (let digestIndex = 0; digestIndex < digestsNeeded; digestIndex++) {
        const hmac = crypto.createHmac('sha512', serverSeed);
        hmac.update(`${clientSeed}:${nonce}:${digestIndex}`);
        const digest = hmac.digest();
        let offset = 0;
        for (let i = 0; i < numbersPerDigest && randomNumbers.length < ranges.length; i++) {
            const range = ranges[randomNumbers.length];
            const bytes = digest.slice(offset, offset + 4);
            const num = bytes.readUInt32BE(0);
            const rand = Math.floor((num / 0x100000000) * (range.maxRange - range.minRange + 1)) + range.minRange;
            randomNumbers.push(rand);
            offset += 4;
        }
    }
    return randomNumbers;
}

function mapTaggedResults(randomNumbers, ranges, serverSeed, clientSeed, nonce) {
    const seenValues = {};
    const taggedResults = [];
    randomNumbers.forEach((num, index) => {
        if (!ranges[index]) {
            console.error(`No range found for index ${index}`);
            return;
        }
        const { tag = "UNKNOWN", minRange, maxRange } = ranges[index];
        let finalNumber = num;
        let nonceOffset = 0;
        if (!seenValues[tag]) {
            seenValues[tag] = new Set();
        }
        while (seenValues[tag].has(finalNumber.toString())) {
            nonceOffset++;
            const newNonce = incrementNonce(nonce, nonceOffset);
            const hmac = crypto.createHmac('sha512', serverSeed);
            hmac.update(`${clientSeed}:${newNonce}`);
            const digest = hmac.digest();
            const bytes = digest.slice(0, 4);
            const newNum = bytes.readUInt32BE(0);
            finalNumber = Math.floor((newNum / 0x100000000) * (maxRange - minRange + 1)) + minRange;
        }
        seenValues[tag].add(finalNumber.toString());
        taggedResults.push({ value: finalNumber.toString(), tag });
    });
    return taggedResults;
}

function filterResults(taggedResults, ranges) {
    let filteredResults = [];
    const usedValuesByTag = {};

    function getNextCard(tag) {
        if (!usedValuesByTag[tag]) {
            usedValuesByTag[tag] = new Set();
        }
        const cards = taggedResults.filter(item => item.tag === tag);
        for (const card of cards) {
            if (!usedValuesByTag[tag].has(card.value)) {
                usedValuesByTag[tag].add(card.value);
                return card;
            }
        }
        return null;
    }

    const codeCard = taggedResults.find((item) => item.tag === "Code Card");
    if (codeCard) filteredResults.push(codeCard);

    const energyCategoryItem = taggedResults.find((item) => item.tag === "energyCategory");
    if (energyCategoryItem) {
        const energyCategoryNum = parseInt(energyCategoryItem.value, 10);
        if (energyCategoryNum >= 1 && energyCategoryNum <= 3) {
            const basicEnergy = taggedResults.find((item) => item.tag === "Energy");
            if (basicEnergy) filteredResults.push(basicEnergy);
        } else if (energyCategoryNum === 4) {
            const holofoilEnergy = taggedResults.find((item) => item.tag === "Energy , Holofoil");
            if (holofoilEnergy) filteredResults.push(holofoilEnergy);
        }
    }

    const commonCards = taggedResults.filter((item) => item.tag === "Common");
    filteredResults.push(...commonCards.slice(0, 4));

    const uncommonCards = taggedResults.filter((item) => item.tag === "Uncommon");
    filteredResults.push(...uncommonCards.slice(0, 3));

    const reverseHolo = getNextCard("Reverse Holofoil");
    if (reverseHolo) filteredResults.push(reverseHolo);

    const reverseHoloCategoryItem2 = taggedResults.find((item) => item.tag === "slot9");
    if (reverseHoloCategoryItem2) {
        const reverseHoloCategoryNum = parseInt(reverseHoloCategoryItem2.value, 10);
        if (reverseHoloCategoryNum >= 1 && reverseHoloCategoryNum <= 8658) {
            const commonReverseHolo = getNextCard("Reverse Holofoil");
            if (commonReverseHolo) filteredResults.push(commonReverseHolo);
        } else if (reverseHoloCategoryNum >= 8659 && reverseHoloCategoryNum <= 9491) {
            const rareCard = taggedResults.find((item) => item.tag === "Illustration Rare");
            if (rareCard) filteredResults.push(rareCard);
        } else if (reverseHoloCategoryNum >= 9492 && reverseHoloCategoryNum <= 9804) {
            const rareCard = taggedResults.find((item) => item.tag === "Special Illustration Rare");
            if (rareCard) filteredResults.push(rareCard);
        } else if (reverseHoloCategoryNum >= 9805 && reverseHoloCategoryNum <= 10000) {
            const rareCard = taggedResults.find((item) => item.tag === "Hyper Rare");
            if (rareCard) filteredResults.push(rareCard);
        }
    }

    const rareCategoryItem = taggedResults.find((item) => item.tag === "slot10");
    if (rareCategoryItem) {
        const rareCategoryNum = parseInt(rareCategoryItem.value, 10);
        if (rareCategoryNum >= 1 && rareCategoryNum <= 8125) {
            const rareCard = taggedResults.find((item) => item.tag === "Rare , Holofoil");
            if (rareCard) filteredResults.push(rareCard);
        } else if (rareCategoryNum >= 8126 && reverseHoloCategoryNum <= 9375) {
            const rareCard = taggedResults.find((item) => item.tag === "Double Rare");
            if (rareCard) filteredResults.push(rareCard);
        } else if (rareCategoryNum >= 9376 && reverseHoloCategoryNum <= 10000) {
            const rareCard = taggedResults.find((item) => item.tag === "Ultra Rare");
            if (rareCard) filteredResults.push(rareCard);
        }
    }

    const godPackCategoryItem = taggedResults.find((item) => item.tag === "godPackCategory");
    const isGodPack = godPackCategoryItem && parseInt(godPackCategoryItem.value, 10) === 1000;

    if (isGodPack) {
        const godContentsItem = taggedResults.find((item) => item.tag === "godContentsCategory");
        const godContentsNum = godContentsItem ? parseInt(godContentsItem.value, 10) : 0;

        let lastThreeCards = [];
        if (godContentsNum === 1) {
            lastThreeCards = [
                { value: "3", tag: "Illustration Rare" },
                { value: "4", tag: "Illustration Rare" },
                { value: "2", tag: "Special Illustration Rare" }
            ];
        } else if (godContentsNum === 2) {
            lastThreeCards = [
                { value: "5", tag: "Illustration Rare" },
                { value: "6", tag: "Illustration Rare" },
                { value: "3", tag: "Special Illustration Rare" }
            ];
        } else if (godContentsNum === 3) {
            lastThreeCards = [
                { value: "1", tag: "Illustration Rare" },
                { value: "2", tag: "Illustration Rare" },
                { value: "1", tag: "Special Illustration Rare" }
            ];
        }

        if (lastThreeCards.length > 0) {
            filteredResults = filteredResults.slice(0, Math.max(0, filteredResults.length - 3));
            filteredResults.push(...lastThreeCards);
        }
    }

    return filteredResults;

}

async function setData(serverSeed, clientSeed, nonce) {
    const randomNumbers = generateRandomNumbers(ranges, serverSeed, clientSeed, nonce);
    const taggedResults = mapTaggedResults(randomNumbers, ranges, serverSeed, clientSeed, nonce);
    const filteredResults = filterResults(taggedResults, ranges);
    return {
        success: true,
        result: filteredResults,
        nonce,
        serverSeed,
        clientSeed
    };
}

export { setData, ranges };