"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBrowserEnvironment = createBrowserEnvironment;
exports.initDashboardDesignConfigure = initDashboardDesignConfigure;
exports.initDashboardCustomPoolEnv = initDashboardCustomPoolEnv;
exports.loadDependencies = loadDependencies;
const jsdom_1 = require("jsdom");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const vm = __importStar(require("vm"));
/**
 * 设置模拟的 Web 端运行环境
 */
async function createBrowserEnvironment(options) {
    const html = options.html || `<!DOCTYPE html><html><body>
  <script>
      window.BI = window.BI || {};
      BI.fineServletURL = "/webroot/decision";
  </script>
 <div id="app"></div>
 </body></html>`;
    // 用于捕获控制台输出
    const virtualConsole = new jsdom_1.VirtualConsole();
    virtualConsole.sendTo(console);
    const dom = new jsdom_1.JSDOM(html, {
        url: options.baseUrl || 'http://localhost/',
        runScripts: 'dangerously',
        resources: 'usable',
        virtualConsole
    });
    const { window } = dom;
    // 拦截并模拟 Canvas API，防止在没装 canvas 依赖时抛出 Not implemented 错误
    if (typeof window.HTMLCanvasElement !== 'undefined') {
        window.HTMLCanvasElement.prototype.getContext = function () {
            return {
                fillRect: () => { },
                clearRect: () => { },
                getImageData: (x, y, w, h) => ({ data: new Array(w * h * 4) }),
                putImageData: () => { },
                createImageData: () => [],
                setTransform: () => { },
                drawImage: () => { },
                save: () => { },
                fillText: () => { },
                restore: () => { },
                beginPath: () => { },
                moveTo: () => { },
                lineTo: () => { },
                closePath: () => { },
                stroke: () => { },
                translate: () => { },
                scale: () => { },
                rotate: () => { },
                arc: () => { },
                fill: () => { },
                measureText: () => ({ width: 10 }), // 随便给个默认宽度防止后续数学运算出错
                transform: () => { },
                rect: () => { },
                clip: () => { },
            };
        };
    }
    // 注入全量变量以便某些 script 依赖
    // 使用 Object.defineProperty 防止较新 Node 版本中 global.navigator 是 readonly 导致报错
    ['window', 'document', 'navigator', 'location'].forEach(prop => {
        Object.defineProperty(global, prop, {
            value: window[prop],
            writable: true,
            configurable: true
        });
    });
    // ========== 1. 提取并注入环境变配置 ==========
    const dashboardEnv = await initDashboardDesignConfigure(options.dashboardId, options.finebiServerUrl);
    console.log(`[FineBI SDK] 仪表板环境变量提取完成。`);
    // 获取 custom pool 缓存数据
    const customPoolData = await initDashboardCustomPoolEnv(options.dashboardId, options.finebiServerUrl);
    console.log(`[FineBI SDK] 仪表板 Custom Pool 数据获取完成。`);
    // 将提取的属性全部挂载到模拟的 window.BI 以及 window.Dec 上
    window.BI = Object.assign(window.BI || {}, dashboardEnv.BI);
    // 注入 pool 变量
    window.BI.pool = customPoolData;
    // 映射到 global，方便直接访问全局变量
    global.BI = window.BI;
    // ========== 2. 加载指定的后续依赖包 ==========
    if (options.scripts && options.scripts.length > 0) {
        await loadDependencies(window, options.scripts, options.finebiServerUrl);
    }
    return {
        window,
        document: window.document,
        dom
    };
}
/**
 * 初始化仪表板配置环境
 * 访问仪表板 view 页面，提取 body 下的第一个 script 标签内的内容并解析出 BI 属性
 */
async function initDashboardDesignConfigure(dashboardId, serverUrl) {
    const url = `${serverUrl}/webroot/decision/link/035I`;
    console.log(`[FineBI SDK] 正在获取仪表板页面: ${url}`);
    const res = await fetch(url);
    if (!res.ok) {
        throw new Error(`请求仪表板页面失败: ${res.status} ${res.statusText}`);
    }
    const htmlText = await res.text();
    // 1. 使用 JSDOM 解析 HTML 并找到带有 BI 配置的 script 标签
    const dom = new jsdom_1.JSDOM(htmlText);
    // 不局限于 body，也扫描 head 中的 script
    const inlineScripts = dom.window.document.querySelectorAll('script:not([src])');
    let targetScriptContent = '';
    // 考虑到实际获取的可能是 window.Dec 或 window.BI，我们把所有 inline 脚本内容收集起来
    for (const s of inlineScripts) {
        if (s.textContent) {
            targetScriptContent += s.textContent + '\n';
        }
    }
    if (!targetScriptContent) {
        throw new Error('未能在返回的页面中找到内嵌的 script 标签');
    }
    const scriptContent = targetScriptContent;
    // 2. 为安全地提取并解析所有的 JSON 对象赋值操作，我们在纯净的 vm 沙盒中直接执行这段 js
    // 使用 vm 代替 JSDOM 防止由于某些宿主 API 缺失导致的静默执行失败
    const sandbox = {
        window: {},
        document: { title: '' },
        console: console,
    };
    sandbox.BI = sandbox.window.BI = {};
    sandbox.Dec = sandbox.window.Dec = {};
    const context = vm.createContext(sandbox);
    try {
        const script = new vm.Script(scriptContent);
        script.runInContext(context);
    }
    catch (err) {
        console.error('[FineBI SDK] 执行脚本提取环境变量时发生异常:', err);
    }
    // 实际系统版本差异，有的注入的是 window.BI，有的是 window.Dec
    const biProperties = Object.keys(sandbox.window.BI || {}).length > 0
        ? sandbox.window.BI
        : (sandbox.window.Dec || {});
    console.log(`[FineBI SDK] 成功提取到 BI/Dec 属性，包含 keys: ${Object.keys(biProperties).join(', ')}`);
    return {
        rawScript: scriptContent, // 原始 JS 文本
        BI: biProperties // 解析出来的对象集合
    };
}
/**
 * 获取仪表板customPool
 * 访问 /v5/cache/report/pool/custom 接口，取回数据塞入 BI.pool
 */
async function initDashboardCustomPoolEnv(dashboardId, serverUrl) {
    const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJBVGFkbWluIiwidGVuYW50SWQiOiJkZWZhdWx0IiwiaXNzIjoiZmFucnVhbiIsImRlc2NyaXB0aW9uIjoiQVRhZG1pbihBVGFkbWluKSIsImV4cCI6MTc3NjM5MjgzNCwiaWF0IjoxNzc2Mzg5MjM0LCJqdGkiOiJsMStEQU1sRTU4LzRWYUk0c0NLd1JpVkI5WC9IeTl6OXpTQWZpUDc2bGhNcFFvTjcifQ.h-tvOl_Yiu3MhocB8H7lrPKr9TF-PSJ8iH6VkOR3sig';
    const url = `${serverUrl}/webroot/decision/v5/design/report/pool/custom?reportId=${dashboardId}&entryType=1&fine_auth_token=${token}`;
    console.log(`[FineBI SDK] 正在获取仪表板Pool: ${url}`);
    const res = await fetch(url);
    if (!res.ok) {
        throw new Error(`请求 custom pool 失败: ${res.status} ${res.statusText}`);
    }
    // 这个接口一般直接返回 JSON
    const data = await res.json();
    return data.data;
}
/**
 * 加载指定的后续依赖包
 * 下载或者读取本地的依赖 js 并挂载到给定的 window 环境上
 */
async function loadDependencies(window, scripts, serverUrl) {
    console.log(`[FineBI SDK] 开始加载 ${scripts.length} 个 JS 依赖文件...`);
    for (const scriptPath of scripts) {
        try {
            let code = '';
            const isUrl = scriptPath.startsWith('http://') || scriptPath.startsWith('https://') || scriptPath.startsWith('//');
            const isServerConfigured = serverUrl && (serverUrl.startsWith('http://') || serverUrl.startsWith('https://'));
            if (isUrl) {
                const targetUrl = scriptPath.startsWith('//') ? `http:${scriptPath}` : scriptPath;
                console.log(`[FineBI SDK] 正在拉取在线脚本: ${targetUrl}`);
                const res = await fetch(targetUrl);
                if (!res.ok)
                    throw new Error(`HTTP error! status: ${res.status}`);
                code = await res.text();
                console.log(`[FineBI SDK] 加载成功: ${targetUrl}`);
            }
            else if (isServerConfigured) {
                // 如果给了在线基准地址，则拼接 URL
                const targetUrl = new URL(scriptPath, serverUrl).href;
                console.log(`[FineBI SDK] 正在拉取拼接脚本: ${targetUrl}`);
                const res = await fetch(targetUrl);
                if (!res.ok)
                    throw new Error(`HTTP error! status: ${res.status}`);
                code = await res.text();
                console.log(`[FineBI SDK] 加载成功: ${targetUrl}`);
            }
            else {
                // 保留作为本地路径计算的兼容
                const basePath = serverUrl || '';
                const fullPath = basePath ? path.resolve(basePath, scriptPath) : path.resolve(scriptPath);
                console.log(`[FineBI SDK] 正在读取本地脚本: ${fullPath}`);
                if (fs.existsSync(fullPath)) {
                    code = fs.readFileSync(fullPath, 'utf-8');
                    console.log(`[FineBI SDK] 读取成功: ${fullPath}`);
                }
                else {
                    console.warn(`[Warning] Local script not found: ${fullPath}`);
                    continue;
                }
            }
            const scriptEl = window.document.createElement('script');
            scriptEl.textContent = code;
            window.document.head.appendChild(scriptEl);
        }
        catch (error) {
            console.warn(`[Warning] Failed to load script ${scriptPath}:`, error);
        }
    }
    console.log(`[FineBI SDK] 所有依赖加载并挂载完毕。`);
}
