/**
 * SlideMind Slide Runtime
 *
 * External loader for e-learning integration snippets.
 * Loaded via <script> tag, exposes window.SlideMind.run(config).
 *
 * Auth flow: on first run, calls GET /bootstrap?configId=... to obtain
 * a wp_rest nonce + learnerId/sessionId. All subsequent /chat, /memory,
 * and /profile calls pass X-WP-Nonce.
 *
 * @package SlideMind
 * @since   0.4.0
 */
(function() {
    'use strict';

    // ── Authoring tool runtime adapters ─────────────────────
    var EDITORS = {
        storyline: {
            init: function() { return GetPlayer(); },
            getVar: function(p, n) { return p.GetVar(n); },
            setVar: function(p, n, v) { p.SetVar(n, v); }
        },
        captivate: {
            getVar: function(_, n) { return cpAPIInterface.getVariableValue(n); },
            setVar: function(_, n, v) { cpAPIInterface.setVariableValue(n, v); }
        },
        lectora: {
            getVar: function(_, n) { return window["VarCmd_" + n].getValue(); },
            setVar: function(_, n, v) { if (window["VarCmd_" + n]) window["VarCmd_" + n].set(v); }
        },
        dominknow: {
            getVar: function(_, n) { return parent.contentApi.getData(n); },
            setVar: function(_, n, v) { parent.contentApi.setData(n, v); }
        },
        ispring: {
            getVar: function(_, n) { return window[n]; },
            setVar: function(_, n, v) { window[n] = v; }
        }
    };

    // ── Bootstrap (synchronous) ─────────────────────────────
    // Returns { nonce, learnerId, sessionId, expiry } or null on failure.
    function bootstrap(baseUrl, configId) {
        // baseUrl already contains '?' with plain permalinks
        // (e.g. /index.php?rest_route=/slidemind/v2/). Use '&' when it does,
        // '?' otherwise, so configId lands as a real $_GET entry.
        var url = baseUrl + 'bootstrap';
        url += (url.indexOf('?') !== -1 ? '&' : '?')
            + 'configId=' + encodeURIComponent(configId);
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, false);
        xhr.withCredentials = true;
        try {
            xhr.send();
        } catch (e) {
            return null;
        }
        if (xhr.status !== 200) return null;
        try {
            return JSON.parse(xhr.responseText);
        } catch (e) {
            return null;
        }
    }

    // ── Memory singleton (nonce-authenticated) ──────────────
    function initMemory(baseUrl, configId, ctx) {
        if (!ctx || window.SM_Memory) return;
        var memUrl = baseUrl + 'memory/';
        var memId = ctx.learnerId
            ? { learnerId: ctx.learnerId }
            : { sessionId: ctx.sessionId, sessionToken: ctx.sessionToken };

        window.SM_Memory = {
            save: function(scope, variables) {
                var payload = Object.assign(
                    { configId: configId, scope: scope, variables: variables }, memId
                );
                return fetch(memUrl + 'set', {
                    method: 'POST',
                    credentials: 'include',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-WP-Nonce':   ctx.nonce
                    },
                    body: JSON.stringify(payload)
                }).then(function(r) { return r.json(); });
            },
            load: function(scope, keys) {
                var payload = Object.assign(
                    { configId: configId, scope: scope, keys: keys }, memId
                );
                return fetch(memUrl + 'get', {
                    method: 'POST',
                    credentials: 'include',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-WP-Nonce':   ctx.nonce
                    },
                    body: JSON.stringify(payload)
                }).then(function(r) { return r.json(); });
            }
        };
    }

    // ── Save memory variables (capture helper) ──────────────
    function saveMemoryVars(config, editorDef, player) {
        var vars = config.memoryVars;
        if (!vars || !vars.length || !window.SM_Memory) return;

        var byScope = {};
        for (var i = 0; i < vars.length; i++) {
            var v = vars[i];
            if (!v.name || !v.source) continue;
            var scope = v.scope || 'global';
            if (!byScope[scope]) byScope[scope] = {};
            byScope[scope][v.name] = editorDef.getVar(player, v.source);
        }

        for (var s in byScope) {
            if (byScope.hasOwnProperty(s)) {
                SM_Memory.save(s, byScope[s]);
            }
        }
    }

    // ── QA mode ─────────────────────────────────────────────
    function doChat(config, editorDef, player, ctx) {
        var userMessage = editorDef.getVar(player, config.inputVar || 'userMessage');

        var xhr = new XMLHttpRequest();
        xhr.open('POST', config.baseUrl + 'chat', true);
        xhr.withCredentials = true;
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.setRequestHeader('X-WP-Nonce', ctx.nonce);

        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            var outVar = config.outputVar || 'aiResponse';
            if (xhr.status === 200) {
                var r = JSON.parse(xhr.responseText);
                if (r.success) {
                    if (r.structured) {
                        for (var k in r.data) {
                            if (r.data.hasOwnProperty(k)) editorDef.setVar(player, k, r.data[k]);
                        }
                    } else {
                        editorDef.setVar(player, outVar, r.message.content);
                    }
                } else {
                    editorDef.setVar(player, outVar, 'Erreur: ' + (r.error || 'Erreur inconnue'));
                }
            } else {
                editorDef.setVar(player, outVar, 'Erreur de connexion');
            }
        };

        var chatPayload = {
            configId: config.configId,
            messages: [{ role: 'user', content: userMessage }],
            variables: {}
        };
        if (ctx.learnerId) chatPayload.learnerId = ctx.learnerId;
        if (ctx.sessionId) {
            chatPayload.sessionId = ctx.sessionId;
            chatPayload.sessionToken = ctx.sessionToken;
        }
        xhr.send(JSON.stringify(chatPayload));

        saveMemoryVars(config, editorDef, player);
    }

    // ── Capture mode ────────────────────────────────────────
    function doCapture(config, editorDef, player) {
        saveMemoryVars(config, editorDef, player);
    }

    // ── Retrieve / Profile mode ─────────────────────────────
    function doRetrieve(config, editorDef, player, ctx) {
        var vars = config.memoryVars;
        if (!vars || !vars.length) return;
        if (!ctx.learnerId && !ctx.sessionId) return;

        var isProfile = config.mode === 'profile';
        var endpoint  = isProfile ? 'profile/get' : 'memory/get';
        var memId = ctx.learnerId
            ? { learnerId: ctx.learnerId }
            : { sessionId: ctx.sessionId, sessionToken: ctx.sessionToken };

        var keys = [];
        for (var i = 0; i < vars.length; i++) {
            keys.push(vars[i].name);
        }

        var payload = Object.assign({ configId: config.configId, keys: keys }, memId);
        if (!isProfile) payload.scope = 'global';

        var xhr = new XMLHttpRequest();
        xhr.open('POST', config.baseUrl + endpoint, false);
        xhr.withCredentials = true;
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.setRequestHeader('X-WP-Nonce', ctx.nonce);
        xhr.send(JSON.stringify(payload));

        if (xhr.status === 200) {
            var r = JSON.parse(xhr.responseText);
            if (r.success) {
                for (var j = 0; j < vars.length; j++) {
                    var v = vars[j];
                    editorDef.setVar(player, v.source, r.data[v.name] || '');
                }
            }
        }
    }

    // ── Entry point ─────────────────────────────────────────
    window.SlideMind = {
        run: function(config) {
            var tool = config.authoringTool || config.editor;
            var editorDef = EDITORS[tool] || EDITORS.storyline;

            // Captivate: retry until cpAPIInterface is available (max 50 × 200ms = 10s)
            if (tool === 'captivate' && !window.cpAPIInterface) {
                config._retries = (config._retries || 0) + 1;
                if (config._retries <= 50) {
                    setTimeout(function() { SlideMind.run(config); }, 200);
                }
                return;
            }

            var player = editorDef.init ? editorDef.init() : null;

            // Bootstrap: obtain nonce + identity. Cached in window for subsequent slides.
            var ctx = window.SM_BootstrapCtx;
            if (!ctx || ctx.configId !== config.configId) {
                ctx = bootstrap(config.baseUrl, config.configId);
                if (!ctx) {
                    // Access denied or network error — bail gracefully.
                    return;
                }
                window.SM_BootstrapCtx = ctx;
            }

            initMemory(config.baseUrl, config.configId, ctx);

            switch (config.mode) {
                case 'qa':       doChat(config, editorDef, player, ctx); break;
                case 'capture':  doCapture(config, editorDef, player); break;
                case 'retrieve':
                case 'profile':  doRetrieve(config, editorDef, player, ctx); break;
            }
        }
    };
})();
