export class ShopPresetRegistry {
    static #presets = new Map();

    static register(definition) {
        if (!definition?.id || !definition?.name) throw new Error("Shop presets require an id and name.");
        const merchandiseKinds = [...new Set((definition.merchandiseKinds || []).map(String).filter(Boolean))];
        const catalogRules = this.#normalizeCatalogRules(definition.catalogRules, merchandiseKinds);
        if (!merchandiseKinds.length && !catalogRules.default.includeTags.length) {
            throw new Error(`Shop preset ${definition.id} requires merchandise kinds or catalog tags.`);
        }
        const preset = {
            id: String(definition.id),
            moduleId: String(definition.moduleId || ""),
            name: String(definition.name),
            description: String(definition.description || ""),
            icon: String(definition.icon || "fas fa-store"),
            defaultImageId: String(definition.defaultImageId || "").trim(),
            defaultName: String(definition.defaultName || definition.name),
            themeId: String(definition.themeId || "default"),
            supportedSystems: [...new Set((definition.supportedSystems || []).map(String).filter(Boolean))],
            merchandiseKinds,
            coreKinds: [...new Set((definition.coreKinds || merchandiseKinds).map(String).filter(kind => merchandiseKinds.includes(kind)))],
            catalogRules,
            sizes: this.#normalizeSizes(definition.sizes),
            defaults: {
                size: ["modest", "standard", "extensive"].includes(definition.defaults?.size) ? definition.defaults.size : "standard",
                includeSpecial: definition.defaults?.includeSpecial === true,
                maximumLevel: Number.isFinite(Number(definition.defaults?.maximumLevel))
                    ? Math.max(0, Math.floor(Number(definition.defaults.maximumLevel))) : null
            }
        };
        this.#presets.set(preset.id, preset);
        return foundry.utils.deepClone(preset);
    }

    static get(id) {
        const preset = this.#presets.get(String(id || ""));
        return preset ? foundry.utils.deepClone(preset) : null;
    }

    static list({ systemId = game.system?.id || "" } = {}) {
        return Array.from(this.#presets.values())
            .filter(preset => !preset.supportedSystems.length || preset.supportedSystems.includes(systemId))
            .map(preset => foundry.utils.deepClone(preset))
            .sort((left, right) => left.name.localeCompare(right.name));
    }

    static #normalizeCatalogRules(rules = {}, fallbackKinds = []) {
        const normalize = source => ({
            merchandiseKinds: [...new Set((source?.merchandiseKinds || fallbackKinds).map(String).filter(Boolean))],
            includeTags: [...new Set((source?.includeTags || []).map(String).filter(Boolean))],
            excludeTags: [...new Set((source?.excludeTags || []).map(String).filter(Boolean))],
            activeTypes: [...new Set((source?.activeTypes || []).map(String).filter(Boolean))],
            filterState: foundry.utils.deepClone(source?.filterState || {})
        });
        const base = normalize(rules.default || {});
        return {
            default: base,
            systems: Object.fromEntries(Object.entries(rules.systems || {}).map(([systemId, source]) => [
                String(systemId),
                normalize({ ...base, ...source })
            ]))
        };
    }

    static #normalizeSizes(sizes = {}) {
        const defaults = {
            modest: { count: 8, quantity: { min: 1, max: 2 } },
            standard: { count: 12, quantity: { min: 1, max: 3 } },
            extensive: { count: 18, quantity: { min: 1, max: 4 } }
        };
        return Object.fromEntries(Object.entries(defaults).map(([id, fallback]) => {
            const source = sizes?.[id] || {};
            const count = Math.max(1, Math.floor(Number(source.count) || fallback.count));
            const minimum = Math.max(1, Math.floor(Number(source.quantity?.min) || fallback.quantity.min));
            const maximum = Math.max(minimum, Math.floor(Number(source.quantity?.max) || fallback.quantity.max));
            return [id, { count, quantity: { min: minimum, max: maximum } }];
        }));
    }
}
