(() => {
    "use strict";

    const originalItems = Array.isArray(window.MATCHING_ITEMS)
        ? window.MATCHING_ITEMS
        : [];

    if (originalItems.length < 2) {
        return;
    }

    const config = window.MATCHING_CONFIG || {};
    const shouldShuffle = config.shuffle !== false;

    const cardGrid = document.getElementById("cardGrid");
    const matchedScore = document.getElementById("matchedScore");
    const gameScore = document.getElementById("gameScore");
    const gameTime = document.getElementById("gameTime");
    const gameProgress = document.getElementById("gameProgress");
    const gameMessage = document.getElementById("gameMessage");
    const restartButton = document.getElementById("restartButton");
    const finishPanel = document.getElementById("finishPanel");
    const finalScore = document.getElementById("finalScore");
    const finalTime = document.getElementById("finalTime");
    const playAgainButton = document.getElementById("playAgainButton");

    let gameItems = [];
    let selectedCards = [];
    let matched = 0;
    let score = 0;
    let seconds = 0;
    let timerId = null;
    let locked = false;
    let currentAudio = null;

    function shuffle(items) {
        const result = [...items];

        for (let i = result.length - 1; i > 0; i -= 1) {
            const j = Math.floor(Math.random() * (i + 1));
            [result[i], result[j]] = [result[j], result[i]];
        }

        return result;
    }

    function escapeHtml(value) {
        return String(value ?? "").replace(/[&<>'"]/g, (char) => ({
            "&": "&amp;",
            "<": "&lt;",
            ">": "&gt;",
            "'": "&#039;",
            '"': "&quot;"
        })[char]);
    }

    function formatTime(total) {
        const minutes = Math.floor(total / 60).toString().padStart(2, "0");
        const secs = (total % 60).toString().padStart(2, "0");
        return `${minutes}:${secs}`;
    }

    function startTimer() {
        clearInterval(timerId);

        timerId = window.setInterval(() => {
            seconds += 1;
            gameTime.textContent = formatTime(seconds);
        }, 1000);
    }

    function stopAudio() {
        if (currentAudio) {
            currentAudio.pause();
            currentAudio.currentTime = 0;
            currentAudio = null;
        }

        if ("speechSynthesis" in window) {
            window.speechSynthesis.cancel();
        }
    }

    function browserSpeak(word) {
        if (!("speechSynthesis" in window)) {
            return;
        }

        const utterance = new SpeechSynthesisUtterance(word);
        utterance.lang = "en-US";
        utterance.rate = 0.78;
        window.speechSynthesis.speak(utterance);
    }

    function speak(item) {
        if (!item) {
            return;
        }

        stopAudio();

        if (item.audio) {
            currentAudio = new Audio(item.audio);
            currentAudio.play().catch(() => browserSpeak(item.word));
            return;
        }

        browserSpeak(item.word);
    }

    function updateStats() {
        matchedScore.textContent = `${matched} / ${gameItems.length}`;
        gameScore.textContent = String(score);
        gameProgress.style.width = `${(matched / gameItems.length) * 100}%`;
    }

    function makeImageCard(item) {
        const button = document.createElement("button");
        button.type = "button";
        button.className = "mg-match-card mg-image-card";
        button.dataset.id = String(item.id);
        button.dataset.kind = "image";
        button.setAttribute("aria-label", `Picture for ${item.word}`);

        if (item.image) {
            const image = document.createElement("img");
            image.src = item.image;
            image.alt = item.word;
            image.loading = "lazy";
            image.addEventListener("error", () => {
                image.remove();
                button.textContent = item.word.charAt(0).toUpperCase();
            });
            button.appendChild(image);
        } else {
            button.textContent = item.word.charAt(0).toUpperCase();
        }

        button.addEventListener("click", () => selectCard(button, item, "image"));
        return button;
    }

    function makeWordCard(item) {
        const button = document.createElement("button");
        button.type = "button";
        button.className = "mg-match-card mg-word-card";
        button.dataset.id = String(item.id);
        button.dataset.kind = "word";
        button.innerHTML = `<strong>${escapeHtml(item.word)}</strong>`;
        button.setAttribute("aria-label", item.word);
        button.addEventListener("click", () => selectCard(button, item, "word"));
        return button;
    }

    function clearSelection() {
        selectedCards.forEach(({ button }) => {
            button.classList.remove("selected");
        });

        selectedCards = [];
    }

    function selectCard(button, item, kind) {
        if (
            locked ||
            button.classList.contains("matched") ||
            selectedCards.some((selected) => selected.button === button)
        ) {
            return;
        }

        speak(item);

        if (selectedCards.length === 1 && selectedCards[0].kind === kind) {
            selectedCards[0].button.classList.remove("selected");
            selectedCards = [];
        }

        selectedCards.push({ button, item, kind });
        button.classList.add("selected");

        if (selectedCards.length === 2) {
            checkPair();
        }
    }

    function checkPair() {
        const [first, second] = selectedCards;

        if (!first || !second) {
            return;
        }

        locked = true;

        const correct =
            first.kind !== second.kind &&
            Number(first.item.id) === Number(second.item.id);

        if (correct) {
            first.button.classList.add("correct");
            second.button.classList.add("correct");
            gameMessage.textContent = `✓ ${first.item.word}`;
            score += 100;
            matched += 1;

            window.setTimeout(() => {
                first.button.classList.add("matched");
                second.button.classList.add("matched");
                clearSelection();
                updateStats();
                locked = false;

                if (matched === gameItems.length) {
                    finishGame();
                } else {
                    gameMessage.textContent =
                        "Tap a picture and its matching English word.";
                }
            }, 420);

            return;
        }

        first.button.classList.add("wrong");
        second.button.classList.add("wrong");
        gameMessage.textContent = "Try again!";
        score = Math.max(0, score - 20);

        window.setTimeout(() => {
            first.button.classList.remove("wrong");
            second.button.classList.remove("wrong");
            clearSelection();
            updateStats();
            locked = false;
            gameMessage.textContent =
                "Tap a picture and its matching English word.";
        }, 520);
    }

    function renderBoard() {
        const pairCount = gameItems.length;
        const columnCount = pairCount <= 5 ? 2 : 3;
        const cards = [];

        cardGrid.dataset.pairs = String(pairCount);
        cardGrid.dataset.columns = String(columnCount);
        cardGrid.style.setProperty("--matching-columns", String(columnCount));

        gameItems.forEach((item) => {
            cards.push(makeImageCard(item));
            cards.push(makeWordCard(item));
        });

        cardGrid.innerHTML = "";

        shuffle(cards).forEach((card) => {
            cardGrid.appendChild(card);
        });
    }

    function finishGame() {
        clearInterval(timerId);
        stopAudio();
        finalScore.textContent = String(score);
        finalTime.textContent = formatTime(seconds);
        finishPanel.hidden = false;
    }

    function renderGame() {
        clearInterval(timerId);
        stopAudio();

        gameItems = shouldShuffle ? shuffle(originalItems) : [...originalItems];
        selectedCards = [];
        matched = 0;
        score = 0;
        seconds = 0;
        locked = false;

        gameTime.textContent = "00:00";
        gameMessage.textContent =
            "Tap a picture and its matching English word.";
        finishPanel.hidden = true;

        renderBoard();
        updateStats();
        startTimer();
    }

    restartButton.addEventListener("click", renderGame);
    playAgainButton.addEventListener("click", renderGame);
    window.addEventListener("beforeunload", stopAudio);

    renderGame();
})();
