var RoleSelect = (function () {
    // Set to true by handleTurnChanged so that a WS turn_changed that races
    // ahead of the fetch response doesn't get overridden by Tray.open().
    var _turnChangedBeforeFetch = false;

    // Set to true while placeCard animation is running.  handleTurnChanged
    // defers its work until the animation completes.
    var _animationPending  = false;
    var _pendingTurnChange = null;

    // Delay before the tray animation begins (ms).  Gives the gamer a moment
    // to see their pick confirmed before the tray slides in.  Set to 0 by
    // _testReset() so Jasmine tests don't need jasmine.clock().
    var _placeCardDelay = 3000;

    // Delay after the tray closes before advancing to the next turn (ms).
    // Gives the gamer a moment to see their confirmed seat before the turn moves.
    var _postTrayDelay = 3000;

    var ROLES = [
        { code: "SC", name: "Shepherd",   element: "Air"   },
        { code: "PC", name: "Player",     element: "Fire"  },
        { code: "NC", name: "Narrator",   element: "Time"  },
        { code: "AC", name: "Alchemist",  element: "Water" },
        { code: "BC", name: "Builder",    element: "Stone" },
        { code: "EC", name: "Economist",  element: "Space" },
    ];

    function getSelectRoleUrl() {
        var el = document.querySelector("[data-select-role-url]");
        return el ? el.dataset.selectRoleUrl : null;
    }

    function getCsrf() {
        var m = document.cookie.match(/csrftoken=([^;]+)/);
        return m ? m[1] : "";
    }

    function closeFan() {
        var backdrop = document.querySelector(".role-select-backdrop");
        if (backdrop) backdrop.remove();
    }

    function _showNoDeckWarning(stack) {
        if (document.querySelector(".role-no-deck-warning")) return;
        var el = document.createElement("div");
        el.className = "role-no-deck-warning";
        el.innerHTML =
            "<p>Equip card deck before Role select</p>" +
            "<div class=\"guard-actions\">" +
            "<a class=\"btn btn-info\" href=\"/gameboard/\">FYI</a>" +
            "<button class=\"btn btn-cancel\">NVM</button>" +
            "</div>";
        el.querySelector(".btn-cancel").addEventListener("click", function () {
            el.remove();
        });
        if (stack) {
            var rect = stack.getBoundingClientRect();
            el.style.position = "fixed";
            el.style.left = Math.round(rect.left + rect.width  / 2) + "px";
            el.style.top  = Math.round(rect.top  + rect.height / 2) + "px";
            el.style.transform = "translate(-50%, -50%)";
        }
        document.body.appendChild(el);
    }

    function selectRole(roleCode) {
        _turnChangedBeforeFetch = false;  // fresh selection, reset the race flag
        closeFan();

        // Show the tray handle — gamer confirmed a pick, tray animation about to run
        var trayWrap = document.getElementById("id_tray_wrap");
        if (trayWrap) trayWrap.classList.remove("role-select-phase");

        // Immediately lock the stack — do not wait for WS turn_changed
        var stack = document.querySelector(".card-stack[data-starter-roles]");
        if (stack) {
            stack.dataset.state = "ineligible";
            stack.removeEventListener("click", openFan);
            var current = stack.dataset.starterRoles;
            stack.dataset.starterRoles = current ? current + "," + roleCode : roleCode;
        }

        // Mark seat as actively being claimed (glow state) and swap ban → check immediately
        var activePos = document.querySelector('.table-seat[data-role="' + roleCode + '"]');
        if (activePos) {
            activePos.classList.add('active');
            var ban = activePos.querySelector('.fa-ban');
            if (ban) { ban.classList.remove('fa-ban'); ban.classList.add('fa-circle-check'); }
        }

        // Immediately fade out the gate-slot circle for the current turn's slot
        var activeSlot = stack ? stack.dataset.activeSlot : null;
        if (activeSlot) {
            var slotCircle = document.querySelector('.gate-slot[data-slot="' + activeSlot + '"]');
            if (slotCircle) slotCircle.classList.add('role-assigned');
        }

        var url = getSelectRoleUrl();
        if (!url) return;

        // Block handleTurnChanged immediately — WS turn_changed can arrive while
        // the fetch is in-flight and must be deferred until our animation completes.
        _animationPending = true;

        fetch(url, {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                "X-CSRFToken": getCsrf(),
            },
            body: "role=" + encodeURIComponent(roleCode),
        }).then(function (response) {
            if (!response.ok) {
                // Server rejected (role already taken) — undo optimistic update
                _animationPending = false;
                if (stack) {
                    stack.dataset.starterRoles = stack.dataset.starterRoles
                        .split(",").filter(function (r) { return r.trim() !== roleCode; }).join(",");
                }
                openFan();
            } else {
                // Animate the role card into the tray: open, fade-in, force-close.
                // Any turn_changed that arrived while the fetch was in-flight is
                // queued in _pendingTurnChange and will run after onComplete.
                if (typeof Tray !== "undefined") {
                    setTimeout(function () {
                        Tray.placeCard(roleCode, function () {
                            // Swap ban → check, clear glow, mark seat as confirmed
                            var seatedPos = document.querySelector('.table-seat[data-role="' + roleCode + '"]');
                            if (seatedPos) {
                                seatedPos.classList.remove('active');
                                seatedPos.classList.add('role-confirmed');
                            }
                            // Hold _animationPending through the post-tray pause so any
                            // turn_changed WS event that arrives now is still deferred.
                            setTimeout(function () {
                                _animationPending = false;
                                if (_pendingTurnChange) {
                                    var ev = _pendingTurnChange;
                                    _pendingTurnChange = null;
                                    handleTurnChanged(ev);
                                }
                            }, _postTrayDelay);
                        });
                    }, _placeCardDelay);
                } else {
                    _animationPending = false;
                }
            }
        });
    }

    function getStarterRoles() {
        var stack = document.querySelector(".card-stack[data-starter-roles]");
        if (!stack) return [];
        var raw = stack.dataset.starterRoles;
        return raw ? raw.split(",").map(function (s) { return s.trim(); }) : [];
    }

    function openFan() {
        if (document.querySelector(".role-select-backdrop")) return;

        var stack = document.querySelector(".card-stack[data-starter-roles]");
        if (stack && stack.dataset.equippedDeck === "") {
            _showNoDeckWarning(stack);
            return;
        }

        var taken = getStarterRoles();
        var available = ROLES.filter(function (r) { return taken.indexOf(r.code) === -1; });

        var backdrop = document.createElement("div");
        backdrop.className = "role-select-backdrop";

        var modal = document.createElement("div");
        modal.id = "id_role_select";

        available.forEach(function (role) {
            var card = document.createElement("div");
            card.className = "card";
            card.dataset.role = role.code;

            var back = document.createElement("div");
            back.className = "card-back";
            back.textContent = "ROLE";

            var front = document.createElement("div");
            front.className = "card-front";
            front.innerHTML = '<div class="card-role-name">' + role.name + "</div>";

            card.appendChild(back);
            card.appendChild(front);

            card.addEventListener("mouseenter", function () {
                card.classList.add("flipped");
            });
            card.addEventListener("mouseleave", function () {
                if (!card.classList.contains("guard-active")) {
                    card.classList.remove("flipped");
                }
            });
            card.addEventListener("click", function (e) {
                e.stopPropagation();
                card.classList.add("flipped");
                card.classList.add("guard-active");
                window.showGuard(
                    card,
                    "Start round 1<br>as " + role.name + " (" + role.code + ") …?",
                    function () {                       // confirm
                        card.classList.remove("guard-active");
                        selectRole(role.code);
                    },
                    function () {                       // dismiss (NVM / outside click)
                        card.classList.remove("guard-active");
                        card.classList.remove("flipped");
                    },
                    { invertY: true }   // modal grid: tooltip flies away from centre (upper→above, lower→below)
                );
            });

            modal.appendChild(card);
        });

        backdrop.appendChild(modal);
        backdrop.addEventListener("click", closeFan);
        document.body.appendChild(backdrop);
    }

    function init() {
        var stack = document.querySelector(".card-stack[data-state='eligible']");
        if (!stack) return;
        stack.addEventListener("click", openFan);
    }

    var _reload = function () { window.location.reload(); };

    function handleAllRolesFilled() {
        var wrap = document.getElementById('id_pick_sigs_wrap');
        if (wrap) wrap.style.display = '';
        var stack = document.querySelector('.card-stack');
        if (stack) stack.remove();
        var trayWrap = document.getElementById('id_tray_wrap');
        if (trayWrap) trayWrap.classList.remove('role-select-phase');
    }

    function handleSigSelectStarted() {
        _reload();
    }

    function handleTurnChanged(event) {
        // If a placeCard animation is running, defer until it completes.
        if (_animationPending) {
            _pendingTurnChange = event;
            return;
        }

        var active = String(event.detail.active_slot);

        // Force-close tray instantly so it never obscures the next player's card-stack.
        // Also set the race flag so the fetch .then() doesn't re-open if it arrives late.
        _turnChangedBeforeFetch = true;
        if (typeof Tray !== "undefined") Tray.forceClose();

        // Hide tray handle until the next player confirms their pick
        var trayWrap = document.getElementById("id_tray_wrap");
        if (trayWrap) trayWrap.classList.add("role-select-phase");

        // Clear any stale .active glow from hex seats
        document.querySelectorAll('.table-seat.active').forEach(function (p) {
            p.classList.remove('active');
        });

        // Sync seat icons from starter_roles so state persists without a reload
        if (event.detail.starter_roles) {
            var assignedRoles = event.detail.starter_roles;
            document.querySelectorAll(".table-seat").forEach(function (seat) {
                var role = seat.dataset.role;
                if (assignedRoles.indexOf(role) !== -1) {
                    seat.classList.add("role-confirmed");
                    var ban = seat.querySelector(".fa-ban");
                    if (ban) { ban.classList.remove("fa-ban"); ban.classList.add("fa-circle-check"); }
                }
            });
            // Hide slot circles in turn order: slots 1..N done when N roles assigned
            var assignedCount = assignedRoles.length;
            document.querySelectorAll(".gate-slot[data-slot]").forEach(function (circle) {
                if (parseInt(circle.dataset.slot, 10) <= assignedCount) {
                    circle.classList.add("role-assigned");
                }
            });
        }

        // Update active slot on the card stack so selectRole() can read it
        var stack = document.querySelector(".card-stack[data-user-slots]");
        if (stack) stack.dataset.activeSlot = active;

        var stack = document.querySelector(".card-stack[data-user-slots]");
        if (stack) {
            // Sync starter-roles from server so the fan reflects actual DB state
            if (event.detail.starter_roles) {
                stack.dataset.starterRoles = event.detail.starter_roles.join(",");
            }

            // Update eligibility and ban icon together
            var userSlots = stack.dataset.userSlots
                ? stack.dataset.userSlots.split(",") : [];
            if (userSlots.indexOf(active) !== -1) {
                stack.dataset.state = "eligible";
                var ban = stack.querySelector(".fa-ban");
                if (ban) ban.remove();
                stack.removeEventListener("click", openFan);
                stack.addEventListener("click", openFan);
            } else {
                stack.dataset.state = "ineligible";
                stack.removeEventListener("click", openFan);
                if (!stack.querySelector(".fa-ban")) {
                    var icon = document.createElement("i");
                    icon.className = "fa-solid fa-ban";
                    stack.appendChild(icon);
                }
            }
        }

        // Clear any stale seat glow (JS-only; glow is only during tray animation)
        document.querySelectorAll(".table-seat.active").forEach(function (s) {
            s.classList.remove("active");
        });
    }

    window.addEventListener("room:role_select_start", init);
    window.addEventListener("room:turn_changed", handleTurnChanged);
    window.addEventListener("room:all_roles_filled", handleAllRolesFilled);
    window.addEventListener("room:sig_select_started", handleSigSelectStarted);

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", init);
    } else {
        init();
    }

    return {
        openFan:    openFan,
        closeFan:   closeFan,
        setReload:  function (fn) { _reload = fn; },
        // Testing hook — resets animation-pause state between Jasmine specs
        _testReset: function () {
            _animationPending  = false;
            _pendingTurnChange = null;
            _placeCardDelay    = 0;
            _postTrayDelay     = 0;
        },
    };
}());
