(() => {
    const sourceItems = Array.isArray(window.MEMORY_ITEMS) ? window.MEMORY_ITEMS : [];
    const board = document.getElementById('memoryBoard');
    if (!board || sourceItems.length < 2) return;

    const pairScore = document.getElementById('pairScore');
    const moveScore = document.getElementById('moveScore');
    const gameTime = document.getElementById('gameTime');
    const progress = document.getElementById('gameProgress');
    const message = document.getElementById('gameMessage');
    const restart = document.getElementById('restartButton');
    const finishPanel = document.getElementById('finishPanel');
    const playAgain = document.getElementById('playAgainButton');
    const finalMoves = document.getElementById('finalMoves');
    const finalTime = document.getElementById('finalTime');

    let deck = [];
    let firstCard = null;
    let secondCard = null;
    let locked = false;
    let pairsFound = 0;
    let moves = 0;
    let seconds = 0;
    let timer = null;
    let started = false;
    let currentAudio = null;

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

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

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

    const stopAudio = () => {
        if (currentAudio) {
            currentAudio.pause();
            currentAudio.currentTime = 0;
            currentAudio = null;
        }
        if ('speechSynthesis' in window) speechSynthesis.cancel();
    };

    const speak = item => {
        stopAudio();
        if (item.audio) {
            currentAudio = new Audio(item.audio);
            currentAudio.play().catch(() => speakWithBrowser(item.word));
        } else {
            speakWithBrowser(item.word);
        }
    };

    const speakWithBrowser = word => {
        if (!('speechSynthesis' in window)) return;
        speechSynthesis.cancel();
        const utterance = new SpeechSynthesisUtterance(word);
        utterance.lang = 'en-US';
        utterance.rate = 0.78;
        speechSynthesis.speak(utterance);
    };

    const startTimer = () => {
        if (started) return;
        started = true;
        timer = window.setInterval(() => {
            seconds += 1;
            gameTime.textContent = formatTime(seconds);
        }, 1000);
    };

    const createDeck = () => shuffle(sourceItems.flatMap(item => ([
        { pairId: item.id, kind: 'image', item },
        { pairId: item.id, kind: 'word', item }
    ])));

    const cardContent = card => {
        if (card.kind === 'image') {
            if (card.item.image) {
                return `<img src="${escapeHtml(card.item.image)}" alt="">`;
            }
            return `<span class="mm-card-letter">${escapeHtml([...card.item.word][0]?.toUpperCase() || '?')}</span>`;
        }
        return `<strong>${escapeHtml(card.item.word)}</strong>`;
    };

    const render = () => {
        const pairCount = sourceItems.length;
        const columnCount = pairCount <= 5 ? 2 : 3;

        board.dataset.pairs = String(pairCount);
        board.dataset.columns = String(columnCount);
        board.style.setProperty('--memory-columns', String(columnCount));

        board.innerHTML = deck.map((card, index) => `
            <button class="mm-card" type="button" data-index="${index}" aria-label="Memory card">
                <span class="mm-card-inner">
                    <span class="mm-card-face mm-card-cover">?</span>
                    <span class="mm-card-face mm-card-content ${card.kind === 'word' ? 'is-word' : 'is-image'}">
                        ${cardContent(card)}
                    </span>
                </span>
            </button>
        `).join('');

        pairScore.textContent = `0 / ${sourceItems.length}`;
        moveScore.textContent = '0';
        gameTime.textContent = '00:00';
        progress.style.width = '0%';
        message.textContent = 'Find the matching picture and English word.';
    };

    const updateScore = () => {
        pairScore.textContent = `${pairsFound} / ${sourceItems.length}`;
        moveScore.textContent = String(moves);
        progress.style.width = `${(pairsFound / sourceItems.length) * 100}%`;
    };

    const finish = () => {
        if (timer) window.clearInterval(timer);
        finalMoves.textContent = String(moves);
        finalTime.textContent = formatTime(seconds);
        finishPanel.hidden = false;
    };

    const resetTurn = () => {
        firstCard = null;
        secondCard = null;
        locked = false;
    };

    const handleCard = cardElement => {
        if (locked || cardElement.classList.contains('is-open') || cardElement.classList.contains('is-matched')) return;

        startTimer();
        const card = deck[Number(cardElement.dataset.index)];
        cardElement.classList.add('is-open');

        speak(card.item);

        if (!firstCard) {
            firstCard = { element: cardElement, data: card };
            message.textContent = 'Now find its matching card.';
            return;
        }

        secondCard = { element: cardElement, data: card };
        locked = true;
        moves += 1;
        updateScore();

        const isPair = firstCard.data.pairId === secondCard.data.pairId && firstCard.data.kind !== secondCard.data.kind;

        if (isPair) {
            firstCard.element.classList.add('is-matched');
            secondCard.element.classList.add('is-matched');
            pairsFound += 1;
            updateScore();
            message.textContent = 'Great! You found a pair.';
            speak(firstCard.data.item);
            resetTurn();
            if (pairsFound === sourceItems.length) window.setTimeout(finish, 500);
            return;
        }

        firstCard.element.classList.add('is-wrong');
        secondCard.element.classList.add('is-wrong');
        message.textContent = 'Try again.';

        window.setTimeout(() => {
            firstCard.element.classList.remove('is-open', 'is-wrong');
            secondCard.element.classList.remove('is-open', 'is-wrong');
            resetTurn();
        }, 800);
    };

    const resetGame = () => {
        if (timer) window.clearInterval(timer);
        stopAudio();
        deck = createDeck();
        firstCard = null;
        secondCard = null;
        locked = false;
        pairsFound = 0;
        moves = 0;
        seconds = 0;
        timer = null;
        started = false;
        finishPanel.hidden = true;
        render();
    };

    board.addEventListener('click', event => {
        const card = event.target.closest('.mm-card');
        if (card) handleCard(card);
    });

    restart.addEventListener('click', resetGame);
    playAgain.addEventListener('click', resetGame);
    window.addEventListener('beforeunload', stopAudio);
    resetGame();
})();
