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

export class FactionEmblemRenderer {
    static #imageCache = new Map();
    static #pending = new Map();
    static #sourceImages = new Map();
    static #waiters = new Map();

    static getImage(entity = {}, fallback = "") {
        const presentation = FactionEmblemProviderRegistry.getPresentation(entity);
        if (!presentation) return String(fallback || "").trim();
        const cacheKey = `${presentation.providerId}:${presentation.cacheKey}`;
        const cached = this.#imageCache.get(cacheKey);
        if (cached) return cached;
        const entityId = String(entity?.id || "").trim();
        if (entityId) {
            const waiters = this.#waiters.get(cacheKey) || new Map();
            waiters.set(entityId, entity);
            this.#waiters.set(cacheKey, waiters);
        }
        if (!this.#pending.has(cacheKey)) {
            const pending = this.#render(presentation)
                .then(imageSrc => {
                    this.#imageCache.set(cacheKey, imageSrc);
                    for (const waitingEntity of this.#waiters.get(cacheKey)?.values() || []) {
                        Hooks.callAll("augurNexusCampaignEntitiesChanged", { reason: "factionEmblemRendered", entity: waitingEntity });
                    }
                    return imageSrc;
                })
                .catch(err => console.warn("augur-nexus | Failed to compose a faction emblem.", err))
                .finally(() => {
                    this.#pending.delete(cacheKey);
                    this.#waiters.delete(cacheKey);
                });
            this.#pending.set(cacheKey, pending);
        }
        return String(fallback || presentation.layers[0]?.src || "").trim();
    }

    static async getImageAsync(entity = {}, fallback = "") {
        const initial = this.getImage(entity, fallback);
        const presentation = FactionEmblemProviderRegistry.getPresentation(entity);
        if (!presentation) return initial;
        const cacheKey = `${presentation.providerId}:${presentation.cacheKey}`;
        const cached = this.#imageCache.get(cacheKey);
        if (cached) return cached;
        const rendered = await this.#pending.get(cacheKey);
        return rendered || initial;
    }

    static clearCache() {
        this.#imageCache.clear();
        this.#pending.clear();
        this.#waiters.clear();
    }

    static async #render(presentation) {
        const outputSize = 384;
        const canvas = document.createElement("canvas");
        canvas.width = outputSize;
        canvas.height = outputSize;
        const context = canvas.getContext("2d");
        if (!context) throw new Error("The browser could not create an emblem canvas.");
        const scaleX = outputSize / presentation.width;
        const scaleY = outputSize / presentation.height;
        for (const layer of presentation.layers) {
            const image = await this.#loadSource(layer.src);
            context.drawImage(
                image,
                layer.x * scaleX,
                layer.y * scaleY,
                layer.width * scaleX,
                layer.height * scaleY
            );
        }
        return canvas.toDataURL("image/webp", 0.92);
    }

    static #loadSource(src) {
        const path = String(src || "").trim();
        if (this.#sourceImages.has(path)) return this.#sourceImages.get(path);
        const pending = new Promise((resolve, reject) => {
            const image = new Image();
            image.onload = () => resolve(image);
            image.onerror = () => reject(new Error(`Could not load emblem layer ${path}.`));
            image.src = path;
        });
        this.#sourceImages.set(path, pending);
        return pending;
    }
}
