(() => {
    'use strict';

    const root = document.getElementById('flashcardsEditor');
    if (!root) return;

    const setId = Number(root.dataset.setId);
    const library = document.getElementById('wordLibrary');
    const selected = document.getElementById('selectedWords');
    const search = document.getElementById('wordSearch');
    const title = document.getElementById('setTitle');
    const description = document.getElementById('setDescription');
    const saveStatus = document.getElementById('saveStatus');
    const summarySaveStatus = document.getElementById('summarySaveStatus');
    const speakButton = document.getElementById('speakButton');

    let dragged = null;
    let searchTimer = null;
    let saveTimer = null;
    let currentWord = null;

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

    function parseWord(element) {
        try {
            return JSON.parse(element.dataset.wordJson || '{}');
        } catch (error) {
            console.error('Invalid word data', error);
            return null;
        }
    }

    async function requestJson(url, data = null, asJson = false) {
        const options = {};
        if (data !== null) {
            options.method = 'POST';
            if (asJson) {
                options.headers = {'Content-Type': 'application/json'};
                options.body = JSON.stringify(data);
            } else {
                const form = new FormData();
                Object.entries(data).forEach(([key, value]) => form.append(key, value));
                options.body = form;
            }
        }

        const response = await fetch(url, options);
        let payload;
        try {
            payload = await response.json();
        } catch (_) {
            throw new Error('The server returned an invalid response.');
        }
        if (!response.ok || payload.success === false) {
            throw new Error(payload.message || 'The request could not be completed.');
        }
        return payload;
    }

    function setSaveState(text, state = '') {
        saveStatus.textContent = text;
        summarySaveStatus.textContent = text;
        saveStatus.dataset.state = state;
        summarySaveStatus.dataset.state = state;
    }

    function updateCounts() {
        const count = selected.querySelectorAll('.fc-selected-item').length;
        document.getElementById('selectedCount').textContent = count;
        document.getElementById('summarySelectedCount').textContent = count;
        document.getElementById('selectedEmpty').hidden = count > 0;
        [...selected.querySelectorAll('.fc-selected-item')].forEach((element, index) => {
            element.querySelector('.fc-order').textContent = String(index + 1);
        });
    }

    function setLibraryItemState(wordId, isSelected) {
        const button = library.querySelector(`[data-add-word="${Number(wordId)}"]`);
        if (!button) return;
        button.disabled = isSelected;
        button.classList.toggle('is-selected', isSelected);
        const label = button.querySelector('.fc-add-label');
        if (label) label.textContent = isSelected ? '✓ Added' : '+ Add';
    }

    function renderPreview(word) {
        currentWord = word;
        const previewImage = document.getElementById('previewImage');
        const previewWord = document.getElementById('previewWord');
        const previewIpa = document.getElementById('previewIpa');
        const previewMeaning = document.getElementById('previewMeaning');
        const previewMeta = document.getElementById('previewMeta');
        const previewExampleWrap = document.getElementById('previewExampleWrap');
        const previewExample = document.getElementById('previewExample');

        if (!word) {
            previewImage.innerHTML = '<span>A</span>';
            previewWord.textContent = 'Select a word';
            previewIpa.hidden = true;
            previewMeaning.textContent = 'Click a selected word to preview it.';
            previewMeta.hidden = true;
            previewExampleWrap.hidden = true;
            speakButton.disabled = true;
            return;
        }

        previewImage.innerHTML = word.image
            ? `<img src="${escapeHtml(word.image)}" alt="${escapeHtml(word.word)}">`
            : `<span>${escapeHtml([...word.word][0]?.toUpperCase() || 'A')}</span>`;
        previewWord.textContent = word.word || '';
        previewIpa.textContent = word.ipa || '';
        previewIpa.hidden = !word.ipa;
        previewMeaning.textContent = word.meaning || 'No Vietnamese meaning.';

        const meta = [word.partOfSpeech, word.topics, word.level].filter(Boolean);
        previewMeta.innerHTML = meta.map((item) => `<span>${escapeHtml(item)}</span>`).join('');
        previewMeta.hidden = meta.length === 0;

        previewExample.textContent = word.example || '';
        previewExampleWrap.hidden = !word.example;
        speakButton.disabled = !word.word;
    }

    function createSelectedItem(word, orderNo) {
        const element = document.createElement('div');
        element.className = 'fc-selected-item';
        element.draggable = true;
        element.dataset.wordId = String(word.id);
        element.dataset.wordJson = JSON.stringify(word);

        const image = word.image
            ? `<img src="${escapeHtml(word.image)}" alt="">`
            : escapeHtml([...word.word][0]?.toUpperCase() || 'A');
        const partOfSpeech = word.partOfSpeech ? `<em>${escapeHtml(word.partOfSpeech)}</em>` : '';

        element.innerHTML = `
            <span class="fc-drag" title="Drag to reorder">⋮⋮</span>
            <span class="fc-order">${Number(orderNo)}</span>
            <span class="fc-thumb fc-selected-thumb">${image}</span>
            <span class="fc-selected-copy">
                <strong>${escapeHtml(word.word)}</strong>
                <small>${escapeHtml(word.meaning)}</small>
                ${partOfSpeech}
            </span>
            <button type="button" data-remove-word="${Number(word.id)}" aria-label="Remove ${escapeHtml(word.word)}">×</button>`;
        bindSelectedItem(element);
        return element;
    }

    function bindSelectedItem(element) {
        element.addEventListener('click', (event) => {
            if (event.target.closest('[data-remove-word]')) return;
            selected.querySelectorAll('.fc-selected-item.is-active').forEach((item) => item.classList.remove('is-active'));
            element.classList.add('is-active');
            renderPreview(parseWord(element));
        });
        element.addEventListener('dragstart', () => {
            dragged = element;
            element.classList.add('dragging');
        });
        element.addEventListener('dragend', async () => {
            element.classList.remove('dragging');
            dragged = null;
            try {
                await saveOrder();
            } catch (error) {
                alert(error.message);
            }
        });
    }

    async function saveOrder() {
        setSaveState('Saving...', 'saving');
        await requestJson('ajax/reorder.php', {
            set_id: setId,
            word_ids: [...selected.querySelectorAll('.fc-selected-item')].map((item) => Number(item.dataset.wordId))
        }, true);
        setSaveState('Saved', 'saved');
    }

    function renderLibrary(items) {
        library.innerHTML = items.map((word) => {
            const image = word.image
                ? `<img src="${escapeHtml(word.image)}" alt="">`
                : escapeHtml([...word.word][0]?.toUpperCase() || 'A');
            const selectedClass = word.isSelected ? ' is-selected' : '';
            const disabled = word.isSelected ? ' disabled' : '';
            return `<button class="fc-word-item${selectedClass}" data-add-word="${Number(word.id)}"
                        data-word-json="${escapeHtml(JSON.stringify(word))}" type="button"${disabled}>
                        <span class="fc-thumb">${image}</span>
                        <span class="fc-word-copy"><strong>${escapeHtml(word.word)}</strong><small>${escapeHtml(word.meaning)}</small></span>
                        <span class="fc-add-label">${word.isSelected ? '✓ Added' : '+ Add'}</span>
                    </button>`;
        }).join('');
        document.getElementById('libraryCount').textContent = String(items.length);
        document.getElementById('summaryLibraryCount').textContent = String(items.length);
    }

    [...selected.querySelectorAll('.fc-selected-item')].forEach(bindSelectedItem);
    const firstSelected = selected.querySelector('.fc-selected-item');
    if (firstSelected) {
        firstSelected.classList.add('is-active');
        renderPreview(parseWord(firstSelected));
    } else {
        renderPreview(null);
    }

    selected.addEventListener('dragover', (event) => {
        event.preventDefault();
        if (!dragged) return;
        const after = [...selected.querySelectorAll('.fc-selected-item:not(.dragging)')]
            .find((item) => event.clientY <= item.getBoundingClientRect().top + item.offsetHeight / 2);
        selected.insertBefore(dragged, after || null);
        updateCounts();
    });

    library.addEventListener('click', async (event) => {
        const button = event.target.closest('[data-add-word]');
        if (!button || button.disabled) return;
        button.disabled = true;
        button.classList.add('is-loading');
        try {
            const result = await requestJson('ajax/add-word.php', {
                set_id: setId,
                word_id: button.dataset.addWord
            });
            if (!result.already_selected) {
                const item = createSelectedItem(result.word, result.order_no);
                selected.appendChild(item);
                selected.querySelectorAll('.fc-selected-item.is-active').forEach((row) => row.classList.remove('is-active'));
                item.classList.add('is-active');
                renderPreview(result.word);
            }
            setLibraryItemState(result.word.id, true);
            updateCounts();
            setSaveState('Saved', 'saved');
        } catch (error) {
            button.disabled = false;
            alert(error.message);
        } finally {
            button.classList.remove('is-loading');
        }
    });

    selected.addEventListener('click', async (event) => {
        const button = event.target.closest('[data-remove-word]');
        if (!button) return;
        if (!confirm('Remove this word?')) return;

        const item = button.closest('.fc-selected-item');
        const wordId = Number(item.dataset.wordId);
        button.disabled = true;
        try {
            await requestJson('ajax/remove-word.php', {set_id: setId, word_id: wordId});
            const wasActive = item.classList.contains('is-active');
            item.remove();
            setLibraryItemState(wordId, false);
            updateCounts();

            if (wasActive) {
                const next = selected.querySelector('.fc-selected-item');
                if (next) {
                    next.classList.add('is-active');
                    renderPreview(parseWord(next));
                } else {
                    renderPreview(null);
                }
            }
            setSaveState('Saved', 'saved');
        } catch (error) {
            button.disabled = false;
            alert(error.message);
        }
    });

    search.addEventListener('input', () => {
        clearTimeout(searchTimer);
        searchTimer = setTimeout(async () => {
            try {
                const result = await requestJson(`ajax/search.php?q=${encodeURIComponent(search.value)}&set_id=${setId}`);
                renderLibrary(result.items || []);
            } catch (error) {
                console.error(error);
            }
        }, 250);
    });

    function autosave() {
        setSaveState('Saving...', 'saving');
        clearTimeout(saveTimer);
        saveTimer = setTimeout(async () => {
            try {
                await requestJson('save.php', {
                    set_id: setId,
                    title: title.value,
                    description: description.value
                }, true);
                setSaveState('Saved', 'saved');
            } catch (error) {
                setSaveState('Save failed', 'error');
            }
        }, 500);
    }

    title.addEventListener('input', autosave);
    description.addEventListener('input', autosave);

    speakButton.addEventListener('click', () => {
        if (!currentWord?.word) return;
        if (currentWord.audio) {
            const audio = new Audio(currentWord.audio);
            audio.play().catch(playTts);
        } else {
            playTts();
        }

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