import { ShopProductProviderRegistry } from "./ShopProductProviderRegistry.js";

export class ShopRestockService {
    static #warnedMissing = new Set();

    static async resolve(profile, { shop = null } = {}) {
        const entries = profile?.entries || [];
        const products = await Promise.all(entries.map(entry =>
            ShopProductProviderRegistry.resolve(entry.providerId, entry.productId, { shop, entry })
        ));
        const resolved = [];
        const missing = [];
        entries.forEach((entry, index) => {
            if (products[index]) resolved.push({ entry, product: products[index] });
            else missing.push(entry);
        });
        return { resolved, missing };
    }

    static async generate(profile, { shop = null, seed = foundry.utils.randomID(), warn = true } = {}) {
        const entries = (profile?.entries || []).filter(entry => entry.enabled !== false);
        const random = this.#createRandom(String(seed));
        const chosen = this.#weightedOrder(entries, random);
        const generation = profile?.generation || {};
        const minimum = Math.max(1, Math.floor(Number(generation.countMin) || 1));
        const maximum = Math.max(minimum, Math.floor(Number(generation.countMax) || minimum));
        const count = Math.min(chosen.length, minimum + Math.floor(random() * (maximum - minimum + 1)));
        const defaultQuantityMin = Math.max(1, Math.floor(Number(generation.quantityMin) || 1));
        const defaultQuantityMax = Math.max(defaultQuantityMin, Math.floor(Number(generation.quantityMax) || defaultQuantityMin));
        const inventory = [];
        const missing = [];

        for (const entry of chosen) {
            if (inventory.length >= count) break;
            const product = await ShopProductProviderRegistry.resolve(entry.providerId, entry.productId, { shop, entry });
            if (!product) {
                missing.push(entry);
                continue;
            }
            const quantityMin = entry.quantity?.min > 0 ? entry.quantity.min : defaultQuantityMin;
            const quantityMax = Math.max(quantityMin, entry.quantity?.max >= quantityMin ? entry.quantity.max : defaultQuantityMax);
            inventory.push({
                id: foundry.utils.randomID(),
                providerId: entry.providerId,
                productId: entry.productId,
                quantity: quantityMin + Math.floor(random() * (quantityMax - quantityMin + 1)),
                priceOverride: entry.priceOverride,
                pinned: false,
                generated: true
            });
        }
        if (warn && missing.length) this.#warnMissing(profile, missing);
        return { inventory, resolvedCount: inventory.length, missing };
    }

    static #weightedOrder(entries, random) {
        const weighted = entries.flatMap(entry => Array(Math.min(20, Math.max(1, entry.weight || 1))).fill(entry));
        const chosen = [];
        const used = new Set();
        while (weighted.length && chosen.length < entries.length) {
            const entry = weighted[Math.floor(random() * weighted.length)];
            const key = `${entry.providerId}::${entry.productId}`;
            if (!used.has(key)) {
                used.add(key);
                chosen.push(entry);
            }
            for (let index = weighted.length - 1; index >= 0; index--) {
                const row = weighted[index];
                if (`${row.providerId}::${row.productId}` === key) weighted.splice(index, 1);
            }
        }
        return chosen;
    }

    static #warnMissing(profile, missing) {
        const key = `${profile?.revision || 0}:${missing.map(entry => entry.sourceHint || entry.productId).sort().join("|")}`;
        if (this.#warnedMissing.has(key)) return;
        this.#warnedMissing.add(key);
        const sources = [...new Set(missing.map(entry => entry.sourceHint).filter(Boolean))];
        const suffix = sources.length ? ` Missing sources: ${sources.join(", ")}.` : "";
        ui.notifications.warn(`Restocking skipped ${missing.length} unavailable item reference${missing.length === 1 ? "" : "s"}.${suffix}`);
    }

    static #createRandom(seed) {
        let hash = 2166136261;
        for (const character of seed) { hash ^= character.charCodeAt(0); hash = Math.imul(hash, 16777619); }
        return () => {
            hash += 0x6D2B79F5;
            let value = hash;
            value = Math.imul(value ^ (value >>> 15), value | 1);
            value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
            return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
        };
    }
}
