const ranges = [
    { minRange: 1, maxRange: 117, tag: "T , Foil" },
    { minRange: 1, maxRange: 130, tag: "C , Foil" },
    { minRange: 1, maxRange: 130, tag: "C , Foil" },
    { minRange: 1, maxRange: 130, tag: "C , Foil" },
    { minRange: 1, maxRange: 130, tag: "C , Foil" },
    { minRange: 1, maxRange: 136, tag: "U , Foil" },
    { minRange: 1, maxRange: 136, tag: "U , Foil" },
    { minRange: 1, maxRange: 15, tag: "L , Foil" },
    { minRange: 1, maxRange: 100, tag: "Slot9" },
    { minRange: 1, maxRange: 12, tag: "C Borderless , Normal" },
    { minRange: 1, maxRange: 12, tag: "U Borderless , Normal" },
    { minRange: 1, maxRange: 5, tag: "U Profile , Normal" },
    { minRange: 1, maxRange: 100, tag: "Slot10" },
    { minRange: 1, maxRange: 12, tag: "C Borderless , Normal" },
    { minRange: 1, maxRange: 12, tag: "U Borderless , Normal" },
    { minRange: 1, maxRange: 5, tag: "U Profile , Normal" },
    { minRange: 1, maxRange: 100, tag: "Slot11" },
    { minRange: 1, maxRange: 12, tag: "C Borderless , Foil" },
    { minRange: 1, maxRange: 12, tag: "U Borderless , Foil" },
    { minRange: 1, maxRange: 5, tag: "U Profile , Foil" },
    { minRange: 1, maxRange: 100, tag: "Slot12" },
    { minRange: 1, maxRange: 129, tag: "R Etched , Foil" },
    { minRange: 1, maxRange: 38, tag: "M Etched , Foil" },
    { minRange: 1, maxRange: 100, tag: "Slot13" },
    { minRange: 1, maxRange: 129, tag: "R Etched , Foil" },
    { minRange: 1, maxRange: 38, tag: "M Etched , Foil" },
    { minRange: 1, maxRange: 10, tag: "Slot14" },
    { minRange: 1, maxRange: 34, tag: "Extended Art , Normal" },
    { minRange: 1, maxRange: 34, tag: "Extended Art , Foil" },
    { minRange: 1, maxRange: 100, tag: "Slot15" },
    { minRange: 1, maxRange: 19, tag: "R Borderless , Normal" },
    { minRange: 1, maxRange: 3, tag: "M Borderless , Normal" },
    { minRange: 1, maxRange: 9, tag: "R Profile , Normal" },
    { minRange: 1, maxRange: 9, tag: "M Profile , Normal" },
    { minRange: 1, maxRange: 11, tag: "Frame , Normal" },
    { minRange: 1, maxRange: 100, tag: "Slot16" },
    { minRange: 1, maxRange: 19, tag: "R Borderless , Foil" },
    { minRange: 1, maxRange: 3, tag: "M Borderless , Foil" },
    { minRange: 1, maxRange: 9, tag: "R Profile , Foil" },
    { minRange: 1, maxRange: 9, tag: "M Profile , Foil" },
    { minRange: 1, maxRange: 11, tag: "Frame , Foil" },
    { minRange: 1, maxRange: 10, tag: "Texture , Foil" },
];

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')
    );
}

async function generateHmacSha512(key, message) {
    const keyBuffer = new TextEncoder().encode(key);
    const msgBuffer = new TextEncoder().encode(message);
    const cryptoKey = await crypto.subtle.importKey(
        "raw",
        keyBuffer,
        { name: "HMAC", hash: "SHA-512" },
        false,
        ["sign"]
    );
    const signature = await crypto.subtle.sign("HMAC", cryptoKey, msgBuffer);
    return new Uint8Array(signature);
}

async 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 message = `${clientSeed}:${nonce}:${digestIndex}`;
        const digest = await generateHmacSha512(serverSeed, message);
        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 view = new DataView(bytes.buffer, bytes.byteOffset, 4);
            const num = view.getUint32(0, false); // Big-endian
            const rand = Math.floor((num / 0x100000000) * (range.maxRange - range.minRange + 1)) + range.minRange;
            randomNumbers.push(rand);
            offset += 4;
        }
    }

    return randomNumbers;
}

async function mapTaggedResults(randomNumbers, ranges, serverSeed, clientSeed, nonce) {
    const seenValues = {};
    const taggedResults = [];

    for (let index = 0; index < randomNumbers.length; index++) {
        if (!ranges[index]) continue;
        const { tag = "UNKNOWN", minRange, maxRange } = ranges[index];
        let finalNumber = randomNumbers[index];
        let nonceOffset = 0;

        if (!seenValues[tag]) seenValues[tag] = new Set();

        while (seenValues[tag].has(finalNumber.toString())) {
            nonceOffset++;
            const newNonce = incrementNonce(nonce, nonceOffset);
            const message = `${clientSeed}:${newNonce}`;
            const digest = await generateHmacSha512(serverSeed, message);
            const bytes = digest.slice(0, 4);
            const view = new DataView(bytes.buffer, bytes.byteOffset, 4);
            finalNumber = Math.floor((view.getUint32(0, false) / 0x100000000) * (maxRange - minRange + 1)) + minRange;
        }

        seenValues[tag].add(finalNumber.toString());
        taggedResults.push({ value: finalNumber.toString(), tag });
    }

    return taggedResults;
}

async function filterResults(taggedResults, ranges, serverSeed, clientSeed, nonce) {
    let filteredResults = [];
    let nonceOffset = 0;
    const usedIndexes = new Set();

    const add = (item) => {
        if (item) {
            filteredResults.push(item);
            usedIndexes.add(taggedResults.indexOf(item));
        }
    };

    const findAndConsume = (tag) => {
        const item = taggedResults.find((item, i) => item.tag === tag && !usedIndexes.has(i));
        if (item) usedIndexes.add(taggedResults.indexOf(item));
        return item;
    };

    // Top part
    let tFrameFoil = taggedResults.filter((item, i) => item.tag === "T , Foil" && !usedIndexes.has(i));
    tFrameFoil.slice(0, 1).forEach(add);

    let cFoil = taggedResults.filter((item, i) => item.tag === "C , Foil" && !usedIndexes.has(i));
    cFoil.slice(0, 4).forEach(add);

    let uFoil = taggedResults.filter((item, i) => item.tag === "U , Foil" && !usedIndexes.has(i));
    uFoil.slice(0, 2).forEach(add);

    let lFoil = taggedResults.filter((item, i) => item.tag === "L , Foil" && !usedIndexes.has(i));
    lFoil.slice(0, 1).forEach(add);

    // Fix starts here — Declare slot9Item + slot9Tag outside
    const slot9 = taggedResults.find(item => item.tag === "Slot9");
    let slot9Item = null;
    let slot9Tag = null;

    if (slot9) {
        const num = parseInt(slot9.value, 10);
        if (num <= 67) {
            slot9Tag = "C Borderless , Normal";
            slot9Item = findAndConsume(slot9Tag);
        } else if (num <= 87) {
            slot9Tag = "U Borderless , Normal";
            slot9Item = findAndConsume(slot9Tag);
        } else if (num <= 100) {
            slot9Tag = "U Profile , Normal";
            slot9Item = findAndConsume(slot9Tag);
        }
        add(slot9Item);
    }

    const slot10 = taggedResults.find(item => item.tag === "Slot10");
    if (slot10) {
        const num = parseInt(slot10.value, 10);
        let slot10Item = null;
        let slot10Tag = null;
        if (num <= 67) {
            slot10Tag = "C Borderless , Normal";
            slot10Item = findAndConsume(slot10Tag);
        } else if (num <= 87) {
            slot10Tag = "U Borderless , Normal";
            slot10Item = findAndConsume(slot10Tag);
        } else if (num <= 100) {
            slot10Tag = "U Profile , Normal";
            slot10Item = findAndConsume(slot10Tag);
        }

        if (slot9Item && slot10Item && slot9Tag === slot10Tag && slot9Item.value === slot10Item.value) {
            const rangeForTag = ranges.find(range => range.tag === slot10Tag);
            if (rangeForTag) {
                const { minRange, maxRange } = rangeForTag;
                let newValue;
                do {
                    nonceOffset++;
                    const newNonce = incrementNonce(nonce, nonceOffset);
                    const message = `${clientSeed}:${newNonce}`;
                    const digest = await generateHmacSha512(serverSeed, message);
                    const bytes = digest.slice(0, 4);
                    const view = new DataView(bytes.buffer, bytes.byteOffset, 4);
                    newValue = Math.floor((view.getUint32(0, false) / 0x100000000) * (maxRange - minRange + 1)) + minRange;
                } while (newValue.toString() === slot9Item.value);
                slot10Item = { value: newValue.toString(), tag: slot10Tag };
            }
        }
        add(slot10Item);
    }

    // Slot11–16
    const slot11 = taggedResults.find(item => item.tag === "Slot11");
    if (slot11) {
        const num = parseInt(slot11.value, 10);
        if (num <= 67) add(findAndConsume("C Borderless , Foil"));
        else if (num <= 87) add(findAndConsume("U Borderless , Foil"));
        else add(findAndConsume("U Profile , Foil"));
    }

    const slot12 = taggedResults.find(item => item.tag === "Slot12");
    if (slot12) {
        const num = parseInt(slot12.value, 10);
        if (num <= 89) add(findAndConsume("R Etched , Foil"));
        else add(findAndConsume("M Etched , Foil"));
    }

    const slot13 = taggedResults.find(item => item.tag === "Slot13");
    if (slot13) {
        const num = parseInt(slot13.value, 10);
        if (num <= 89) add(findAndConsume("R Etched , Foil"));
        else add(findAndConsume("M Etched , Foil"));
    }

    const slot14 = taggedResults.find(item => item.tag === "Slot14");
    if (slot14) {
        const num = parseInt(slot14.value, 10);
        if (num <= 8) add(findAndConsume("Extended Art , Normal"));
        else add(findAndConsume("Extended Art , Foil"));
    }

    const slot15 = taggedResults.find(item => item.tag === "Slot15");
    if (slot15) {
        const num = parseInt(slot15.value, 10);
        if (num <= 42) add(findAndConsume("R Borderless , Normal"));
        else if (num <= 46) add(findAndConsume("M Borderless , Normal"));
        else if (num <= 72) add(findAndConsume("R Profile , Normal"));
        else if (num <= 83) add(findAndConsume("M Profile , Normal"));
        else add(findAndConsume("Frame , Normal"));
    }

    const slot16 = taggedResults.find(item => item.tag === "Slot16");
    if (slot16) {
        const num = parseInt(slot16.value, 10);
        if (num <= 40) add(findAndConsume("R Borderless , Foil"));
        else if (num <= 43) add(findAndConsume("M Borderless , Foil"));
        else if (num <= 68) add(findAndConsume("R Profile , Foil"));
        else if (num <= 78) add(findAndConsume("M Profile , Foil"));
        else if (num <= 96) add(findAndConsume("Frame , Foil"));
        else add(findAndConsume("Texture , Foil"));
    }

    return filteredResults;
}

async function setData(serverSeed, clientSeed, nonce) {
    const randomNumbers = await generateRandomNumbers(ranges, serverSeed, clientSeed, nonce);
    const taggedResults = await mapTaggedResults(randomNumbers, ranges, serverSeed, clientSeed, nonce);
    const filteredResults = await filterResults(taggedResults, ranges, serverSeed, clientSeed, nonce);

    return {
        success: true,
        result: filteredResults,
        nonce,
        serverSeed,
        clientSeed
    };
}

export { setData, ranges};
