"use strict";
/**
 * GameDataHandler - Data Sanitizer & Scorer
 *
 * Game Codes:
 * - G001: Quiz text-text
 * - G002: Quiz audio-text (audio question, text answer)
 * - G003: Quiz text-audio (text question, audio answer)
 * - G004: Quiz image-text (image question, text answer)
 * - G005: Quiz text-image (text question, image answer)
 *
 * - G110: Sequence Word - no audio
 * - G111: Sequence Word - có audio, missing_letter_count từ item
 * - G112: Sequence Word - có audio, missing_letter_count từ item
 * - G113: Sequence Word - có audio, missing_letter_count từ item
 *
 * - G120: Sequence Sentence - no audio
 * - G121: Sequence Sentence - có audio, missing_letter_count từ item
 * - G122: Sequence Sentence - có audio, missing_letter_count từ item
 * - G123: Sequence Sentence - có audio, missing_letter_count từ item
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.GAME_CODES = void 0;
exports.getGameCategory = getGameCategory;
exports.sanitizeForClient = sanitizeForClient;
exports.checkAnswer = checkAnswer;
// Game code metadata
exports.GAME_CODES = {
    // Quiz
    G001: { name: 'Quiz Text-Text', category: 'quiz', hasAudio: false, hasImage: false },
    G002: { name: 'Quiz Audio-Text', category: 'quiz', hasAudio: true, hasImage: false },
    G003: { name: 'Quiz Text-Audio', category: 'quiz', hasAudio: true, hasImage: false },
    G004: { name: 'Quiz Image-Text', category: 'quiz', hasAudio: false, hasImage: true },
    G005: { name: 'Quiz Text-Image', category: 'quiz', hasAudio: false, hasImage: true },
    // Sequence Word
    G110: { name: 'Sequence Word', category: 'sequence_word', hasAudio: false },
    G111: { name: 'Sequence Word Audio', category: 'sequence_word', hasAudio: true },
    G112: { name: 'Sequence Word Audio', category: 'sequence_word', hasAudio: true },
    G113: { name: 'Sequence Word Audio', category: 'sequence_word', hasAudio: true },
    // Sequence Sentence
    G120: { name: 'Sequence Sentence', category: 'sequence_sentence', hasAudio: false },
    G121: { name: 'Sequence Sentence Audio', category: 'sequence_sentence', hasAudio: true },
    G122: { name: 'Sequence Sentence Audio', category: 'sequence_sentence', hasAudio: true },
    G123: { name: 'Sequence Sentence Audio', category: 'sequence_sentence', hasAudio: true },
};
function getGameCategory(code) {
    return exports.GAME_CODES[code]?.category || 'unknown';
}
// =============================================================================
// SANITIZE DATA FOR CLIENT (REMOVE ANSWERS)
// =============================================================================
/**
 * Sanitize game data before sending to iframe
 * CRITICAL: Never send answers/correct data to client
 */
function sanitizeForClient(code, items) {
    if (!Array.isArray(items))
        return [];
    switch (code) {
        // ===== QUIZ VARIANTS =====
        case 'G001': // Quiz text-text
            return sanitizeQuizTextText(items);
        case 'G002': // Quiz audio-text
            return sanitizeQuizAudioText(items);
        case 'G003': // Quiz text-audio
            return sanitizeQuizTextAudio(items);
        case 'G004': // Quiz image-text
            return sanitizeQuizImageText(items);
        case 'G005': // Quiz text-image
            return sanitizeQuizTextImage(items);
        // ===== SEQUENCE WORD VARIANTS =====
        case 'G110': // Sequence word
            return sanitizeSequenceWord(items);
        case 'G111': // Sequence word
            return sanitizeSequenceWord(items);
        case 'G112': // Sequence word
            return sanitizeSequenceWord(items);
        case 'G113': // Sequence word
            return sanitizeSequenceWord(items);
        // ===== SEQUENCE SENTENCE VARIANTS =====
        case 'G120': // Sequence sentence
            return sanitizeSequenceSentence(items);
        case 'G121': // Sequence sentence
            return sanitizeSequenceSentence(items);
        case 'G122': // Sequence sentence
            return sanitizeSequenceSentence(items);
        case 'G123': // Sequence sentence
            return sanitizeSequenceSentence(items);
        default:
            console.warn(`[GameDataHandler] Unknown game code: ${code}, returning raw data`);
            return items;
    }
}
// =============================================================================
// QUIZ SANITIZERS
// =============================================================================
/**
 * G001: Quiz Text-Text
 * Client receives: id, question, options (shuffled)
 * Client does NOT receive: answer
 */
function sanitizeQuizTextText(items) {
    return items.map(item => {
        // Normalize options to {text: string}
        const options = (item.options || []).map((o) => {
            if (typeof o === 'string') {
                return { text: o };
            }
            if (o && typeof o === 'object') {
                return { text: String(o.text ?? '') };
            }
            return { text: String(o ?? '') };
        });
        // Shuffle to hide answer position
        shuffleArray(options);
        // Save shuffled text order for SDK to resolve index
        const shuffledTexts = options.map((o) => String(o.text ?? ''));
        return {
            id: item.id,
            question: item.question,
            options: options,
            __shuffledOptions: shuffledTexts, // SDK internal
        };
    });
}
/**
 * G002: Quiz Audio-Text
 * Client receives: id, question (audio URL), options (shuffled)
 * Client does NOT receive: answer
 */
function sanitizeQuizAudioText(items) {
    return items.map(item => {
        const options = (item.options || []).map((o) => {
            if (typeof o === 'string') {
                return { text: o };
            }
            if (o && typeof o === 'object') {
                return { text: String(o.text ?? '') };
            }
            return { text: String(o ?? '') };
        });
        shuffleArray(options);
        const shuffledTexts = options.map((o) => String(o.text ?? ''));
        return {
            id: item.id,
            question: item.audio || item.audio_url,
            options: options,
            __shuffledOptions: shuffledTexts,
        };
    });
}
/**
 * G003: Quiz Text-Audio
 * Client receives: id, question (text), options (audio URLs shuffled)
 * Client does NOT receive: answer
 */
function sanitizeQuizTextAudio(items) {
    return items.map(item => {
        const options = (item.options || []).map((o) => {
            if (typeof o === 'string') {
                return { audio: o };
            }
            if (o && typeof o === 'object') {
                const audioUrl = o.audio || o.audio_url || '';
                return { audio: String(audioUrl) };
            }
            return { audio: String(o ?? '') };
        });
        shuffleArray(options);
        const shuffledAudios = options.map((o) => String(o.audio ?? ''));
        return {
            id: item.id,
            question: item.question,
            options: options,
            __shuffledOptions: shuffledAudios,
        };
    });
}
/**
 * G004: Quiz Image-Text
 * Client receives: id, image_url, question (hint), options (shuffled)
 * Client does NOT receive: answer
 */
function sanitizeQuizImageText(items) {
    return items.map(item => {
        const options = (item.options || []).map((o) => {
            if (typeof o === 'string') {
                return { text: o };
            }
            if (o && typeof o === 'object') {
                return { text: String(o.text ?? '') };
            }
            return { text: String(o ?? '') };
        });
        shuffleArray(options);
        const shuffledTexts = options.map((o) => String(o.text ?? ''));
        return {
            id: item.id,
            image_url: item.image_url,
            question: item.question,
            options: options,
            __shuffledOptions: shuffledTexts,
        };
    });
}
/**
 * G005: Quiz Text-Image
 * Client receives: id, question (text), options (image URLs shuffled)
 * Client does NOT receive: answer
 */
function sanitizeQuizTextImage(items) {
    return items.map(item => {
        const options = (item.options || []).map((o) => {
            if (typeof o === 'string') {
                return { image_url: o };
            }
            if (o && typeof o === 'object') {
                const imageUrl = o.image_url || o.image || '';
                return { image_url: String(imageUrl) };
            }
            return { image_url: String(o ?? '') };
        });
        shuffleArray(options);
        const shuffledUrls = options.map((o) => String(o.image_url ?? ''));
        return {
            id: item.id,
            question: item.question,
            options: options, // Each option has {image_url: ...}
            __shuffledOptions: shuffledUrls,
        };
    });
}
// =============================================================================
// SEQUENCE WORD SANITIZERS
// =============================================================================
/**
 * Sequence Word (G110-G113)
 * Client receives: id, question (array with blanks), options (missing letters shuffled), audio_url (optional)
 * Client does NOT receive: word, parts, answer, missing_letter_count
 *
 * Logic:
 * 1. Read missing_letter_count from item (count of letters to blank out)
 * 2. Randomly select positions to blank
 * 3. question: array with blanks at selected positions
 * 4. options: extracted missing letters (shuffled)
 */
function sanitizeSequenceWord(items) {
    return items.map(item => {
        const parts = item.answer || item.parts || [];
        const missingCount = item.missing_letter_count || 0;
        if (missingCount === 0 || parts.length === 0) {
            // No missing - all visible
            return {
                id: item.id,
                question: [...parts],
                options: [],
                ...(item.audio_url && { audio_url: item.audio_url })
            };
        }
        // Randomly select which positions to blank
        const allIndices = Array.from({ length: parts.length }, (_, i) => i);
        const blankIndices = new Set();
        const count = Math.min(missingCount, parts.length);
        while (blankIndices.size < count) {
            const randomIdx = Math.floor(Math.random() * allIndices.length);
            const actualIdx = allIndices[randomIdx];
            blankIndices.add(actualIdx);
            allIndices.splice(randomIdx, 1);
        }
        // Build question array with blanks at random positions
        const question = parts.map((p, i) => blankIndices.has(i) ? "" : String(p));
        // Extract missing letters and shuffle
        const missingLetters = Array.from(blankIndices).map(i => String(parts[i]));
        shuffleArray(missingLetters);
        const result = {
            id: item.id,
            question, // e.g. ["H", "", "L", "", "O"]
            options: missingLetters, // e.g. ["L", "E"] - shuffled
            __shuffledOptions: [...missingLetters] // SDK internal: to resolve indices
        };
        if (item.audio_url) {
            result.audio_url = item.audio_url;
        }
        // CRITICAL: Do NOT send word, parts, answer, missing_letter_count
        return result;
    });
}
// =============================================================================
// SEQUENCE SENTENCE SANITIZERS
// =============================================================================
/**
 * Sequence Sentence (G120-G123)
 * Client receives: id, question (array with blanks), options (missing words shuffled), audio_url (optional)
 * Client does NOT receive: sentence, parts, answer, missing_letter_count
 *
 * Logic: Same as Sequence Word
 * 1. Read missing_letter_count from item
 * 2. Randomly select positions to blank
 * 3. question: array with blanks
 * 4. options: extracted missing words (shuffled)
 */
function sanitizeSequenceSentence(items) {
    return items.map(item => {
        const parts = item.answer || item.parts || [];
        const missingCount = item.missing_letter_count || 0;
        if (missingCount === 0 || parts.length === 0) {
            // No missing - all visible
            return {
                id: item.id,
                question: [...parts],
                options: [],
                ...(item.audio_url && { audio_url: item.audio_url })
            };
        }
        // Randomly select which positions to blank
        const allIndices = Array.from({ length: parts.length }, (_, i) => i);
        const blankIndices = new Set();
        const count = Math.min(missingCount, parts.length);
        while (blankIndices.size < count) {
            const randomIdx = Math.floor(Math.random() * allIndices.length);
            const actualIdx = allIndices[randomIdx];
            blankIndices.add(actualIdx);
            allIndices.splice(randomIdx, 1);
        }
        // Build question array with blanks at random positions
        const question = parts.map((p, i) => blankIndices.has(i) ? "" : String(p));
        // Extract missing words and shuffle
        const missingWords = Array.from(blankIndices).map(i => String(parts[i]));
        shuffleArray(missingWords);
        const result = {
            id: item.id,
            question, // e.g. ["I", "", "reading", ""]
            options: missingWords, // e.g. ["love", "books"] - shuffled
            __shuffledOptions: [...missingWords] // SDK internal
        };
        if (item.audio_url) {
            result.audio_url = item.audio_url;
        }
        // CRITICAL: Do NOT send sentence, parts, answer, missing_letter_count
        return result;
    });
}
/**
 * Check if player's answer is correct
 *
 * @param code - Game code (G001, G110, etc.)
 * @param originalItem - Original item from server (has answer field!)
 * @param playerAnswer - Player's answer (text for quiz, array for sequence)
 */
function checkAnswer(code, originalItem, playerAnswer) {
    switch (code) {
        // ===== QUIZ VARIANTS (G001-G005) =====
        case 'G001': // Quiz Text-Text
        case 'G002': // Quiz Audio-Text
        case 'G003': // Quiz Text-Audio
        case 'G004': // Quiz Image-Text
        case 'G005': // Quiz Text-Image
            return checkQuizAnswer(originalItem, playerAnswer);
        // ===== SEQUENCE WORD VARIANTS (G110-G113) =====
        case 'G110': // Sequence Word
        case 'G111': // Sequence Word Audio
        case 'G112': // Sequence Word Audio
        case 'G113': // Sequence Word Audio
            return checkSequenceAnswer(originalItem, playerAnswer);
        // ===== SEQUENCE SENTENCE VARIANTS (G120-G123) =====
        case 'G120': // Sequence Sentence
        case 'G121': // Sequence Sentence Audio
        case 'G122': // Sequence Sentence Audio
        case 'G123': // Sequence Sentence Audio
            return checkSequenceAnswer(originalItem, playerAnswer);
        default:
            console.warn(`[GameDataHandler] Unknown game code for scoring: ${code}`);
            return { isCorrect: false, score: 0 };
    }
}
// Quiz Scoring
function checkQuizAnswer(item, answerChoice) {
    const correctAnswer = String(item.answer || '');
    if (!correctAnswer) {
        return { isCorrect: false, score: 0, feedback: 'No correct answer defined' };
    }
    let playerAnswerText;
    if (typeof answerChoice === 'number') {
        // Index: resolve from original options
        if (Array.isArray(item.options)) {
            const v = item.options[answerChoice];
            if (typeof v === 'string') {
                playerAnswerText = v;
            }
            else if (v && typeof v === 'object' && 'text' in v) {
                playerAnswerText = String(v.text ?? '');
            }
            else {
                playerAnswerText = String(v ?? '');
            }
        }
        else {
            return { isCorrect: false, score: 0, feedback: 'Invalid question options' };
        }
    }
    else {
        // Direct text answer
        if (answerChoice && typeof answerChoice === 'object' && 'text' in answerChoice) {
            playerAnswerText = String(answerChoice.text ?? '');
        }
        else {
            playerAnswerText = String(answerChoice ?? '');
        }
    }
    const isCorrect = playerAnswerText.toLowerCase().trim() === correctAnswer.toLowerCase().trim();
    return {
        isCorrect,
        score: isCorrect ? 1 : 0,
        feedback: isCorrect
            ? `✅ Correct! "${playerAnswerText}" matches answer "${correctAnswer}"`
            : `❌ Wrong. You chose "${playerAnswerText}" but correct answer is "${correctAnswer}"`
    };
}
// Sequence Scoring
function checkSequenceAnswer(item, answer) {
    const correctOrder = item.answer || item.parts;
    if (!Array.isArray(answer) || !Array.isArray(correctOrder)) {
        return { isCorrect: false, score: 0 };
    }
    const isCorrect = arraysEqual(answer, correctOrder);
    return {
        isCorrect,
        score: isCorrect ? 1 : 0,
    };
}
// =============================================================================
// UTILITIES
// =============================================================================
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}
function arraysEqual(a, b) {
    if (a.length !== b.length)
        return false;
    return a.every((val, idx) => {
        if (typeof val === 'string' && typeof b[idx] === 'string') {
            return val.toLowerCase().trim() === b[idx].toLowerCase().trim();
        }
        return val === b[idx];
    });
}
//# sourceMappingURL=GameDataHandler.js.map