/*
 * WebViewJavascriptBridge.js
 *
 * H5 端桥协议代码，与 Native BridgeBus（@MainActor BridgeProtocol 实现）
 * 一一对应。详见 docs/H5-Native-Implementation-Design.md §3.2 / §3.3。
 *
 * 在 BridgedWebView 构造时通过 WKUserScript（.atDocumentStart）注入到所有
 * 加载的 H5 页面。H5 业务代码运行时，window.WebViewJavascriptBridge 已就绪。
 *
 * 协议（Native ↔ H5 双向 JSON 消息）：
 *   - H5 → Native：window.webkit.messageHandlers.WVJBHandler.postMessage({...})
 *   - Native → H5：window.WebViewJavascriptBridge._handleMessageFromObjC('<base64>')
 *   - 单条消息：
 *       { handlerName, data?, callbackId? }     // 主动调用对端 handler
 *       { responseId, responseData? }            // 响应对端早前 callHandler 的 callbackId
 */

;(function () {
    if (window.WebViewJavascriptBridge) { return; }

    // ── 内部状态 ─────────────────────────────────────────────
    var messageHandlers = {};    // H5 注册的 handler：name → fn(data, responseCallback)
    var responseCallbacks = {};  // 等待 Native 响应的 JS 回调：callbackId → fn(responseData)
    var nextCallbackId = 1;

    // ── 对外 API ─────────────────────────────────────────────
    var bridge = window.WebViewJavascriptBridge = {

        /**
         * H5 注册 handler，等待 Native 主动 callHandler。重名覆盖。
         * @param {string} handlerName
         * @param {function(data, responseCallback)} handler
         */
        registerHandler: function (handlerName, handler) {
            messageHandlers[handlerName] = handler;
        },

        /**
         * H5 主动调 Native handler。
         * @param {string} handlerName
         * @param {*} data            任意 JSON 可序列化值
         * @param {function(responseData)} [responseCallback]
         */
        callHandler: function (handlerName, data, responseCallback) {
            var callbackId = null;
            if (typeof responseCallback === 'function') {
                callbackId = 'cb_' + (nextCallbackId++) + '_' + Date.now();
                responseCallbacks[callbackId] = responseCallback;
            }
            var message = { handlerName: handlerName };
            if (data !== undefined && data !== null) {
                message.data = data;
            }
            if (callbackId) {
                message.callbackId = callbackId;
            }
            _postMessageToNative(message);
        },

        /**
         * Native → H5 入口。BridgeBus.sendToJS 会注入：
         *   window.WebViewJavascriptBridge._handleMessageFromObjC('<base64of JSON>')
         * base64 包装是为了避免 JSON 内容里的单引号 / 反斜杠扰乱 evaluateJavaScript 的字符串字面量。
         */
        _handleMessageFromObjC: function (base64String) {
            try {
                var json = _decodeBase64UTF8(base64String);
                var message = JSON.parse(json);
                _dispatchFromNative(message);
            } catch (e) {
                console.error('[WVJB] handle message from native failed:', e);
            }
        }
    };

    // ── 内部 ─────────────────────────────────────────────────

    function _postMessageToNative(message) {
        try {
            window.webkit.messageHandlers.WVJBHandler.postMessage(message);
        } catch (e) {
            console.error('[WVJB] post to native failed (WVJBHandler 通道未注册?):', e);
        }
    }

    function _dispatchFromNative(message) {
        // Native 响应 H5 早前 callHandler
        if (message.responseId) {
            var cb = responseCallbacks[message.responseId];
            if (cb) {
                delete responseCallbacks[message.responseId];
                try { cb(message.responseData); }
                catch (e) { console.error('[WVJB] response callback threw:', e); }
            }
            return;
        }

        // Native 主动调用 H5 handler
        var handler = messageHandlers[message.handlerName];
        if (!handler) {
            console.warn('[WVJB] no H5 handler registered for:', message.handlerName);
            return;
        }

        var responseCallback;
        if (message.callbackId) {
            var responseId = message.callbackId;
            responseCallback = function (responseData) {
                _postMessageToNative({
                    responseId: responseId,
                    responseData: responseData
                });
            };
        }

        try { handler(message.data, responseCallback); }
        catch (e) { console.error('[WVJB] H5 handler "' + message.handlerName + '" threw:', e); }
    }

    /**
     * 标准 base64 → UTF-8 字符串。
     * 直接 atob() 只能处理 latin1；这里用 decodeURIComponent + escape 处理多字节字符。
     */
    function _decodeBase64UTF8(b64) {
        return decodeURIComponent(Array.prototype.map.call(atob(b64), function (c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }

    // ── marcuswestin 旧式握手兼容 ─────────────────────────────
    // 部分 H5 代码这样初始化 bridge：
    //   function setupWebViewJavascriptBridge(callback) {
    //       if (window.WebViewJavascriptBridge) return callback(WebViewJavascriptBridge);
    //       if (window.WVJBCallbacks) return window.WVJBCallbacks.push(callback);
    //       window.WVJBCallbacks = [callback];
    //       /* iframe trick - WKWebView 模式不需要 */
    //   }
    //   setupWebViewJavascriptBridge(function (bridge) { bridge.registerHandler(...) });
    //
    // 我们在 documentStart 就注入 bridge，正常路径下 H5 拿到的 window.WebViewJavascriptBridge
    // 已存在，会立即同步 callback(bridge)。但若 H5 代码本身用了 WVJBCallbacks 队列模式
    // (Array.push 在 bridge 之前)，需要在此处 flush 一次。
    if (Array.isArray(window.WVJBCallbacks)) {
        var pending = window.WVJBCallbacks;
        delete window.WVJBCallbacks;
        for (var i = 0; i < pending.length; i++) {
            try { pending[i](bridge); }
            catch (e) { console.error('[WVJB] pending WVJBCallback threw:', e); }
        }
    }
})();
