function flushPromises() {
    return new Promise(resolve => setTimeout(resolve, 0));
}

describe("NotePage", () => {
    let testDiv, item1, item2, don1, doff1, don2, doff2;

    function makeItem(slug, isEquipped) {
        const li = document.createElement("li");
        li.className = "note-item";
        li.dataset.slug = slug;
        li.dataset.donUrl  = `/billboard/note/${slug}/don`;
        li.dataset.doffUrl = `/billboard/note/${slug}/doff`;
        li.innerHTML = `
            <div class="note-don-doff">
                <button class="btn btn-equip note-don-btn${isEquipped ? " btn-disabled" : ""}">${isEquipped ? "×" : "DON"}</button>
                <button class="btn btn-unequip note-doff-btn${isEquipped ? "" : " btn-disabled"}">${isEquipped ? "DOFF" : "×"}</button>
            </div>
            <div class="note-item__body"><p class="note-item__title">${slug}</p></div>
        `;
        return li;
    }

    beforeEach(() => {
        NotePage._testReset();
        testDiv = document.createElement("div");
        testDiv.innerHTML = `<span id="id_greeting_prefix">Welcome,</span><span id="id_greeting_name">Earthman</span>`;
        item1 = makeItem("super-schizo", false);
        item2 = makeItem("super-nomad",  false);
        testDiv.appendChild(item1);
        testDiv.appendChild(item2);
        document.body.appendChild(testDiv);

        window.fetch = jasmine.createSpy("fetch").and.returnValue(
            Promise.resolve({ ok: true, json: () => Promise.resolve({ title: "Schizoid Man", greeting: "21st Century" }) })
        );

        don1  = item1.querySelector(".note-don-btn");
        doff1 = item1.querySelector(".note-doff-btn");
        don2  = item2.querySelector(".note-don-btn");
        doff2 = item2.querySelector(".note-doff-btn");

        NotePage._init();
    });

    afterEach(() => {
        testDiv.remove();
        document.body.classList.remove("notes-locked");
        delete window.fetch;
    });

    // ── Lock / unlock ─────────────────────────────────────────────────────────

    describe("click-lock behaviour", () => {
        it("clicking a note adds note-item--locked", () => {
            item1.click();
            expect(item1.classList.contains("note-item--locked")).toBe(true);
        });

        it("clicking a note adds notes-locked to body", () => {
            item1.click();
            expect(document.body.classList.contains("notes-locked")).toBe(true);
        });

        it("clicking the same note again removes lock", () => {
            item1.click();
            item1.click();
            expect(item1.classList.contains("note-item--locked")).toBe(false);
            expect(document.body.classList.contains("notes-locked")).toBe(false);
        });

        it("clicking a different note moves lock to that note", () => {
            item1.click();
            item2.click();
            expect(item1.classList.contains("note-item--locked")).toBe(false);
            expect(item2.classList.contains("note-item--locked")).toBe(true);
        });

        it("body click clears all locks", () => {
            item1.click();
            document.body.click();
            expect(item1.classList.contains("note-item--locked")).toBe(false);
            expect(document.body.classList.contains("notes-locked")).toBe(false);
        });
    });

    // ── DON/DOFF state ────────────────────────────────────────────────────────

    describe("DON button", () => {
        it("clicking DON sends POST to don URL", async () => {
            don1.click();
            await flushPromises();
            expect(window.fetch).toHaveBeenCalledWith(
                "/billboard/note/super-schizo/don",
                jasmine.objectContaining({ method: "POST" })
            );
        });

        it("after DON, note gains note-item--donned", async () => {
            don1.click();
            await flushPromises();
            expect(item1.classList.contains("note-item--donned")).toBe(true);
        });

        it("after DON, lock is cleared", async () => {
            item1.click();  // lock first
            don1.click();
            await flushPromises();
            expect(item1.classList.contains("note-item--locked")).toBe(false);
            expect(document.body.classList.contains("notes-locked")).toBe(false);
        });

        it("after DON, greeting prefix and name are updated", async () => {
            don1.click();
            await flushPromises();
            expect(document.getElementById("id_greeting_prefix").innerHTML).toContain("21st Century");
            expect(document.getElementById("id_greeting_name").textContent).toBe("Schizoid Man");
        });

        it("DONning item1 auto-removes donned state from previously DONned item2", async () => {
            item2.classList.add("note-item--donned");
            don2.classList.add("btn-disabled"); don2.textContent = "×";
            doff2.classList.remove("btn-disabled"); doff2.textContent = "DOFF";
            NotePage._donnedItem = item2;

            don1.click();
            await flushPromises();

            expect(item2.classList.contains("note-item--donned")).toBe(false);
            expect(don2.classList.contains("btn-disabled")).toBe(false);
            expect(don2.textContent).toBe("DON");
            expect(doff2.classList.contains("btn-disabled")).toBe(true);
        });

        it("DONning one does not POST a doff for the previous", async () => {
            item2.classList.add("note-item--donned");
            NotePage._donnedItem = item2;
            don1.click();
            await flushPromises();
            // Only one fetch call (the DON), no DOFF call for item2
            expect(window.fetch.calls.count()).toBe(1);
        });
    });

    describe("DOFF button", () => {
        it("clicking DOFF sends POST to doff URL", async () => {
            window.fetch = jasmine.createSpy("fetch").and.returnValue(
                Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
            );
            item1.classList.add("note-item--donned");
            doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
            don1.classList.add("btn-disabled"); don1.textContent = "×";
            NotePage._donnedItem = item1;

            doff1.click();
            await flushPromises();
            expect(window.fetch).toHaveBeenCalledWith(
                "/billboard/note/super-schizo/doff",
                jasmine.objectContaining({ method: "POST" })
            );
        });

        it("after DOFF, note loses note-item--donned", async () => {
            window.fetch = jasmine.createSpy("fetch").and.returnValue(
                Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
            );
            item1.classList.add("note-item--donned");
            doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
            NotePage._donnedItem = item1;

            doff1.click();
            await flushPromises();
            expect(item1.classList.contains("note-item--donned")).toBe(false);
        });

        it("after DOFF, greeting reverts to Welcome, / Earthman", async () => {
            window.fetch = jasmine.createSpy("fetch").and.returnValue(
                Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
            );
            document.getElementById("id_greeting_prefix").textContent = "21st Century";
            document.getElementById("id_greeting_name").textContent = "Schizoid Man";
            item1.classList.add("note-item--donned");
            doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
            NotePage._donnedItem = item1;

            doff1.click();
            await flushPromises();
            expect(document.getElementById("id_greeting_prefix").textContent).toBe("Welcome,");
            expect(document.getElementById("id_greeting_name").textContent).toBe("Earthman");
        });
    });

    // ── Initial donned state ──────────────────────────────────────────────────

    describe("initial load with an already-donned note", () => {
        it("note whose DON is btn-disabled gets note-item--donned on init", () => {
            const equippedItem = makeItem("stargazer", true);
            testDiv.appendChild(equippedItem);
            NotePage._testReset();
            NotePage._init();
            expect(equippedItem.classList.contains("note-item--donned")).toBe(true);
        });
    });
});
