// retoor <retoor@molodetz.nl>

import { OptimisticAction } from "./OptimisticAction.js";
import { EmojiPickerElement } from "./EmojiPickerElement.js";

export class ReactionBar extends OptimisticAction {
    constructor() {
        super();
        document.addEventListener("click", (event) => this.onClick(event));
    }

    async onClick(event) {
        if (event.target.closest(".reaction-picker")) {
            return;
        }
        const addButton = event.target.closest(".reaction-add-btn");
        if (addButton) {
            event.preventDefault();
            this.togglePalette(addButton);
            return;
        }
        const moreButton = event.target.closest(".reaction-palette-more");
        if (moreButton) {
            event.preventDefault();
            this.togglePicker(moreButton);
            return;
        }
        const trigger = event.target.closest("[data-reaction-emoji]");
        if (trigger && !trigger.disabled) {
            event.preventDefault();
            await this.react(trigger);
            return;
        }
        this.closeAll();
    }

    togglePalette(addButton) {
        const palette = addButton.parentElement.querySelector(".reaction-palette");
        const isOpen = palette.hidden === false;
        this.closeAll();
        palette.hidden = isOpen;
        addButton.setAttribute("aria-expanded", palette.hidden ? "false" : "true");
        if (!palette.hidden) {
            this.clampToViewport(palette);
            this.ensureInView(palette);
        }
    }

    togglePicker(moreButton) {
        const bar = moreButton.closest(".reaction-bar");
        const host = bar ? bar.querySelector(".reaction-picker") : null;
        if (!host) {
            return;
        }
        const isOpen = host.hidden === false;
        this.closeAll();
        if (isOpen) {
            return;
        }
        this.mountPicker(host, bar);
        host.hidden = false;
        moreButton.setAttribute("aria-expanded", "true");
        this.clampToViewport(host);
        this.flipIfClipped(host);
        this.ensureInView(host);
    }

    mountPicker(host, bar) {
        if (host.querySelector("emoji-picker")) {
            return;
        }
        EmojiPickerElement.load();
        host.appendChild(EmojiPickerElement.create((unicode) => this.reactWith(bar, unicode)));
    }

    clampToViewport(panel) {
        const margin = 8;
        panel.style.left = "";
        const rect = panel.getBoundingClientRect();
        const overflowRight = rect.right - (window.innerWidth - margin);
        if (overflowRight > 0) {
            panel.style.left = `${-overflowRight}px`;
        }
        const adjusted = panel.getBoundingClientRect();
        if (adjusted.left < margin) {
            const current = parseFloat(panel.style.left || "0");
            panel.style.left = `${current + (margin - adjusted.left)}px`;
        }
    }

    flipIfClipped(panel) {
        const margin = 8;
        panel.classList.remove("below");
        if (panel.getBoundingClientRect().top < margin) {
            panel.classList.add("below");
        }
    }

    ensureInView(panel) {
        const margin = 8;
        const rect = panel.getBoundingClientRect();
        const extra = rect.height * 0.3;
        if (rect.top < margin) {
            window.scrollBy({ top: rect.top - margin - extra, behavior: "smooth" });
        } else if (rect.bottom > window.innerHeight - margin) {
            window.scrollBy({ top: rect.bottom - (window.innerHeight - margin) + extra, behavior: "smooth" });
        }
    }

    closeAll() {
        document.querySelectorAll(".reaction-palette, .reaction-picker").forEach((panel) => {
            panel.hidden = true;
        });
        document.querySelectorAll(".reaction-add-btn, .reaction-palette-more").forEach((button) => {
            button.setAttribute("aria-expanded", "false");
        });
    }

    async react(trigger) {
        const bar = trigger.closest(".reaction-bar");
        if (!bar) {
            return;
        }
        await this.reactWith(bar, trigger.dataset.reactionEmoji);
    }

    async reactWith(bar, emoji) {
        const type = bar.dataset.reactionType;
        const uid = bar.dataset.reactionUid;
        await this.submit(`/reactions/${type}/${uid}`, { emoji }, null, (result) => this.render(bar, result));
        this.closeAll();
    }

    render(bar, result) {
        const counts = result.counts || {};
        const mine = new Set(result.mine || []);
        Object.keys(counts).forEach((emoji) => this.ensureChip(bar, emoji));
        mine.forEach((emoji) => this.ensureChip(bar, emoji));
        bar.querySelectorAll(".reaction-list .reaction-chip").forEach((chip) => {
            const emoji = chip.dataset.reactionEmoji;
            const count = counts[emoji] || 0;
            chip.querySelector(".reaction-count").textContent = count;
            chip.hidden = count === 0 && !mine.has(emoji);
            chip.classList.toggle("reacted", mine.has(emoji));
            chip.setAttribute("aria-pressed", mine.has(emoji) ? "true" : "false");
        });
    }

    ensureChip(bar, emoji) {
        const list = bar.querySelector(".reaction-list");
        if (!list || this.findChip(list, emoji)) {
            return;
        }
        const chip = document.createElement("button");
        chip.type = "button";
        chip.className = "reaction-chip";
        chip.dataset.reactionEmoji = emoji;
        chip.setAttribute("aria-label", `React with ${emoji}`);
        chip.setAttribute("aria-pressed", "false");
        chip.hidden = true;

        const glyph = document.createElement("span");
        glyph.className = "reaction-emoji";
        glyph.textContent = emoji;

        const count = document.createElement("span");
        count.className = "reaction-count";
        count.textContent = "0";

        chip.append(glyph, count);
        list.appendChild(chip);
    }

    findChip(list, emoji) {
        return Array.from(list.querySelectorAll(".reaction-chip")).find(
            (chip) => chip.dataset.reactionEmoji === emoji
        );
    }
}
