describe("SeaDeal", () => {
    let testDiv, overlay, stage, stageCard, statBlock;

    const CARD = {
        id: 99, name: "Queen of Crowns",
        arcana: "MIDDLE", suit: "CROWNS", number: 13,
        corner_rank: "Q", suit_icon: "fa-crown",
        name_group: "", name_title: "Queen of Crowns",
        levity_qualifier: "Elevated", gravity_qualifier: "Graven",
        reversal_qualifier: "Vacant",
        keywords_upright: ["nurturing", "practical", "abundance"],
        keywords_reversed: ["financial dependence", "smothering"],
        energies:    [{ type: "LIBIDO",  effect: "Energy entry." }],
        operations:  [{ type: "COVER",   effect: "Operation entry." }],
    };

    function makeFixture({ polarity = "levity" } = {}) {
        testDiv = document.createElement("div");
        testDiv.innerHTML = `
            <div id="id_sea_overlay" data-sea-user-polarity="${polarity}">

                <!-- Cross grid (simplified) -->
                <div class="sea-cross">
                    <div class="sea-crucifix-cell sea-pos-crown">
                        <div class="sea-card-slot sea-card-slot--empty"></div>
                    </div>
                    <div class="sea-crucifix-cell sea-pos-leave">
                        <div class="sea-card-slot sea-card-slot--empty"></div>
                    </div>
                    <div class="sea-crucifix-cell sea-pos-core">
                        <div class="sea-sig-card"></div>
                        <div class="sea-pos-cover">
                            <div class="sea-card-slot sea-card-slot--empty"></div>
                        </div>
                        <div class="sea-pos-cross">
                            <div class="sea-card-slot sea-card-slot--empty"></div>
                        </div>
                    </div>
                    <div class="sea-crucifix-cell sea-pos-loom">
                        <div class="sea-card-slot sea-card-slot--empty"></div>
                    </div>
                    <div class="sea-crucifix-cell sea-pos-lay">
                        <div class="sea-card-slot sea-card-slot--empty"></div>
                    </div>
                </div>

                <!-- Sea stage (big card viewer) -->
                <div class="sea-stage" id="id_sea_stage" style="display:none">
                    <div class="sea-stage-backdrop"></div>
                    <div class="sea-stage-content">
                        <div class="sig-stage-card sea-stage-card" style="--sig-card-w:140px">
                            <div class="fan-card-corner fan-card-corner--tl">
                                <span class="fan-corner-rank"></span>
                                <i class="fa-solid stage-suit-icon" style="display:none"></i>
                            </div>
                            <div class="fan-card-face">
                                <div class="fan-card-face-upright">
                                    <p class="fan-card-name-group"></p>
                                    <p class="sig-qualifier-above"></p>
                                    <h3 class="fan-card-name"></h3>
                                    <p class="sig-qualifier-below"></p>
                                </div>
                                <p class="fan-card-arcana"></p>
                                <div class="fan-card-face-reversal">
                                    <p class="fan-card-reversal-name"></p>
                                    <p class="fan-card-reversal-qualifier"></p>
                                </div>
                            </div>
                            <div class="fan-card-corner fan-card-corner--br">
                                <span class="fan-corner-rank"></span>
                                <i class="fa-solid stage-suit-icon" style="display:none"></i>
                            </div>
                        </div>
                        <div class="sig-stat-block sea-stat-block">
                            <button class="btn btn-reverse spin-btn" type="button">SPIN</button>
                            <button class="btn btn-info fyi-btn" type="button">FYI</button>
                            <div class="stat-face stat-face--upright">
                                <p class="stat-face-label">Emanation</p>
                                <ul class="stat-keywords" id="id_sea_stat_upright"></ul>
                            </div>
                            <div class="stat-face stat-face--reversed">
                                <p class="stat-face-label">Reversal</p>
                                <ul class="stat-keywords" id="id_sea_stat_reversed"></ul>
                            </div>
                            <div class="sig-info" id="id_sea_fyi_panel" style="display:none">
                                <div class="sig-info-header">
                                    <h4 class="sig-info-title"></h4>
                                    <p class="sig-info-type"></p>
                                </div>
                                <p class="sig-info-effect"></p>
                                <span class="sig-info-index"></span>
                            </div>
                            <button class="btn btn-nav-left fyi-prev" type="button">PRV</button>
                            <button class="btn btn-nav-right fyi-next" type="button">NXT</button>
                        </div>
                    </div>
                </div>

            </div>
        `;
        document.body.appendChild(testDiv);
        overlay    = testDiv.querySelector("#id_sea_overlay");
        stage      = testDiv.querySelector("#id_sea_stage");
        stageCard  = testDiv.querySelector(".sea-stage-card");
        statBlock  = testDiv.querySelector(".sea-stat-block");
        SeaDeal._testInit();
    }

    afterEach(() => {
        if (testDiv) testDiv.remove();
        // Purge any stale overlays left by tests that called makeFixture() twice
        document.querySelectorAll('#id_sea_overlay').forEach(el => el.remove());
    });

    // ── openStage ────────────────────────────────────────────────────────── //

    describe("openStage()", () => {
        beforeEach(() => makeFixture());

        it("makes #id_sea_stage visible", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            expect(stage.style.display).not.toBe("none");
        });

        it("populates corner rank on stage card", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            const rank = stageCard.querySelector(".fan-card-corner--tl .fan-corner-rank");
            expect(rank.textContent).toBe("Q");
        });

        it("shows suit icon on stage card", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            const icon = stageCard.querySelector(".fan-card-corner--tl .stage-suit-icon");
            expect(icon.style.display).not.toBe("none");
            expect(icon.classList.contains("fa-crown")).toBe(true);
        });

        it("populates upright card name for non-major", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            expect(stageCard.querySelector(".fan-card-name").textContent).toBe("Queen of Crowns");
        });

        it("puts levity qualifier in qualifier-above for non-major", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            expect(stageCard.querySelector(".sig-qualifier-above").textContent).toBe("Elevated");
        });

        it("puts gravity qualifier in qualifier-above for non-major gravity", () => {
            // Re-init with gravity polarity (no double-append; beforeEach already ran)
            overlay.dataset.seaUserPolarity = "gravity";
            SeaDeal._testInit();
            stageCard = testDiv.querySelector(".sea-stage-card");
            SeaDeal.openStage(CARD, ".sea-pos-cover", false);
            expect(stageCard.querySelector(".sig-qualifier-above").textContent).toBe("Graven");
        });

        it("puts reversal word in reversal-qualifier slot", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            expect(stageCard.querySelector(".fan-card-reversal-qualifier").textContent).toBe("Vacant");
        });

        it("populates upright keywords in stat block", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            const items = testDiv.querySelectorAll("#id_sea_stat_upright li");
            expect(items.length).toBe(3);
            expect(items[0].textContent).toBe("nurturing");
        });

        it("populates reversed keywords in stat block", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            const items = testDiv.querySelectorAll("#id_sea_stat_reversed li");
            expect(items.length).toBe(2);
        });

        it("fills the target position slot", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            const slot = testDiv.querySelector(".sea-pos-cover .sea-card-slot");
            expect(slot.classList.contains("sea-card-slot--filled")).toBe(true);
        });

        it("resets SPIN to upright when opening a new card", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            stageCard.classList.add("stage-card--reversed");
            statBlock.classList.add("is-reversed");
            SeaDeal.openStage(CARD, ".sea-pos-cross", true);
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
            expect(statBlock.classList.contains("is-reversed")).toBe(false);
        });
    });

    // ── SPIN in sea stage ─────────────────────────────────────────────────── //

    describe("SPIN btn in sea stage", () => {
        beforeEach(() => {
            makeFixture();
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
        });

        it("toggles is-reversed on stat block", () => {
            testDiv.querySelector(".spin-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(statBlock.classList.contains("is-reversed")).toBe(true);
        });

        it("toggles stage-card--reversed on stage card", () => {
            testDiv.querySelector(".spin-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
        });

        it("second SPIN click restores upright", () => {
            const btn = testDiv.querySelector(".spin-btn");
            btn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
            btn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(statBlock.classList.contains("is-reversed")).toBe(false);
        });
    });

    // ── FYI in sea stage ──────────────────────────────────────────────────── //

    describe("FYI btn in sea stage", () => {
        beforeEach(() => {
            makeFixture();
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
        });

        it("FYI click shows the info panel", () => {
            testDiv.querySelector(".fyi-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(testDiv.querySelector("#id_sea_fyi_panel").style.display).not.toBe("none");
        });

        it("shows first energy entry title as 'Energy'", () => {
            testDiv.querySelector(".fyi-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(testDiv.querySelector(".sig-info-title").textContent).toBe("Energy");
        });

        it("shows first entry type", () => {
            testDiv.querySelector(".fyi-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(testDiv.querySelector(".sig-info-type").textContent).toBe("LIBIDO");
        });

        it("NXT advances to operation entry", () => {
            testDiv.querySelector(".fyi-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            testDiv.querySelector(".fyi-next").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(testDiv.querySelector(".sig-info-title").textContent).toBe("Operation");
            expect(testDiv.querySelector(".sig-info-type").textContent).toBe("COVER");
        });
    });

    // ── Backdrop dismiss ──────────────────────────────────────────────────── //

    describe("stage backdrop click", () => {
        beforeEach(() => {
            makeFixture();
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
        });

        it("hides the sea stage", () => {
            testDiv.querySelector(".sea-stage-backdrop").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(stage.style.display).toBe("none");
        });

        it("leaves the slot filled after dismiss", () => {
            testDiv.querySelector(".sea-stage-backdrop").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            const slot = testDiv.querySelector(".sea-pos-cover .sea-card-slot");
            expect(slot.classList.contains("sea-card-slot--filled")).toBe(true);
        });
    });

    // ── Re-open from deposited slot (two-step tap) ────────────────────────── //

    describe("clicking a deposited slot", () => {
        let slot;
        beforeEach(() => {
            makeFixture();
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            // Dismiss first — adds --visible to slot
            testDiv.querySelector(".sea-stage-backdrop").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            slot = testDiv.querySelector(".sea-pos-cover .sea-card-slot");
        });

        it("first click focuses the slot (does not yet open stage)", () => {
            slot.dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(slot.classList.contains("sea-card-slot--focused")).toBeTrue();
            expect(stage.style.display).toBe("none");
        });

        it("re-opens the sea stage on second click", () => {
            slot.dispatchEvent(new MouseEvent("click", { bubbles: true })); // focus
            slot.dispatchEvent(new MouseEvent("click", { bubbles: true })); // open
            expect(stage.style.display).not.toBe("none");
        });

        it("re-populates stage with the same card rank on second click", () => {
            slot.dispatchEvent(new MouseEvent("click", { bubbles: true })); // focus
            slot.dispatchEvent(new MouseEvent("click", { bubbles: true })); // open
            const rank = stageCard.querySelector(".fan-card-corner--tl .fan-corner-rank");
            expect(rank.textContent).toBe("Q");
        });
    });

    // ── Reversed-card auto-rotate on modal open ─────────────────────────────── //
    //
    // Spec 2026-05-26: a reversed card opens rightside-up (so the user sees
    // the upright card frame + a dimmed upright title + a highlighted
    // upside-down reversal title) and then SLOWLY auto-rotates 180° to land
    // upside-down. Duration is 2× the normal SPIN transition (0.4s → 0.8s).
    // The stat-block gets `.is-reversed` immediately (REVERSAL label
    // highlights as the modal opens); the card itself gets `.stage-card--
    // reversed` only after the auto-rotate lands. `[data-spinning]` is set
    // for the rotation window so the FLIP btn hides during the spin (the
    // SCSS-side companion rule also repositions FLIP to visual bottom-left
    // post-rotation, matching the game-kit fan precedent).

    describe("reversed-card open", () => {
        const REVERSED_CARD = Object.assign({}, CARD, { reversed: true });

        beforeEach(() => {
            jasmine.clock().install();
            makeFixture();
        });
        afterEach(() => jasmine.clock().uninstall());

        it("sets is-reversed on stat-block immediately", () => {
            SeaDeal.openStage(REVERSED_CARD, ".sea-pos-cover", true);
            expect(statBlock.classList.contains("is-reversed")).toBe(true);
        });

        it("does NOT set stage-card--reversed immediately (card starts rightside-up)", () => {
            SeaDeal.openStage(REVERSED_CARD, ".sea-pos-cover", true);
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
        });

        it("does NOT set data-spinning before the flip-in completes", () => {
            SeaDeal.openStage(REVERSED_CARD, ".sea-pos-cover", true);
            expect(stageCard.dataset.spinning).toBeUndefined();
        });

        it("kicks off the auto-rotate after the flip-in window — data-spinning set + stage-card--reversed added", () => {
            SeaDeal.openStage(REVERSED_CARD, ".sea-pos-cover", true);
            jasmine.clock().tick(500);  // past sea-flip-in (0.35s) + buffer
            expect(stageCard.dataset.spinning).toBe("1");
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
        });

        it("clears data-spinning once the auto-rotate window has elapsed", () => {
            SeaDeal.openStage(REVERSED_CARD, ".sea-pos-cover", true);
            jasmine.clock().tick(500 + 900);  // past flip-in + 0.8s rotate
            expect(stageCard.dataset.spinning).toBeUndefined();
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
        });

        it("does NOT auto-rotate an upright card", () => {
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
            jasmine.clock().tick(2000);
            expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
            expect(stageCard.dataset.spinning).toBeUndefined();
        });
    });

    // ── SPIN click hides FLIP during rotation ───────────────────────────────── //

    describe("SPIN click hides FLIP via [data-spinning]", () => {
        beforeEach(() => {
            jasmine.clock().install();
            makeFixture();
            SeaDeal.openStage(CARD, ".sea-pos-cover", true);
        });
        afterEach(() => jasmine.clock().uninstall());

        it("sets data-spinning on SPIN click so the FLIP btn hides during the rotation", () => {
            testDiv.querySelector(".spin-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            expect(stageCard.dataset.spinning).toBe("1");
        });

        it("clears data-spinning after the SPIN transition window elapses", () => {
            testDiv.querySelector(".spin-btn").dispatchEvent(new MouseEvent("click", { bubbles: true }));
            jasmine.clock().tick(500);  // past 0.4s SPIN transition
            expect(stageCard.dataset.spinning).toBeUndefined();
        });
    });
});
