import { app, BrowserWindow, desktopCapturer, ipcMain, session, shell, protocol, globalShortcut } from "electron";
import { optimizer } from "@electron-toolkit/utils";
import path from "node:path"
import fs from "node:fs";
import { execSync, exec as execCallback } from "child_process";
import os from "node:os";
import { promisify } from "node:util";

/** process.js 必须位于非依赖项的顶部 */
import { isDummy } from "../lib/process";
import { AppEnv, AppRuntime } from "../mapi/env";
import { MAPI } from "../mapi/main";
import { initSystemTemplates } from "../mapi/db/initSystemTemplates";
import { initBuiltinAvatars } from "../mapi/db/initBuiltinAvatars";
import { initAppConfig, initVoicePreCache } from "../mapi/config/initAppConfig";
import { ConfigMain } from "../mapi/config/main";
import { setUpdaterWindow, default as updaterIndex } from "../mapi/updater/index";

import { WindowConfig } from "../config/window";
import { AppConfig } from "../../src/config";
import Log from "../mapi/log/main";
import { ConfigMenu } from "../config/menu";
import { ConfigLang, t } from "../config/lang";
import { ConfigContextMenu } from "../config/contextMenu";
import { preloadDefault, rendererLoadPath } from "../lib/env-main";
import { Page } from "../page";
import { ConfigTray } from "../config/tray";
import { icnsLogoPath, icoLogoPath, logoPath } from "../config/icon";
import { isDev, isMac, isPackaged, shouldEnableDevTools } from "../lib/env";
import { executeHooks } from "../lib/hooks";
import { DevToolsManager } from "../lib/devtools";
import { AppsMain } from "../mapi/app/main";
import { ServerMain } from "../mapi/server/main";
import CryptoManager from "../lib/crypto";
import AntiDebugManager from "../lib/antiDebug";
import { getCommonResourcePath, getModelPath } from "../lib/resource-path";
import { ensurePythonSetup, getMacOSPythonPath } from "../lib/python-setup";
import Store from "../lib/store";
import { checkResourceBundles } from "../mapi/resourceManager";
import { fetchElectronSystemConfig } from "../mapi/systemConfig";

// 窗口状态保存路径
const windowStateFile = () => path.join(app.getPath("userData"), "window-state.json");

// 加载窗口状态
function loadWindowState() {
    try {
        if (fs.existsSync(windowStateFile())) {
            const data = fs.readFileSync(windowStateFile(), "utf-8");
            return JSON.parse(data);
        }
    } catch (error) {
        Log.error("加载窗口状态失败:", error);
    }
    return null;
}

// 保存窗口状态
function saveWindowState(window: BrowserWindow) {
    try {
        const bounds = window.getBounds();
        const state = {
            width: bounds.width,
            height: bounds.height,
            x: bounds.x,
            y: bounds.y,
            isMaximized: window.isMaximized(),
        };
        fs.writeFileSync(windowStateFile(), JSON.stringify(state, null, 2));
    } catch (error) {
        Log.error("保存窗口状态失败:", error);
    }
}

const isDummyNew = isDummy;

// 临时屏蔽防破解，用于调试
// if (process.env["ELECTRON_ENV_PROD"]) {
//     DevToolsManager.setEnable(false);
// }

process.on("uncaughtException", reason => {
    let error: any = reason;
    if (error instanceof Error) {
        error = [error.message, error.stack].join("\n");
    }
    Log.error("UncaughtException", error);
});

process.on("unhandledRejection", reason => {
    Log.error("UnhandledRejection", reason);
    let error: any = reason;
    if (error instanceof Error) {
        error = [error.message, error.stack].join("\n");
    }
    Log.error("UnhandledRejection", error);
});

app.disableHardwareAcceleration();

// 注册自定义协议为特权协议（必须在 app.whenReady() 之前）
protocol.registerSchemesAsPrivileged([
    {
        scheme: 'font',
        privileges: {
            bypassCSP: true,
            supportFetchAPI: true,
            corsEnabled: true,
            stream: true
        }
    }
]);

// Set application name for Windows 10+ notifications
if (process.platform === "win32") app.setAppUserModelId(app.getName());

if (!app.requestSingleInstanceLock()) {
    app.quit();
    process.exit(0);
}

// 🔧 便携版（绿色版）检测和配置
// 便携版应该把数据存储在应用本地目录，而不是 AppData
const isPortable = (() => {
    // 🟢 默认启用便携模式：所有数据存储在安装目录
    console.log('[Portable Detection] 便携模式已默认启用');
    return true;

    /*
    // 原检测逻辑（已禁用）
    try {
        // 方法1: electron-builder 的便携版会设置这个环境变量
        if (process.env.PORTABLE_EXECUTABLE_DIR || process.env.PORTABLE_EXECUTABLE_FILE) {
            return true;
        }

        // 方法2: 检查可执行文件名是否包含 portable
        if (process.execPath && process.execPath.toLowerCase().includes('portable')) {
            return true;
        }

        // 方法3: 检查应用根目录是否存在 portable.txt 标记文件
        if (process.env.APP_ROOT) {
            const portableMarker = path.join(process.env.APP_ROOT, 'portable.txt');
            if (fs.existsSync(portableMarker)) {
                return true;
            }
        }
    } catch (error) {
        console.error('[Portable Detection] 检测失败:', error);
    }

    return false;
    */
})();

const resolveInstallRoot = () => {
    if (isDev) {
        return process.cwd();
    }
    return path.dirname(process.execPath);
};

const resolveResourceBundleRoot = (installRoot: string) => {
    if (!isDev) {
        const packagedBundleRoot = path.join(process.resourcesPath, 'resources-bundles');
        if (fs.existsSync(packagedBundleRoot)) {
            return packagedBundleRoot;
        }
    }
    return path.join(installRoot, 'resources-bundles');
};

const configurePortablePaths = () => {
    const installRoot = resolveInstallRoot();
    const dataRoot = path.join(installRoot, 'data');
    const userDataRoot = path.join(dataRoot, 'userData');
    const sessionDataRoot = path.join(dataRoot, 'session');
    const cacheRoot = path.join(dataRoot, 'cache');
    const tempRoot = path.join(dataRoot, 'temp');
    const resourceStateRoot = path.join(dataRoot, 'resource-state');
    const resourceBundleRoot = resolveResourceBundleRoot(installRoot);

    for (const dir of [dataRoot, userDataRoot, sessionDataRoot, cacheRoot, tempRoot, resourceStateRoot, resourceBundleRoot]) {
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
    }

    AppEnv.installRoot = installRoot;
    AppEnv.dataRoot = dataRoot;
    AppEnv.userData = userDataRoot;
    AppEnv.sessionData = sessionDataRoot;
    AppEnv.cacheRoot = cacheRoot;
    AppEnv.tempRoot = tempRoot;
    AppEnv.resourceStateRoot = resourceStateRoot;
    AppEnv.resourceBundleRoot = resourceBundleRoot;

    app.setPath('userData', userDataRoot);
    app.setPath('sessionData', sessionDataRoot);

    try {
        app.setPath('temp', tempRoot);
    } catch (error) {
        console.warn('[Portable Mode] temp 路径设置失败:', error);
    }

    try {
        app.setPath('cache', cacheRoot);
    } catch (error) {
        console.warn('[Portable Mode] cache 路径设置失败:', error);
    }

    process.env.TMPDIR = tempRoot;
    process.env.TEMP = tempRoot;
    process.env.TMP = tempRoot;
    process.env.PLAYWRIGHT_BROWSERS_PATH = resourceBundleRoot;

    console.log('[Portable Mode] 🟢 便携版模式已启用');
    console.log('[Portable Mode] 安装目录:', installRoot);
    console.log('[Portable Mode] 用户数据目录:', userDataRoot);
    console.log('[Portable Mode] 资源目录:', resourceBundleRoot);
};

if (isPortable) {
    try {
        configurePortablePaths();
    } catch (error) {
        console.error('[Portable Mode] 设置失败:', error);
    }
}

const hasSplashWindow = false;

// 🔧 初始化 AppEnv - 确保所有路径都有效（支持中文路径）
try {
    // 多重降级方案确保 appRoot 总是有效
    let appRoot = process.env.APP_ROOT;

    if (!appRoot || typeof appRoot !== 'string') {
        // 🔧 修复：process.resourcesPath 是 resources 目录本身
        // 如果 AppEnv.appRoot 意为应用根目录（包含 resources 的目录），则需要取父目录
        appRoot = path.dirname(process.resourcesPath);
        console.log('[AppEnv] 使用 resourcesPath 的父目录:', appRoot);
    }

    if (!appRoot || typeof appRoot !== 'string') {
        appRoot = app.getAppPath();
        console.log('[AppEnv] 使用 getAppPath:', appRoot);
    }

    if (!appRoot || typeof appRoot !== 'string') {
        // 最终降级：使用可执行文件目录
        appRoot = path.dirname(process.execPath);
        console.log('[AppEnv] 使用 execPath dirname:', appRoot);
    }

    AppEnv.appRoot = appRoot;
    console.log('[AppEnv] ✅ appRoot 已设置:', AppEnv.appRoot);

} catch (error) {
    console.error('[AppEnv] 设置 appRoot 失败:', error);
    AppEnv.appRoot = path.dirname(process.execPath);
}

try {
    AppEnv.appData = app.getPath("appData");
    AppEnv.userData = AppEnv.userData || app.getPath("userData");
    AppEnv.sessionData = AppEnv.sessionData || app.getPath("sessionData");

    console.log('[AppEnv] ✅ userData 已设置:', AppEnv.userData);

    if (!AppEnv.installRoot) {
        AppEnv.installRoot = resolveInstallRoot();
    }

    if (!AppEnv.dataRoot) {
        if (AppEnv.userData && typeof AppEnv.userData === 'string') {
            if (AppEnv.userData.endsWith('/userData') || AppEnv.userData.endsWith('\\userData')) {
                AppEnv.dataRoot = path.dirname(AppEnv.userData);
            } else if (AppEnv.userData.endsWith('/data') || AppEnv.userData.endsWith('\\data')) {
                AppEnv.dataRoot = AppEnv.userData;
            } else {
                AppEnv.dataRoot = path.join(AppEnv.userData, "data");
            }
        } else {
            AppEnv.dataRoot = path.join(app.getPath("temp"), "zhenqianba-data");
            console.warn('[AppEnv] userData 不可用，使用临时目录:', AppEnv.dataRoot);
        }
    }

    AppEnv.cacheRoot = AppEnv.cacheRoot || path.join(AppEnv.dataRoot, 'cache');
    AppEnv.tempRoot = AppEnv.tempRoot || path.join(AppEnv.dataRoot, 'temp');
    AppEnv.resourceStateRoot = AppEnv.resourceStateRoot || path.join(AppEnv.dataRoot, 'resource-state');
    AppEnv.resourceBundleRoot = AppEnv.resourceBundleRoot || resolveResourceBundleRoot(AppEnv.installRoot);

    console.log('[AppEnv] ✅ dataRoot 已设置:', AppEnv.dataRoot);

} catch (error) {
    console.error('[AppEnv] 设置路径失败:', error);
    throw error;
}

for (const dir of [
    AppEnv.dataRoot,
    path.join(AppEnv.dataRoot, "logs"),
    path.join(AppEnv.dataRoot, "storage"),
    AppEnv.cacheRoot,
    AppEnv.tempRoot,
    AppEnv.resourceStateRoot,
    AppEnv.resourceBundleRoot,
    AppEnv.sessionData,
]) {
    if (dir && !fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
}

if (isPackaged) {
    checkResourceBundles({ installMissing: false }).then((result) => {
        if (result.pending.length > 0) {
            Log.warn('[ResourceManager] bundles pending', result.pending);
        }
        if (result.installed.length > 0) {
            Log.info('[ResourceManager] bundles installed', result.installed);
        }
        if (result.missingRequired.length > 0) {
            Log.warn('[ResourceManager] required bundles missing', result.missingRequired);
        }
    }).catch((error) => {
        Log.warn('[ResourceManager] initial check failed', error);
    });
}

AppEnv.isInit = true;

// 🔧 设置 U2NET_HOME 环境变量，指向打包的模型目录
// ⚠️ 只使用程序自带的 U2-Net 模型，不依赖用户目录下载
const modelsPath = getModelPath('u2net.onnx');
process.env.U2NET_HOME = path.dirname(modelsPath);
Log.info("U2NET_HOME 设置完成", { U2NET_HOME: process.env.U2NET_HOME });

ConfigContextMenu.init();

Log.info("Starting");
Log.info("LaunchInfo", {
    isPackaged,
    userData: AppEnv.userData,
    dataRoot: AppEnv.dataRoot,
});

async function createWindow() {
    let icon = logoPath;
    if (process.platform === "win32") {
        icon = icoLogoPath;
    } else if (process.platform === "darwin") {
        icon = icnsLogoPath;
    }
    if (hasSplashWindow) {
        AppRuntime.splashWindow = new BrowserWindow({
            title: AppConfig.title,
            width: 600,
            height: 350,
            transparent: true,
            frame: false,
            alwaysOnTop: true,
            hasShadow: true,
            skipTaskbar: true,
        });
        rendererLoadPath(AppRuntime.splashWindow, "splash.html");
    }

    // 加载保存的窗口状态
    const savedState = loadWindowState();
    const windowOptions: any = {
        show: !hasSplashWindow,
        title: AppConfig.title,
        ...(!isPackaged ? { icon } : {}),
        frame: false,
        transparent: false,
        hasShadow: true,
        center: !savedState, // 如果有保存的状态，不居中
        minWidth: WindowConfig.minWidth,
        minHeight: WindowConfig.minHeight,
        width: savedState?.width || WindowConfig.initWidth,
        height: savedState?.height || WindowConfig.initHeight,
        backgroundColor: await AppsMain.defaultDarkModeBackgroundColor(),
        titleBarStyle: "hidden",
        trafficLightPosition: { x: 10, y: 11 },
        webPreferences: {
            preload: preloadDefault,
            // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
            nodeIntegration: true,
            webSecurity: false,
            webviewTag: true,
            // Consider using contextBridge.exposeInMainWorld
            // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
            contextIsolation: false,
            // sandbox: false,
        },
    };

    // 如果有保存的位置，设置窗口位置
    if (savedState?.x !== undefined && savedState?.y !== undefined) {
        windowOptions.x = savedState.x;
        windowOptions.y = savedState.y;
    }

    AppRuntime.mainWindow = new BrowserWindow(windowOptions);
    setUpdaterWindow(AppRuntime.mainWindow);

    // 如果之前是最大化状态，恢复最大化
    if (savedState?.isMaximized) {
        AppRuntime.mainWindow.maximize();
    }

    // 监听窗口大小和位置变化，保存状态
    let saveTimeout: NodeJS.Timeout | null = null;
    const scheduleSave = () => {
        if (saveTimeout) {
            clearTimeout(saveTimeout);
        }
        saveTimeout = setTimeout(() => {
            if (AppRuntime.mainWindow && !AppRuntime.mainWindow.isDestroyed()) {
                saveWindowState(AppRuntime.mainWindow);
            }
        }, 500); // 延迟500ms保存，避免频繁写入
    };

    AppRuntime.mainWindow.on("resize", scheduleSave);
    AppRuntime.mainWindow.on("move", scheduleSave);
    AppRuntime.mainWindow.on("maximize", scheduleSave);
    AppRuntime.mainWindow.on("unmaximize", scheduleSave);

    AppRuntime.mainWindow.on("closed", () => {
        if (saveTimeout) {
            clearTimeout(saveTimeout);
        }
        AppRuntime.mainWindow = null;
    });
    AppRuntime.mainWindow.on("show", async () => {
        await executeHooks(AppRuntime.mainWindow, "Show");
    });
    AppRuntime.mainWindow.on("hide", async () => {
        await executeHooks(AppRuntime.mainWindow, "Hide");
    });

    if (isMac) {
        AppRuntime.mainWindow.on("close", event => {
            // @ts-ignore
            if (!app.quitForce && !isDev) {
                executeHooks(AppRuntime.mainWindow, "ShowQuitConfirmDialog");
                event.preventDefault();
            }
        });
    }
    AppRuntime.mainWindow.on("enter-full-screen", () => {
        executeHooks(AppRuntime.mainWindow, "EnterFullScreen");
    });
    AppRuntime.mainWindow.on("leave-full-screen", () => {
        executeHooks(AppRuntime.mainWindow, "LeaveFullScreen");
    });

    rendererLoadPath(AppRuntime.mainWindow, "index.html");
    DevToolsManager.register("Main", AppRuntime.mainWindow);

    AppRuntime.mainWindow.webContents.on("did-finish-load", async () => {
        try {
            const mapiCheck = await AppRuntime.mainWindow!.webContents.executeJavaScript(
                `JSON.stringify({ hasMapi: !!window['$mapi'], hasIpcRenderer: !!window['ipcRenderer'], hasPage: !!window['__page'], windowDollarKeys: Object.keys(window).filter(k => k.startsWith('$')) })`
            );
            console.log('[Main:did-finish-load] preload检查:', mapiCheck);
        } catch(e: any) {
            console.error('[Main:did-finish-load] preload检查失败:', e.message);
        }
        if (hasSplashWindow) {
            AppRuntime.mainWindow?.show();
            setTimeout(() => {
                try {
                    AppRuntime.splashWindow?.close();
                    AppRuntime.splashWindow = null;
                    // AppRuntime.mainWindow.webContents.openDevTools({
                    //     mode: 'detach',
                    // })
                } catch (e) { }
            }, 1000);
        }
        // 🔧 延迟调用 Page.ready，直到 MAPI 初始化完成
        // 等待 MAPI.init() 完成再通知前端
        console.log('[Main:did-finish-load] 页面加载完成，等待 MAPI 初始化...');

        // 给 MAPI 初始化一个机会完成
        // 如果 MAPI 已经初始化，这会立即执行
        // 否则等待最多 5 秒钟
        let mapiInitCheck = 0;
        const mapiCheckInterval = setInterval(() => {
            mapiInitCheck++;
            if (AppRuntime.mapiInitialized || mapiInitCheck > 50) { // 5 秒超时
                clearInterval(mapiCheckInterval);
                if (!AppRuntime.mapiInitialized) {
                    console.warn('[Main:did-finish-load] ⚠️ MAPI 初始化超时，但继续启动页面');
                }
                console.log('[Main:did-finish-load] 调用 Page.ready("main")');
                Page.ready("main");
                DevToolsManager.autoShow(AppRuntime.mainWindow);

                if (isPackaged) {
                    setTimeout(async () => {
                        try {
                            const checkAtLaunch = ConfigMain.get("updaterCheckAtLaunch", "yes");
                            console.log('[Main] auto check update, setting:', checkAtLaunch);
                            if (checkAtLaunch !== "no") {
                                await updaterIndex.checkForUpdate();
                            }
                        } catch (e: any) {
                            console.error('[Main] auto check update failed:', e.message);
                        }
                    }, 8000);
                }
            }
        }, 100);

        // 🔐 启用防破解保护（生产环境且非调试模式时强制启用）
        if (!shouldEnableDevTools || process.env.ELECTRON_ENABLE_ANTI_DEBUG === 'true') {
            console.log('[Main] 启用防破解保护...');
            AntiDebugManager.enableFullProtection(AppRuntime.mainWindow, {
                disableDevTools: true,
                disableShortcuts: true,
                disableContextMenu: true,
                disableMenuBar: !shouldEnableDevTools, // 调试模式保留菜单栏
                interceptHotkeys: true
            });
        } else {
            console.log('[Main] 调试模式：防破解保护已禁用');
            console.log('[Main] 提示：生产环境中设置环境变量 ELECTRON_DEBUG_MODE=true 可启用DevTools');
        }

        // 🆕 隐藏调试快捷键 Ctrl+Shift+Alt+D（即使在生产环境也能启用DevTools）
        globalShortcut.register('Ctrl+Shift+Alt+D', () => {
            console.log('[Main] 隐藏调试快捷键触发');
            if (AppRuntime.mainWindow && !AppRuntime.mainWindow.webContents.isDevToolsOpened()) {
                // 使用 AntiDebugManager 的 restoreDevTools 方法，绕过防护
                AntiDebugManager.restoreDevTools(AppRuntime.mainWindow);
                console.log('[Main] ✅ DevTools 已通过隐藏快捷键打开');
            } else {
                AppRuntime.mainWindow?.webContents.closeDevTools();
                console.log('[Main] DevTools 已关闭');
            }
        });
    });
    AppRuntime.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
        if (url.startsWith("https:")) shell.openExternal(url);
        return { action: "deny" };
    });
}

app.whenReady()
    .then(() => {
        // 注册自定义 font:// 协议
        protocol.registerFileProtocol('font', (request, callback) => {
            try {
                // 从 URL 中提取文件路径
                // font://path/to/font.ttf -> path/to/font.ttf
                const url = request.url.substring(7); // 移除 'font://'
                const fontPath = decodeURIComponent(url);

                Log.info('[FontProtocol] Loading font:', fontPath);

                // 验证文件存在
                if (!fs.existsSync(fontPath)) {
                    Log.error('[FontProtocol] Font file not found:', fontPath);
                    callback({ error: -6 }); // FILE_NOT_FOUND
                    return;
                }

                // 返回文件路径
                callback({ path: fontPath });
            } catch (error) {
                Log.error('[FontProtocol] Error loading font:', error);
                callback({ error: -2 }); // FAILED
            }
        });

        session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
            desktopCapturer.getSources({ types: ["screen"] }).then(sources => {
                // Grant access to the first screen found.
                callback({ video: sources[0], audio: "loopback" });
            });
        });
    })
    .then(ConfigLang.readyAsync)
    .then(async () => {
        // 初始化加密管理器
        try {
            console.log("[Main:init] 初始化加密管理器...");
            await CryptoManager.initializeKeys();
            console.log("[Main:init] ✅ 加密管理器初始化完成");
        } catch (error) {
            console.error("[Main:init] 加密管理器初始化失败:", error);
            Log.error("加密管理器初始化失败:", error);
        }

        // 在创建窗口前先初始化MAPI（包括数据库）
        try {
            console.log("[Main:init] Starting MAPI initialization");
            console.log("[Main:init] AppEnv.isInit =", AppEnv.isInit);
            console.log("[Main:init] AppEnv.dataRoot =", AppEnv.dataRoot);
            console.log("[Main:init] AppEnv.userData =", AppEnv.userData);

            await MAPI.init();

            console.log("[Main:init] MAPI initialized successfully");
            Log.info("MAPI initialized successfully");

            // 🔧 标记 MAPI 已初始化，前端可以开始使用 API
            AppRuntime.mapiInitialized = true;
            console.log('[Main:init] 设置 AppRuntime.mapiInitialized = true');

            // 初始化系统默认模板
            console.log("[Main:init] Initializing system templates...");
            await initSystemTemplates();
            console.log("[Main:init] System templates initialized");

            console.log("[Main:init] Initializing builtin avatars...");
            await initBuiltinAvatars();
            console.log("[Main:init] Builtin avatars initialized");

            // 初始化应用配置（字幕模板等）
            console.log("[Main:init] Initializing app config...");
            await initAppConfig();
            console.log("[Main:init] App config initialized");

            console.log("[Main:init] Pre-fetching system config...");
            try {
                await fetchElectronSystemConfig();
                console.log("[Main:init] System config fetched");
            } catch(e: any) {
                console.warn("[Main:init] System config fetch failed:", e.message);
            }

            console.log("[Main:init] Initializing voice preview cache...");
            await initVoicePreCache();
            console.log("[Main:init] Voice preview cache initialized");

            if (process.platform !== 'win32') {
                console.log("[Main:init] Starting macOS/Linux Python setup (background)...");
                ensurePythonSetup().then((pythonPath) => {
                    if (pythonPath) {
                        console.log("[Main:init] ✅ Python setup complete:", pythonPath);
                    } else {
                        console.warn("[Main:init] Python setup returned null");
                    }
                }).catch((err) => {
                    console.warn("[Main:init] Python setup failed (non-fatal):", err.message);
                });
            }
        } catch (error) {
            console.error("[Main:init] MAPI initialization error:", error);
            Log.error("MAPI initialization failed:", error);
            // 🔧 即使失败也标记为已尝试初始化，允许前端继续（为了诊断）
            AppRuntime.mapiInitialized = true;
            // 不抛出错误，允许应用继续（为了诊断）
        }
    })
    .then(() => {
        MAPI.ready();
        ConfigMenu.ready();
        ConfigTray.ready();

        // 🔐 加密相关 IPC 处理器
        ipcMain.handle("Crypto:getPublicKey", () => {
            try {
                return { success: true, publicKey: CryptoManager.getPublicKey() };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:encryptRSA", (event, data: string) => {
            try {
                return { success: true, encrypted: CryptoManager.encryptRSA(data) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:decryptRSA", (event, encryptedData: string) => {
            try {
                return { success: true, decrypted: CryptoManager.decryptRSA(encryptedData) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:encryptAES", (event, data: string, key: string) => {
            try {
                return { success: true, encrypted: CryptoManager.encryptAES(data, key) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:decryptAES", (event, encryptedData: string, key: string) => {
            try {
                return { success: true, decrypted: CryptoManager.decryptAES(encryptedData, key) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:sha256", (event, data: string) => {
            try {
                return { success: true, hash: CryptoManager.sha256(data) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:generateRandomKey", (event, length: number = 32) => {
            try {
                return { success: true, key: CryptoManager.generateRandomKey(length) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:sign", (event, data: string) => {
            try {
                return { success: true, signature: CryptoManager.sign(data) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        ipcMain.handle("Crypto:verify", (event, data: string, signature: string) => {
            try {
                return { success: true, valid: CryptoManager.verify(data, signature) };
            } catch (error: any) {
                return { success: false, error: error.message };
            }
        });

        // 🔧 System 相关 IPC 处理器 - 获取硬件信息用于设备指纹
        const execAsync = promisify(execCallback);

        ipcMain.handle("System:getSystemInfo", async () => {
            try {
                console.log('[System:getSystemInfo] IPC 被调用');

                // 获取磁盘序列号（Windows）
                let diskSerial = 'unknown';
                try {
                    if (process.platform === 'win32') {
                        console.log('[System:getSystemInfo] 执行 wmic diskdrive 命令...');
                        const { stdout } = await execAsync('wmic diskdrive get SerialNumber');
                        console.log('[System:getSystemInfo] wmic 输出:', stdout);
                        const lines = stdout.trim().split('\n');
                        if (lines.length > 1) {
                            diskSerial = lines[1].trim();
                            console.log('[System:getSystemInfo] 获得的 diskSerial:', diskSerial);
                        }
                    } else if (process.platform === 'darwin') {
                        const { stdout } = await execAsync('ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformSerialNumber');
                        const match = stdout.match(/"IOPlatformSerialNumber"\s*=\s*"([^"]+)"/);
                        if (match) {
                            diskSerial = match[1];
                        }
                    }
                } catch (e) {
                    console.warn('[System:getSystemInfo] 获取磁盘序列号失败:', e);
                    Log.warn('[System:getSystemInfo] 获取磁盘序列号失败:', e);
                }

                const systemInfo = {
                    hostname: os.hostname(),
                    platform: os.platform(),
                    arch: os.arch(),
                    cpuModel: os.cpus()[0]?.model || 'unknown',
                    diskSerial
                };

                console.log('[System:getSystemInfo] 返回系统信息:', systemInfo);
                return systemInfo;
            } catch (error: any) {
                console.error("[System:getSystemInfo] 失败:", error);
                Log.error("[System:getSystemInfo] 失败:", error);
                return null;
            }
        });

        ipcMain.handle("System:getHardwareSerial", async () => {
            try {
                console.log('[System:getHardwareSerial] IPC 被调用');

                const hardwareSerial: any = {
                    motherboard: 'unknown',
                    bios: 'unknown'
                };

                if (process.platform === 'win32') {
                    // 获取主板序列号
                    try {
                        console.log('[System:getHardwareSerial] 执行 wmic baseboard 命令...');
                        const { stdout: mbStdout } = await execAsync('wmic baseboard get SerialNumber');
                        console.log('[System:getHardwareSerial] baseboard 输出:', mbStdout);
                        const mbLines = mbStdout.trim().split('\n');
                        if (mbLines.length > 1) {
                            hardwareSerial.motherboard = mbLines[1].trim();
                        }
                    } catch (e) {
                        console.warn('[System:getHardwareSerial] 获取主板序列号失败:', e);
                        Log.warn('[System:getHardwareSerial] 获取主板序列号失败:', e);
                    }

                    // 获取BIOS序列号
                    try {
                        console.log('[System:getHardwareSerial] 执行 wmic bios 命令...');
                        const { stdout: biosStdout } = await execAsync('wmic bios get SerialNumber');
                        console.log('[System:getHardwareSerial] bios 输出:', biosStdout);
                        const biosLines = biosStdout.trim().split('\n');
                        if (biosLines.length > 1) {
                            hardwareSerial.bios = biosLines[1].trim();
                        }
                    } catch (e) {
                        console.warn('[System:getHardwareSerial] 获取BIOS序列号失败:', e);
                        Log.warn('[System:getHardwareSerial] 获取BIOS序列号失败:', e);
                    }
                } else if (process.platform === 'darwin') {
                    try {
                        const { stdout } = await execAsync('ioreg -rd1 -c IOPlatformExpertDevice');
                        const serialMatch = stdout.match(/"IOPlatformSerialNumber"\s*=\s*"([^"]+)"/);
                        if (serialMatch) {
                            hardwareSerial.motherboard = serialMatch[1];
                            hardwareSerial.bios = serialMatch[1];
                        }
                    } catch (e) {
                        console.warn('[System:getHardwareSerial] macOS 获取序列号失败:', e);
                    }
                }

                console.log('[System:getHardwareSerial] 返回硬件序列号:', hardwareSerial);
                return hardwareSerial;
            } catch (error: any) {
                console.error("[System:getHardwareSerial] 失败:", error);
                Log.error("[System:getHardwareSerial] 失败:", error);
                return null;
            }
        });


        // 🔧 IPC处理器：杀死模型进程
        ipcMain.handle("killAllModelProcesses", async (event, data: any) => {
            try {
                let enableGpuCleanup = false;
                try {
                    const raw = ConfigMain.getSync('ipAgentSettings');
                    if (raw) {
                        const s = typeof raw === 'string' ? JSON.parse(raw) : raw;
                        enableGpuCleanup = !!s.enableGpuCleanup;
                    }
                } catch {}
                if (!enableGpuCleanup) {
                    Log.info("[IPC] 本地模型进程优化未开启，跳过进程清理");
                    return { success: true, message: "未开启，跳过" };
                }

                const isWin = process.platform === 'win32';
                const defaultProcs = isWin
                    ? ["launcher.exe", "python.exe", "ffmpeg.exe", "ffprobe.exe"]
                    : ["python3", "ffmpeg", "ffprobe"];
                const processes = data?.processes || defaultProcs;
                const protectedCommands = ['api_server.py', 'LstmSync'];

                Log.info(`[IPC] 开始杀死进程: ${processes.join(", ")}`);

                for (const proc of processes) {
                    try {
                        if (isWin) {
                            if (proc === 'python.exe') {
                                const output = execSync(`powershell -Command "Get-CimInstance Win32_Process -Filter \\"Name = '${proc}'\\" | Select-Object ProcessId, CommandLine | ConvertTo-Json"`, {
                                    encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore']
                                });
                                let procs = [];
                                try { procs = JSON.parse(output); if (!Array.isArray(procs)) procs = [procs]; } catch { procs = []; }
                                for (const p of procs) {
                                    if (!p || !p.ProcessId) continue;
                                    const cmd = (p.CommandLine || '').toLowerCase();
                                    if (protectedCommands.some(kw => cmd.includes(kw.toLowerCase()))) {
                                        Log.info(`[IPC] 🛡️ 跳过保护进程 PID ${p.ProcessId}: ${cmd.substring(0, 100)}`);
                                        continue;
                                    }
                                    try {
                                        execSync(`taskkill /f /pid ${p.ProcessId} /t`, { stdio: 'ignore', shell: 'cmd.exe' });
                                        Log.info(`[IPC] ✓ 已杀死: ${proc} PID ${p.ProcessId}`);
                                    } catch {}
                                }
                            } else {
                                execSync(`taskkill /f /im ${proc} /t`, { stdio: 'ignore', shell: 'cmd.exe' });
                                Log.info(`[IPC] ✓ 已杀死: ${proc}`);
                            }
                        } else {
                            execSync(`pkill -f "${proc}" 2>/dev/null || true`, { stdio: 'ignore', shell: '/bin/bash' });
                            Log.info(`[IPC] ✓ 已杀死: ${proc}`);
                        }
                    } catch (err: any) {
                        Log.debug(`[IPC] ${proc} 不存在或已关闭`);
                    }
                }

                Log.info("[IPC] ✓ 进程清理完成");
                return { success: true, message: "进程已杀死" };
            } catch (error: any) {
                Log.error("[IPC] 杀死进程失败:", error.message);
                return { success: false, error: error.message };
            }
        });

        // 🔧 IPC处理器：手动触发GPU清理（用于测试）
        ipcMain.handle("testGPUCleanup", async (event) => {
            try {
                console.log("[TestGPUCleanup] ========== 手动触发GPU清理 ==========");

                // 检查 GPU 进程
                let gpuProcs = '';
                try {
                    gpuProcs = execSync(`nvidia-smi --query-compute-apps=pid --format=csv,noheader`, {
                        encoding: 'utf-8',
                        stdio: ['pipe', 'pipe', 'ignore']
                    }).trim();
                    console.log(`[TestGPUCleanup] GPU 进程: ${gpuProcs}`);
                } catch (e) {
                    console.log(`[TestGPUCleanup] 没有GPU进程或nvidia-smi失败`);
                }

                if (gpuProcs) {
                    const pidLines = gpuProcs.split('\n');
                    const pids = pidLines.map(p => p.trim()).filter(p => p && !isNaN(Number(p)));

                    console.log(`[TestGPUCleanup] 找到 ${pids.length} 个GPU进程`);

                    const targetServers = ['indextts', 'heygem'];
                    const targetPids: string[] = [];

                    for (const pid of pids) {
                        try {
                            const cmdline = execSync(
                                `powershell -Command "Get-CimInstance Win32_Process -Filter \\"ProcessId = ${pid}\\" | Select-Object -ExpandProperty CommandLine"`,
                                { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }
                            ).toLowerCase();

                            if (targetServers.some(server => cmdline.includes(server))) {
                                console.log(`[TestGPUCleanup] 🎯 发现目标进程 PID ${pid}`);
                                targetPids.push(pid);
                            }
                        } catch (e) {
                            console.log(`[TestGPUCleanup] 检查PID ${pid} 失败`);
                        }
                    }

                    if (targetPids.length > 0) {
                        const pidList = targetPids.join(',');
                        console.log(`[TestGPUCleanup] 🔨 清理 ${targetPids.length} 个进程: ${pidList}`);

                        execSync(
                            `powershell -Command "Stop-Process -Id ${pidList} -Force -ErrorAction SilentlyContinue"`,
                            { stdio: 'ignore' }
                        );

                        console.log(`[TestGPUCleanup] ✓ 已清理 ${targetPids.length} 个进程`);
                    }
                }

                // 调用清理脚本
                try {
                    const appRoot = process.env.APP_ROOT;
                    if (appRoot) {
                        const cleanupScript = path.join(appRoot, 'scripts', 'cleanup_gpu.py');
                        if (fs.existsSync(cleanupScript)) {
                            const pythonExe = process.platform === 'win32' ? 'python.exe' : 'python3';
                            const pythonPath = path.join(getCommonResourcePath('python'), pythonExe);
                            if (fs.existsSync(pythonPath)) {
                                const result = execSync(`"${pythonPath}" "${cleanupScript}"`, {
                                    encoding: 'utf-8',
                                    timeout: 10000,
                                    stdio: ['pipe', 'pipe', 'ignore']
                                });
                                console.log(`[TestGPUCleanup] ✓ GPU显存清理完成`);
                                if (result) {
                                    console.log(`[cleanup_gpu.py] ${result}`);
                                }
                            }
                        }
                    }
                } catch (e) {
                    console.warn(`[TestGPUCleanup] GPU清理脚本失败: ${e}`);
                }

                console.log("[TestGPUCleanup] ========== GPU清理完成 ==========");
                return { success: true, message: "GPU清理完成，请查看控制台输出" };
            } catch (error: any) {
                console.error("[TestGPUCleanup] 错误:", error);
                return { success: false, error: error.message };
            }
        });

        // 添加智能体登录窗口打开处理器
        ipcMain.handle("Agent.OpenLoginWindow", async (event, options) => {
            try {
                const region = options.region || '07';

                await Page.open("agentLogin", {
                    region: region,
                    onGetQRCode: async (region: string) => {
                        // 这里调用智能体登录二维码生成
                        const result = await MAPI.invoke("Agent.Login", "getQRCode", { region });
                        return result;
                    },
                    onCheckStatus: async (region: string) => {
                        // 这里检查登录状态
                        const result = await MAPI.invoke("Agent.Login", "checkStatus", { region });
                        return result;
                    },
                    onClose: () => {
                        console.log("智能体登录窗口已关闭");
                    }
                });

                return { success: true };
            } catch (error) {
                Log.error("打开智能体登录窗口失败:", error);
                return { success: false, error: error.message };
            }
        });

        app.on("browser-window-created", (_, window) => {
            optimizer.watchWindowShortcuts(window);
        });
        createWindow().then();
    });

// 标记应用是否正在退出
let isQuitting = false;

app.on("before-quit", async (event) => {
    // 更新时立即退出，不执行清理（清理已在 quitAndInstall 中完成）
    if ((global as any).__isUpdating) {
        console.log("[AppShutdown] 更新模式，跳过清理，立即退出");
        Log.info("[AppShutdown] 更新模式，跳过清理，立即退出");
        return;
    }

    // 防止多次触发
    if (!isQuitting) {
        event.preventDefault();
        isQuitting = true;

        console.log("[AppShutdown] 应用关闭，开始清理资源...");
        Log.info("[AppShutdown] 应用关闭，开始清理资源...");

        // 设置清理超时（最多8秒，给安装程序留缓冲时间）
        const cleanupTimeout = setTimeout(() => {
            console.log("[AppShutdown] ⚠️ 清理超时，强制退出");
            Log.info("[AppShutdown] 清理超时，强制退出");
            process.exit(0);
        }, 8000);

        try {
            // 1. 停止所有运行中的服务器（最多3秒）
            console.log("[AppShutdown] 停止所有服务器...");
            await Promise.race([
                ServerMain.stopAllServers(),
                new Promise(resolve => setTimeout(resolve, 3000))
            ]);
            console.log("[AppShutdown] ✓ 服务器已停止");

            // 2. 杀死残留的模型进程（Windows）- 总是清理，确保更新/卸载时干净
            if (process.platform === "win32") {
                const processesToKill = ["launcher.exe", "python.exe", "ffmpeg.exe", "ffprobe.exe"];
                const protectedCommands = ['api_server.py', 'LstmSync'];
                console.log("[AppShutdown] 清理残留进程...");

                for (const proc of processesToKill) {
                    try {
                        if (proc === 'python.exe') {
                            // 使用超时保护，避免 PowerShell 查询卡住
                            const output = execSync(`powershell -Command "$ErrorActionPreference='SilentlyContinue'; Get-CimInstance Win32_Process -Filter \\"Name = '${proc}'\\" | Select-Object ProcessId, CommandLine | ConvertTo-Json"`, {
                                encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'], timeout: 2000
                            });
                            let procs = [];
                            try { procs = JSON.parse(output); if (!Array.isArray(procs)) procs = [procs]; } catch { procs = []; }
                            for (const p of procs) {
                                if (!p || !p.ProcessId) continue;
                                const cmd = (p.CommandLine || '').toLowerCase();
                                if (protectedCommands.some(kw => cmd.includes(kw.toLowerCase()))) {
                                    console.log(`[AppShutdown] 🛡️ 跳过保护进程 PID ${p.ProcessId}`);
                                    continue;
                                }
                                try {
                                    execSync(`taskkill /f /pid ${p.ProcessId} /t`, { stdio: 'ignore', shell: 'cmd.exe', timeout: 1000 });
                                    console.log(`[AppShutdown] ✓ 已杀死: ${proc} PID ${p.ProcessId}`);
                                } catch {}
                            }
                        } else {
                            execSync(`taskkill /f /im ${proc} /t`, { stdio: 'ignore', shell: 'cmd.exe', timeout: 1000 });
                            console.log(`[AppShutdown] ✓ 已杀死: ${proc}`);
                        }
                    } catch (err: any) {
                    }
                }
                console.log("[AppShutdown] ✓ 残留进程清理完成");
            }
        } catch (error) {
            console.error("[AppShutdown] 清理过程出错:", error);
            Log.error("[AppShutdown] 清理过程出错:", error);
        } finally {
            clearTimeout(cleanupTimeout);
        }

        console.log("[AppShutdown] ✓ 资源清理完成，退出应用");
        Log.info("[AppShutdown] ✓ 资源清理完成，退出应用");

        // 退出应用
        app.quit();
    }
});

app.on("will-quit", () => {
    // 应用即将退出，不执行清理操作
    // 操作系统会自动回收所有资源
});

app.on("window-all-closed", () => {
    if (process.platform !== "darwin") app.quit();
});

app.on("second-instance", () => {
    if (AppRuntime.mainWindow) {
        if (AppRuntime.mainWindow.isMinimized()) {
            AppRuntime.mainWindow.restore();
        }
        AppRuntime.mainWindow.show();
        AppRuntime.mainWindow.focus();
    }
});

app.on("activate", () => {
    const allWindows = BrowserWindow.getAllWindows();
    if (allWindows.length) {
        if (!AppRuntime.mainWindow.isVisible()) {
            AppRuntime.mainWindow.show();
        }
        AppRuntime.mainWindow.focus();
    } else {
        createWindow().then();
    }
});
