"use strict";
/**
 * Game Bridge
 * Wrapper đơn giản cho game developers để giao tiếp với SDK Iframe
 *
 * Usage:
 * ```typescript
 * import { GameBridge } from 'game-iframe-sdk/game-bridge';
 *
 * const bridge = new GameBridge({
 *     sdkIframeUrl: 'https://sdk.sena.tech/sdk-iframe.html',
 *     debug: true,
 * });
 *
 * // Init
 * await bridge.init({
 *     mode: 'preview',
 *     game_code: 'G001',
 * });
 *
 * // Listen for data
 * bridge.on('dataReady', (data) => {
 *     renderGame(data.items);
 * });
 *
 * // Check answer
 * bridge.checkAnswer('q1', userChoice).then(result => {
 *     showFeedback(result.correct);
 * });
 * ```
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameBridge = void 0;
exports.getGameBridge = getGameBridge;
exports.destroyGameBridge = destroyGameBridge;
const types_1 = require("../sdk-iframe/types");
// =============================================================================
// GAME BRIDGE
// =============================================================================
class GameBridge {
    constructor(config) {
        this.sdkIframe = null;
        this.sdkOrigin = '';
        this.isReady = false;
        // Event handlers
        this.handlers = new Map();
        // Pending requests (for promise-based API)
        this.pendingRequests = new Map();
        this.requestCounter = 0;
        this.config = {
            sdkIframeUrl: config.sdkIframeUrl,
            debug: config.debug ?? false,
            timeout: config.timeout ?? 10000,
        };
        // Extract origin from SDK URL
        try {
            const url = new URL(this.config.sdkIframeUrl);
            this.sdkOrigin = url.origin;
        }
        catch {
            this.sdkOrigin = '*';
        }
        this.setupMessageListener();
    }
    // ==========================================================================
    // PUBLIC API - Init
    // ==========================================================================
    /**
     * Create SDK Iframe and initialize
     */
    async init(payload) {
        // Create hidden iframe
        this.sdkIframe = document.createElement('iframe');
        this.sdkIframe.src = this.config.sdkIframeUrl;
        this.sdkIframe.style.cssText = 'position:absolute;width:0;height:0;border:0;visibility:hidden;';
        this.sdkIframe.setAttribute('aria-hidden', 'true');
        document.body.appendChild(this.sdkIframe);
        this.log('info', 'SDK Iframe created');
        // Wait for iframe to load
        await new Promise((resolve) => {
            this.sdkIframe.onload = () => resolve();
        });
        // Send init
        return this.sendRequest(types_1.SDK_MESSAGE_TYPES.SDK_INIT, payload);
    }
    /**
     * Push data (preview mode)
     */
    async pushData(payload) {
        return this.sendRequest(types_1.SDK_MESSAGE_TYPES.SDK_PUSH_DATA, payload);
    }
    // ==========================================================================
    // PUBLIC API - Game Actions
    // ==========================================================================
    /**
     * Check answer - returns local result immediately
     * Also triggers server sync in background
     */
    async checkAnswer(questionId, choice, timeSpent) {
        const payload = {
            question_id: questionId,
            choice,
            time_spent: timeSpent,
        };
        return this.sendRequest(types_1.SDK_MESSAGE_TYPES.SDK_CHECK_ANSWER, payload);
    }
    /**
     * Get final result
     */
    async getFinalResult() {
        return this.sendRequest(types_1.SDK_MESSAGE_TYPES.SDK_GET_RESULT, {});
    }
    /**
     * Retry sync for a question
     */
    async retrySync(questionId) {
        return this.sendRequest(types_1.SDK_MESSAGE_TYPES.SDK_RETRY_SYNC, {
            question_id: questionId,
        });
    }
    // ==========================================================================
    // PUBLIC API - Events
    // ==========================================================================
    /**
     * Subscribe to events
     */
    on(event, handler) {
        if (!this.handlers.has(event)) {
            this.handlers.set(event, new Set());
        }
        this.handlers.get(event).add(handler);
        return () => this.off(event, handler);
    }
    /**
     * Unsubscribe from events
     */
    off(event, handler) {
        this.handlers.get(event)?.delete(handler);
    }
    // ==========================================================================
    // PUBLIC API - State
    // ==========================================================================
    /**
     * Check if SDK is ready
     */
    isSdkReady() {
        return this.isReady;
    }
    /**
     * Destroy bridge and cleanup
     */
    destroy() {
        // Clear pending requests
        this.pendingRequests.forEach((pending) => {
            clearTimeout(pending.timeout);
            pending.reject(new Error('Bridge destroyed'));
        });
        this.pendingRequests.clear();
        // Remove iframe
        if (this.sdkIframe && this.sdkIframe.parentNode) {
            this.sdkIframe.parentNode.removeChild(this.sdkIframe);
        }
        this.sdkIframe = null;
        // Clear handlers
        this.handlers.clear();
        this.log('info', 'Bridge destroyed');
    }
    // ==========================================================================
    // INTERNAL - Message Handling
    // ==========================================================================
    setupMessageListener() {
        window.addEventListener('message', this.handleMessage.bind(this));
    }
    handleMessage(event) {
        // Validate origin (if not *)
        if (this.sdkOrigin !== '*' && event.origin !== this.sdkOrigin) {
            return;
        }
        const data = event.data;
        if (!(0, types_1.isSdkMessage)(data)) {
            return;
        }
        this.log('debug', `Received: ${data.type}`, data.payload);
        // Handle pending request response
        if (data.request_id && this.pendingRequests.has(data.request_id)) {
            const pending = this.pendingRequests.get(data.request_id);
            clearTimeout(pending.timeout);
            this.pendingRequests.delete(data.request_id);
            if (data.type === types_1.SDK_MESSAGE_TYPES.SDK_ERROR) {
                pending.reject(data.payload);
            }
            else {
                pending.resolve(data.payload);
            }
            return;
        }
        // Handle events
        switch (data.type) {
            case types_1.SDK_MESSAGE_TYPES.SDK_READY:
                this.isReady = true;
                this.emit('ready', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_DATA_READY:
                this.emit('dataReady', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_ANSWER_RESULT:
                this.emit('answerResult', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_SYNC_STATUS:
                this.emit('syncStatus', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_SYNC_ERROR:
                this.emit('syncError', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_FINAL_RESULT:
                this.emit('finalResult', data.payload);
                break;
            case types_1.SDK_MESSAGE_TYPES.SDK_ERROR:
                this.emit('error', data.payload);
                break;
        }
    }
    emit(event, data) {
        this.handlers.get(event)?.forEach(handler => {
            try {
                handler(data);
            }
            catch (err) {
                this.log('error', `Error in ${event} handler`, err);
            }
        });
    }
    // ==========================================================================
    // INTERNAL - Sending Messages
    // ==========================================================================
    sendRequest(type, payload) {
        return new Promise((resolve, reject) => {
            if (!this.sdkIframe?.contentWindow) {
                reject(new Error('SDK Iframe not ready'));
                return;
            }
            const requestId = `req_${++this.requestCounter}_${Date.now()}`;
            const message = (0, types_1.createSdkMessage)(type, payload, requestId);
            // Setup timeout
            const timeout = setTimeout(() => {
                this.pendingRequests.delete(requestId);
                reject(new Error(`Request timeout: ${type}`));
            }, this.config.timeout);
            // Store pending request
            this.pendingRequests.set(requestId, { resolve, reject, timeout });
            // Send message
            this.sdkIframe.contentWindow.postMessage(message, this.sdkOrigin);
            this.log('debug', `Sent: ${type}`, payload);
        });
    }
    log(level, message, data) {
        if (!this.config.debug && level === 'debug')
            return;
        const prefix = '[GameBridge]';
        switch (level) {
            case 'debug':
            case 'info':
                console.log(prefix, message, data ?? '');
                break;
            case 'warn':
                console.warn(prefix, message, data ?? '');
                break;
            case 'error':
                console.error(prefix, message, data ?? '');
                break;
        }
    }
}
exports.GameBridge = GameBridge;
// =============================================================================
// FACTORY
// =============================================================================
let bridgeInstance = null;
/**
 * Get or create GameBridge instance
 */
function getGameBridge(config) {
    if (!bridgeInstance && config) {
        bridgeInstance = new GameBridge(config);
    }
    if (!bridgeInstance) {
        throw new Error('GameBridge not initialized. Call with config first.');
    }
    return bridgeInstance;
}
/**
 * Destroy GameBridge instance
 */
function destroyGameBridge() {
    bridgeInstance?.destroy();
    bridgeInstance = null;
}
//# sourceMappingURL=GameBridge.js.map