import { ipcMain, WebContents } from "electron";
import { spawn, ChildProcess } from "node:child_process";
import fs from "node:fs";
import { promises as fsp } from "node:fs";
import os from "node:os";
import path from "node:path";
import pino from "pino";

import { ChannelPayload } from "../../typedefinition";
import axios from "axios";
import { findExecutable, SUPER_USER_COMMAND_CANDIDATES } from "./superuser";

const logger = pino({ name: "install-manager" });

const getStoreFilterFromArgv = (): "spark" | "apm" | "both" => {
  const argv = process.argv;
  const noApm = argv.includes("--no-apm");
  const noSpark = argv.includes("--no-spark");

  if (noApm && noSpark) return "both";
  if (noApm) return "spark";
  if (noSpark) return "apm";
  return "both";
};

const isOriginEnabled = (
  storeFilter: "spark" | "apm" | "both",
  origin: "spark" | "apm",
): boolean => {
  return storeFilter === "both" || storeFilter === origin;
};

type InstallTask = {
  id: number;
  pkgname: string;
  execCommand: string;
  execParams: string[];
  download_process: ChildProcess | null;
  install_process: ChildProcess | null;
  webContents: WebContents | null;
  downloadDir?: string;
  metalinkUrl?: string;
  filename?: string;
  origin: "spark" | "apm";
  upgradeOnly?: boolean;
  cancelled?: boolean;
  phase: "queued-download" | "downloading" | "queued-install" | "installing";
};

const SHELL_CALLER_PATH = "/opt/spark-store/extras/shell-caller.sh";

// 以下路径配置参考自index.ts并且与其保持一致
// 其中，SPARK_CONFIG_DIR为配置目录，若此目录下出现ssshell-config-do-not-create-desktop文件
// 则代表「关闭『自动创建桌面启动器』功能」
const SPARK_CONFIG_DIR = path.join(
  os.homedir(),
  ".config/spark-union/spark-store",
);
const CREATE_DESKTOP_CONFIG = "ssshell-config-do-not-create-desktop";
const CREATE_DESKTOP_CONFIG_PATH = path.join(
  SPARK_CONFIG_DIR,
  CREATE_DESKTOP_CONFIG,
);

// APM应用的.desktop文件可能在以下几个位置
const APM_DESKTOP_ENTRY_DIRS = [
  "/var/lib/apm", // 实体机/宿主系统
  "/var/lib/apm/apm/files/ace-env/var/lib/apm", // ACE容器
];

// Helper: 用XDG_DESKTOP_DIR拿桌面路径，读不到我就回退到~/Desktop
const resolveDesktopDir = async (): Promise<string> => {
  const userDirsPath = path.join(os.homedir(), ".config", "user-dirs.dirs");

  try {
    const content = await fsp.readFile(userDirsPath, "utf-8");
    const matchRes = content.match(/^XDG_DESKTOP_DIR="\$HOME\/(.+)"$/m);
    if (matchRes?.[1]) {
      return path.join(os.homedir(), matchRes[1]);
    }
  } catch {
    // user-dirs.dirs无法读取
    logger.warn(
      `Failed to get XDG_DESKTOP_DIR, I'm falling back to ~/Desktop!!`,
    );
  }
  return path.join(os.homedir(), "Desktop");
};

// Helper: 为APM安装的应用创建桌面快捷方式（如果启用了「自动创建桌面启动器」）
const createApmDesktopShortcut = async (
  pkgname: string,
  sendLog: (msg: string) => void,
) => {
  // 如上所述，配置目录里面有ssshell-config-do-not-create-desktop文件就是功能关闭
  try {
    await fsp.access(CREATE_DESKTOP_CONFIG_PATH);
    logger.debug(
      `Desktop shortcut creation has been disabled. Skipping creating it for ${pkgname}.`,
    );
    return;
  } catch {
    // 文件不存在就是功能启用 继续
  }

  // 解析桌面路径，确保目录存在
  const desktopDir = await resolveDesktopDir();
  try {
    await fsp.mkdir(desktopDir, { recursive: true });
  } catch (err) {
    logger.warn(`Failed to create desktop directory ${desktopDir}: ${err}`);
    return;
  }

  // 遍历APM应用的.desktop文件可能在以下几个位置
  for (const baseDir of APM_DESKTOP_ENTRY_DIRS) {
    const entriesPath = path.join(baseDir, pkgname, "entries", "applications");
    let files: string[];
    try {
      files = await fsp.readdir(entriesPath);
    } catch {
      continue;
    }

    for (const file of files) {
      // 忽略扩展名不符的
      if (!file.endsWith(".desktop")) {
        continue;
      }

      const srcPath = path.join(entriesPath, file);
      const destPath = path.join(desktopDir, file);

      // 目标已存在则跳过，继续检查下一个
      try {
        await fsp.access(destPath);
        logger.debug(`Shortcut already exists: ${destPath}`);
        sendLog(`Shortcut already exists: ${file}`);
        continue;
      } catch {
        // 不存在，继续
      }

      try {
        // 读取.desktop文件内容
        const content = await fsp.readFile(srcPath, "utf-8");

        // 写入用户桌面，顺带处理一下权限问题
        await fsp.writeFile(destPath, content, { mode: 0o644 });
        sendLog(`Wrote desktop shortcut: ${file}`);
        logger.info(`Wrote shortcut ${destPath} for ${pkgname}.`);
        return;
      } catch (err) {
        logger.warn(`Failed to create desktop shortcut for ${pkgname}: ${err}`);
        return;
      }
    }
  }

  logger.debug(`Could NOT find ${pkgname}'s .desktop file...`);
};

export const tasks = new Map<number, InstallTask>();

// 下载与安装分离：最多 5 个并发下载，安装一次只允许一个
const MAX_CONCURRENT_DOWNLOADS = 5;
let activeDownloadCount = 0;
let installIdle = true;

export const checkSuperUserCommand = async (): Promise<string> => {
  if (process.getuid?.() === 0) return "";

  for (const command of SUPER_USER_COMMAND_CANDIDATES) {
    const superUserCmd = await findExecutable(command);
    if (superUserCmd.length > 0) {
      logger.info(`找到提升权限命令: ${superUserCmd}`);
      return superUserCmd;
    }
  }

  logger.error("没有找到提升权限的命令 pkexec!");
  return "";
};

const runCommandCapture = async (execCommand: string, execParams: string[]) => {
  return await new Promise<{ code: number; stdout: string; stderr: string }>(
    (resolve) => {
      const child = spawn(execCommand, execParams, {
        shell: false,
        env: process.env,
      });

      let stdout = "";
      let stderr = "";

      child.stdout?.on("data", (data) => {
        stdout += data.toString();
      });

      child.stderr?.on("data", (data) => {
        stderr += data.toString();
      });

      child.on("error", (err) => {
        resolve({ code: -1, stdout, stderr: err.message });
      });

      child.on("close", (code) => {
        resolve({ code: typeof code === "number" ? code : -1, stdout, stderr });
      });
    },
  );
};

/** 检测本机是否已安装 apm 命令 */
const checkApmAvailable = async (): Promise<boolean> => {
  const { code, stdout } = await runCommandCapture("which", ["apm"]);
  const found = code === 0 && stdout.trim().length > 0;
  if (!found) logger.info("未检测到 apm 命令");
  return found;
};

/** 检测本机是否具备 Spark/aptss 管理能力 */
const checkSparkAvailable = async (): Promise<boolean> => {
  const { code, stdout } = await runCommandCapture("which", ["aptss"]);
  const found = code === 0 && stdout.trim().length > 0;
  if (!found) logger.info("未检测到 aptss 命令");
  return found;
};

const parseUpgradableList = (output: string) => {
  const apps: Array<{
    pkgname: string;
    newVersion: string;
    currentVersion: string;
    raw: string;
  }> = [];
  const lines = output.split("\n");
  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed) continue;
    if (trimmed.startsWith("Listing")) continue;
    if (trimmed.startsWith("[INFO]")) continue;
    if (trimmed.includes("=") && !trimmed.includes("/")) continue;

    if (!trimmed.includes("/")) continue;

    const tokens = trimmed.split(/\s+/);
    if (tokens.length < 2) continue;
    const pkgToken = tokens[0];
    const pkgname = pkgToken.split("/")[0];
    const newVersion = tokens[1] || "";
    const currentMatch = trimmed.match(
      /\[(?:upgradable from|from):\s*([^\]\s]+)\]/i,
    );
    const currentToken = tokens[5] || "";
    const currentVersion =
      currentMatch?.[1] || currentToken.replace("[", "").replace("]", "");

    if (!pkgname) continue;
    apps.push({ pkgname, newVersion, currentVersion, raw: trimmed });
  }
  return apps;
};

// Listen for download requests from renderer process
ipcMain.on("queue-install", async (event, download_json) => {
  const download =
    typeof download_json === "string"
      ? JSON.parse(download_json)
      : download_json;
  const { id, pkgname, metalinkUrl, filename, origin, upgradeOnly } =
    download || {};

  if (!id || !pkgname) {
    logger.warn("passed arguments missing id or pkgname");
    return;
  }

  logger.info(`收到下载任务: ${id}, 软件包名称: ${pkgname}, 来源: ${origin}`);

  const webContents = event.sender;

  // 避免重复添加同一任务（检查 pkgname + origin），但允许重试下载
  if (!download.retry) {
    const existingTask = Array.from(tasks.values()).find(
      (t) => t.pkgname === pkgname && t.origin === origin,
    );
    if (existingTask) {
      webContents.send("install-log", {
        id,
        time: Date.now(),
        message: `任务 ${pkgname} (${origin}) 已在列表中，忽略重复添加`,
      });
      webContents.send("install-complete", {
        id,
        success: false,
        time: Date.now(),
        exitCode: -1,
        message: JSON.stringify({
          message: `任务 ${pkgname} (${origin}) 已在列表中，忽略重复添加`,
          stdout: "",
          stderr: "",
        }),
      });
      return;
    }
  }
  const superUserCmd = await checkSuperUserCommand();
  let execCommand = "";
  const execParams = [];
  const downloadDir = `/tmp/spark-store/download/${pkgname}`;

  // APM 应用：若本机没有 apm 命令，通知前端弹窗引导安装 APM
  if (origin === "apm") {
    const hasApm = await checkApmAvailable();
    if (!hasApm) {
      webContents.send("trigger-apm-install-dialog");
      webContents.send("install-complete", {
        id,
        success: false,
        time: Date.now(),
        exitCode: -1,
        message: JSON.stringify({
          message: "未安装 APM，无法继续安装此应用",
          stdout: "",
          stderr: "",
        }),
      });
      return;
    }
  }

  if (origin === "spark") {
    execCommand = superUserCmd || SHELL_CALLER_PATH;
    if (superUserCmd) execParams.push(SHELL_CALLER_PATH);

    if (metalinkUrl && filename) {
      execParams.push(
        "ssinstall",
        `${downloadDir}/${filename}`,
        "--delete-after-install",
        "--no-create-desktop-entry",
        "--native",
      );
    } else {
      execParams.push(
        "ssinstall",
        pkgname,
        "--no-create-desktop-entry",
        "--native",
      );
    }
  } else {
    // APM Store logic
    execCommand = superUserCmd || SHELL_CALLER_PATH;
    if (superUserCmd) {
      execParams.push(SHELL_CALLER_PATH);
    }
    execParams.push("apm");

    if (metalinkUrl && filename) {
      execParams.push("ssinstall", `${downloadDir}/${filename}`);
    } else {
      execParams.push("install", "-y", pkgname);
    }
  }

  const task: InstallTask = {
    id,
    pkgname,
    execCommand,
    execParams,
    download_process: null,
    install_process: null,
    webContents,
    downloadDir,
    metalinkUrl,
    filename,
    origin: origin || "apm",
    upgradeOnly: Boolean(upgradeOnly),
    phase: metalinkUrl ? "queued-download" : "queued-install",
  };
  tasks.set(id, task);
  processNextDownload();
  processNextInstall();
});

// Cancel Handler
ipcMain.on("cancel-install", (event, id) => {
  const task = tasks.get(id);
  if (!task) return;

  task.cancelled = true;
  logger.info(`已取消任务: ${id}`);

  // 删除下载目录
  if (task.downloadDir && fs.existsSync(task.downloadDir)) {
    try {
      fs.rmSync(task.downloadDir, { recursive: true, force: true });
      logger.info(`已删除下载目录: ${task.downloadDir}`);
    } catch (err) {
      logger.error(`删除下载目录失败 ${task.downloadDir}: ${err}`);
    }
  }

  // 主动发送完成（失败）事件
  task.webContents?.send("install-complete", {
    id,
    success: false,
    time: Date.now(),
    exitCode: -1,
    message: JSON.stringify({
      message: "用户取消",
      stdout: "",
      stderr: "",
    }),
  });

  const isRunning = task.phase === "downloading" || task.phase === "installing";

  if (isRunning) {
    // 运行中的任务：终止进程，由对应的阶段处理器在 finally 中清理计数器与队列
    task.download_process?.kill();
    task.install_process?.kill();
  } else {
    // 排队中的任务（未开始执行）：直接清理并调度
    tasks.delete(id);
    processNextDownload();
    processNextInstall();
  }
});

/**
 * 尝试启动排队中的下载任务，最多同时运行 MAX_CONCURRENT_DOWNLOADS 个。
 */
function processNextDownload() {
  while (activeDownloadCount < MAX_CONCURRENT_DOWNLOADS) {
    const task = Array.from(tasks.values()).find(
      (t) => t.phase === "queued-download" && !t.cancelled,
    );
    if (!task) break;
    task.phase = "downloading";
    activeDownloadCount++;
    void runDownloadPhase(task);
  }
}

/**
 * 尝试启动排队中的安装任务，安装一次只允许一个。
 */
function processNextInstall() {
  if (!installIdle) return;
  const task = Array.from(tasks.values()).find(
    (t) => t.phase === "queued-install" && !t.cancelled,
  );
  if (!task) {
    installIdle = true;
    return;
  }
  installIdle = false;
  task.phase = "installing";
  void runInstallPhase(task);
}

/**
 * 下载阶段：获取 Metalink → aria2c 下载（含重试）。
 * 下载完成后任务进入 queued-install 等待安装。
 */
async function runDownloadPhase(task: InstallTask) {
  const { webContents, id, downloadDir } = task;

  const sendLog = (msg: string) => {
    webContents?.send("install-log", { id, time: Date.now(), message: msg });
  };
  const sendStatus = (status: string) => {
    webContents?.send("install-status", {
      id,
      time: Date.now(),
      message: status,
    });
  };

  try {
    if (task.cancelled) throw new Error("下载已取消");

    // 1. Metalink & Aria2c Phase
    if (task.metalinkUrl) {
      try {
        if (!fs.existsSync(downloadDir)) {
          fs.mkdirSync(downloadDir, { recursive: true });
        }
      } catch (err) {
        logger.error(`无法创建目录 ${downloadDir}: ${err}`);
        throw err;
      }

      const metalinkPath = path.join(downloadDir, `${task.filename}.metalink`);

      sendLog(`正在获取 Metalink 文件: ${task.metalinkUrl}`);

      const response = await axios.get(task.metalinkUrl, {
        baseURL: "https://erotica.spark-app.store",
        responseType: "stream",
      });

      const writer = fs.createWriteStream(metalinkPath);
      response.data.pipe(writer);

      await new Promise<void>((resolve, reject) => {
        writer.on("finish", resolve);
        writer.on("error", reject);
      });

      sendLog("Metalink 文件下载完成");

      // 清理下载目录中的旧文件（保留 .metalink 文件），防止 aria2c 因同名文件卡住
      const existingFiles = fs.readdirSync(downloadDir);
      for (const file of existingFiles) {
        if (file.endsWith(".metalink")) continue;
        const filePath = path.join(downloadDir, file);
        try {
          fs.unlinkSync(filePath);
          sendLog(`已清理旧文件: ${file}`);
        } catch (err) {
          logger.warn(`清理文件失败 ${filePath}: ${err}`);
        }
      }

      // Aria2c
      const aria2Args = [
        `--dir=${downloadDir}`,
        "--allow-overwrite=true",
        "--async-dns=false",
        "--summary-interval=1",
        "--connect-timeout=10",
        "--timeout=15",
        "--max-tries=3",
        "--retry-wait=5",
        "--max-concurrent-downloads=4",
        "--min-split-size=1M",
        "--lowest-speed-limit=1K",
        "--auto-file-renaming=false",
        "-M",
        metalinkPath,
      ];

      sendStatus("downloading");

      // 下载重试逻辑：共10次，指数退避，首次3秒，末次1分钟
      const timeoutList = [
        3000, 4500, 6500, 9000, 13000, 18000, 26000, 36000, 50000, 60000,
      ];
      let retryCount = 0;
      let downloadSuccess = false;

      while (retryCount < timeoutList.length && !downloadSuccess) {
        const currentTimeout = timeoutList[retryCount];

        if (retryCount > 0) {
          sendLog(`第 ${retryCount} 次重试下载...`);
          webContents?.send("install-progress", { id, progress: 0 });
          // 重试前也清理旧文件
          const retryFiles = fs.readdirSync(downloadDir);
          for (const file of retryFiles) {
            if (file.endsWith(".metalink")) continue;
            const filePath = path.join(downloadDir, file);
            try {
              fs.unlinkSync(filePath);
            } catch (cleanErr) {
              logger.warn(`重试清理文件失败 ${filePath}: ${cleanErr}`);
            }
          }
        }

        try {
          await new Promise<void>((resolve, reject) => {
            sendLog(`启动下载: aria2c ${aria2Args.join(" ")}`);
            const child = spawn("aria2c", aria2Args);
            task.download_process = child;

            let lastProgressTime = Date.now();
            let lastProgress = 0;
            const progressCheckInterval = 1000; // 每1秒检查一次

            // 设置超时检测定时器
            const timeoutChecker = setInterval(() => {
              const now = Date.now();
              // 只在进度为0时检查超时
              if (
                lastProgress === 0 &&
                now - lastProgressTime > currentTimeout
              ) {
                clearInterval(timeoutChecker);
                child.kill();
                reject(new Error(`下载卡在0%超过 ${currentTimeout / 1000} 秒`));
              }
            }, progressCheckInterval);

            child.stdout.on("data", (data) => {
              const str = data.toString();
              // Match ( 12%) or (12%)
              const match = str.match(/[0-9]+(\.[0-9]+)?%/g);
              if (match) {
                const p = parseFloat(match.at(-1)) / 100;
                if (p > lastProgress) {
                  lastProgress = p;
                  lastProgressTime = Date.now();
                }
                webContents?.send("install-progress", { id, progress: p });
              }
            });
            child.stderr.on("data", (d) => sendLog(`aria2c: ${d}`));

            child.on("close", (code) => {
              clearInterval(timeoutChecker);
              if (task.cancelled) {
                reject(new Error("下载已取消"));
                return;
              }
              if (code === 0) {
                webContents?.send("install-progress", { id, progress: 1 });
                resolve();
              } else {
                reject(new Error(`Aria2c exited with code ${code}`));
              }
            });
            child.on("error", (err) => {
              clearInterval(timeoutChecker);
              reject(err);
            });
          });
          // 下载成功后检查是否已取消
          if (task.cancelled) {
            throw new Error("下载已取消");
          }
          downloadSuccess = true;
        } catch (err) {
          retryCount++;
          if (retryCount >= timeoutList.length) {
            throw new Error(
              `下载失败，已重试 ${timeoutList.length} 次: ${err}`,
            );
          }
          sendLog(`下载失败，准备重试 (${retryCount}/${timeoutList.length})`);
          // 等待2秒后重试
          await new Promise((r) => setTimeout(r, 2000));
        }
      }
    }

    // 下载完成，进入安装队列等待
    task.phase = "queued-install";
  } catch (error) {
    logger.error(`Task ${id} download failed: ${error}`);
    if (!task.cancelled) {
      webContents?.send("install-complete", {
        id,
        success: false,
        time: Date.now(),
        exitCode: -1,
        message: JSON.stringify({
          message: error instanceof Error ? error.message : String(error),
          stdout: "",
          stderr: "",
        }),
      });
    }
    tasks.delete(id);
  } finally {
    activeDownloadCount--;
    processNextDownload();
    processNextInstall();
  }
}

/**
 * 安装阶段：执行安装命令，安装一次只允许一个。
 */
async function runInstallPhase(task: InstallTask) {
  const { webContents, id } = task;

  const sendLog = (msg: string) => {
    webContents?.send("install-log", { id, time: Date.now(), message: msg });
  };
  const sendStatus = (status: string) => {
    webContents?.send("install-status", {
      id,
      time: Date.now(),
      message: status,
    });
  };

  try {
    if (task.cancelled) throw new Error("安装已取消");

    // 2. Install Phase
    sendStatus("installing");

    const cmdString = `${task.execCommand} ${task.execParams.join(" ")}`;
    sendLog(`执行安装: ${cmdString}`);
    logger.info(`启动安装: ${cmdString}`);

    const result = await new Promise<{
      code: number;
      stdout: string;
      stderr: string;
    }>((resolve, reject) => {
      const child = spawn(task.execCommand, task.execParams, {
        shell: false,
        env: process.env,
      });
      task.install_process = child;

      let stdout = "";
      let stderr = "";
      let logBuffer = "";
      let logBufferTimer: NodeJS.Timeout | null = null;
      const LOG_FLUSH_MS = 100;

      const flushLogBuffer = () => {
        if (logBuffer.length > 0) {
          sendLog(logBuffer);
          logBuffer = "";
        }
        logBufferTimer = null;
      };

      const bufferedSendLog = (message: string) => {
        logBuffer += message;
        if (!logBufferTimer) {
          logBufferTimer = setTimeout(flushLogBuffer, LOG_FLUSH_MS);
        }
      };

      child.stdout.on("data", (d) => {
        const s = d.toString();
        stdout += s;
        bufferedSendLog(s);
      });

      child.stderr.on("data", (d) => {
        const s = d.toString();
        stderr += s;
        bufferedSendLog(s);
      });

      child.on("close", (code) => {
        if (logBufferTimer) {
          clearTimeout(logBufferTimer);
          flushLogBuffer();
        }
        if (task.cancelled) {
          reject(new Error("安装已取消"));
          return;
        }
        resolve({ code: code ?? -1, stdout, stderr });
      });
      child.on("error", (err) => {
        reject(err);
      });
    });

    // Completion
    const success = result.code === 0;
    const msgObj = {
      message: success ? "安装完成" : `安装失败，退出码 ${result.code}`,
      stdout: result.stdout,
      stderr: result.stderr,
    };

    if (success) {
      logger.info(msgObj);

      // 安装成功后，如果是APM新安装任务，就调用createApmDesktopShortcut
      // 升级任务不创建桌面快捷方式；这个函数自己会读取设置并决定要不要创建
      if (task.origin === "apm" && task.pkgname && !task.upgradeOnly) {
        try {
          await createApmDesktopShortcut(task.pkgname, sendLog);
        } catch (err) {
          logger.warn(`Failed to create shortcut for ${task.pkgname}: ${err}`);
        }
      }
    } else {
      logger.error(msgObj);
    }

    webContents?.send("install-complete", {
      id,
      success,
      time: Date.now(),
      exitCode: result.code,
      message: JSON.stringify(msgObj),
    });
  } catch (error) {
    logger.error(`Task ${id} install failed: ${error}`);
    if (!task.cancelled) {
      webContents?.send("install-complete", {
        id,
        success: false,
        time: Date.now(),
        exitCode: -1,
        message: JSON.stringify({
          message: error instanceof Error ? error.message : String(error),
          stdout: "",
          stderr: "",
        }),
      });
    }
  } finally {
    tasks.delete(id);
    installIdle = true;
    processNextInstall();
    processNextDownload();
  }
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
ipcMain.handle("check-installed", async (_event, payload: any) => {
  const pkgname = typeof payload === "string" ? payload : payload.pkgname;
  const origin = typeof payload === "string" ? "spark" : payload.origin;

  if (!pkgname) {
    logger.warn("check-installed missing pkgname");
    return false;
  }

  logger.info(`检查应用是否已安装: ${pkgname} (来源: ${origin})`);

  let isInstalled = false;

  if (origin === "apm") {
    const { code, stdout } = await runCommandCapture("apm", [
      "list",
      "--installed",
    ]);
    if (code === 0) {
      const cleanStdout = stdout.replace(
        // eslint-disable-next-line no-control-regex
        /\x1b\[[0-9;]*m/g,
        "",
      );
      const lines = cleanStdout.split("\n");
      for (const line of lines) {
        const trimmed = line.trim();
        if (
          !trimmed ||
          trimmed.startsWith("Listing") ||
          trimmed.startsWith("[INFO]") ||
          trimmed.startsWith("警告")
        )
          continue;
        if (trimmed.includes("/")) {
          const installedPkg = trimmed.split("/")[0].trim();
          if (installedPkg === pkgname) {
            isInstalled = true;
            logger.info(`应用已安装 (APM检测): ${pkgname}`);
            break;
          }
        }
      }
    }
    return isInstalled;
  }

  // Spark: 使用 dpkg-query 检查安装状态
  const { code, stdout } = await runCommandCapture("dpkg-query", [
    "-W",
    "-f=${Package}\\t${Status}\\n",
    pkgname,
  ]);

  if (code === 0) {
    const line = stdout.trim();
    if (line) {
      const parts = line.split("\t");
      if (parts.length >= 2) {
        const status = parts[1].trim();
        // 检查状态是否为 "install ok installed"
        if (status === "install ok installed") {
          isInstalled = true;
          logger.info(`应用已安装 (dpkg检测): ${pkgname}`);
        }
      }
    }
  }

  return isInstalled;
});

ipcMain.on("remove-installed", async (_event, payload) => {
  const webContents = _event.sender;
  const pkgname = typeof payload === "string" ? payload : payload.pkgname;
  const origin = typeof payload === "string" ? "spark" : payload.origin;

  if (!pkgname) {
    logger.warn("remove-installed missing pkgname");
    return;
  }
  logger.info(`卸载已安装应用: ${pkgname} (来源: ${origin})`);

  let execCommand = "";
  const execParams = [];

  const superUserCmd = await checkSuperUserCommand();
  execCommand = superUserCmd || SHELL_CALLER_PATH;
  if (superUserCmd) execParams.push(SHELL_CALLER_PATH);

  if (origin === "spark") {
    execParams.push("aptss", "remove", pkgname);
  } else {
    execParams.push("apm", "autoremove", "-y", pkgname);
  }

  const child = spawn(execCommand, execParams, {
    shell: false,
    env: process.env,
  });
  let output = "";

  child.stdout.on("data", (data) => {
    const chunk = data.toString();
    output += chunk;
    webContents.send("remove-progress", chunk);
  });

  child.on("close", (code) => {
    const success = code === 0;
    // 拼接json消息
    const messageJSONObj = {
      message: success ? "卸载完成" : `卸载失败，退出码 ${code}`,
      stdout: output,
      stderr: "",
    };

    if (success) {
      logger.info(messageJSONObj);
    } else {
      logger.error(messageJSONObj);
    }

    webContents.send("remove-complete", {
      id: 0,
      success: success,
      time: Date.now(),
      exitCode: code,
      message: JSON.stringify(messageJSONObj),
      origin: origin,
    } satisfies ChannelPayload);
  });
});

ipcMain.handle(
  "list-installed",
  async (
    _event,
    payload: { origin: "apm" | "spark"; pkgnameList?: string[] },
  ) => {
    const { origin, pkgnameList } = payload;
    const storeFilter = getStoreFilterFromArgv();
    const apmBasePath = "/var/lib/apm/apm/files/ace-env/var/lib/apm";

    if (!isOriginEnabled(storeFilter, origin)) {
      return {
        success: false,
        message: `${origin} origin disabled by startup filter`,
        apps: [],
      };
    }

    if (origin === "spark" && !(await checkSparkAvailable())) {
      return {
        success: false,
        message: "spark origin unavailable on this system",
        apps: [],
      };
    }

    if (origin === "apm" && !(await checkApmAvailable())) {
      return {
        success: false,
        message: "apm origin unavailable on this system",
        apps: [],
      };
    }

    try {
      const installedApps: Array<{
        pkgname: string;
        name: string;
        version: string;
        arch: string;
        flags: string;
        origin: "spark" | "apm";
        icon?: string;
        isDependency: boolean;
      }> = [];

      if (origin === "spark") {
        // 如果提供了包名列表，只检查这些包的安装状态（优化版）
        if (pkgnameList && pkgnameList.length > 0) {
          logger.info(
            `使用优化模式检查 ${pkgnameList.length} 个 Spark 包的安装状态`,
          );

          // 批量查询这些包的状态
          // 注意：dpkg-query 在部分包不存在时也会返回非零码，但已找到的包会输出到 stdout
          const { stdout, stderr } = await runCommandCapture("dpkg-query", [
            "-W",
            "-f=${Package}\\t${Version}\\t${Architecture}\\t${Status}\\n",
            ...pkgnameList,
          ]);

          // 即使没有错误，也可能有警告信息输出到 stderr
          if (stderr) {
            logger.debug(`dpkg-query warnings: ${stderr}`);
          }

          const lines = stdout.split("\n");
          for (const line of lines) {
            const trimmed = line.trim();
            if (!trimmed) continue;
            const parts = trimmed.split("\t");
            if (parts.length >= 4) {
              const status = parts[3].trim();
              // 只保留状态为 "install ok installed" 的包
              if (status === "install ok installed") {
                installedApps.push({
                  pkgname: parts[0],
                  name: parts[0],
                  version: parts[1],
                  arch: parts[2],
                  flags: "[installed]",
                  origin: "spark",
                  isDependency: false,
                });
              }
            }
          }
          return { success: true, apps: installedApps };
        }

        // 回退到全量扫描模式（未提供包名列表时）
        logger.info("使用全量扫描模式获取所有 Spark 已安装包");
        const { code, stdout } = await runCommandCapture("dpkg-query", [
          "-W",
          "-f=${Package}\\t${Version}\\t${Architecture}\\t${Status}\\n",
        ]);

        if (code !== 0) {
          logger.warn(`Failed to list installed packages: ${stdout}`);
          return {
            success: false,
            message: "Failed to list installed packages",
            apps: [],
          };
        }

        const lines = stdout.split("\n");
        for (const line of lines) {
          const trimmed = line.trim();
          if (!trimmed) continue;
          const parts = trimmed.split("\t");
          if (parts.length >= 4) {
            const status = parts[3].trim();
            // 只保留状态为 "install ok installed" 的包
            if (status === "install ok installed") {
              installedApps.push({
                pkgname: parts[0],
                name: parts[0],
                version: parts[1],
                arch: parts[2],
                flags: "[installed]",
                origin: "spark",
                isDependency: false,
              });
            }
          }
        }
        return { success: true, apps: installedApps };
      }

      // 使用 apm list --installed 获取所有已安装应用
      const { code, stdout } = await runCommandCapture("apm", [
        "list",
        "--installed",
      ]);

      if (code !== 0) {
        logger.warn(`Failed to list installed packages: ${stdout}`);
        return {
          success: false,
          message: "Failed to list installed packages",
          apps: [],
        };
      }

      const cleanStdout = stdout.replace(
        // eslint-disable-next-line no-control-regex
        /\x1b\[[0-9;]*m/g,
        "",
      );
      const lines = cleanStdout.split("\n");

      for (const line of lines) {
        const trimmed = line.trim();
        if (
          !trimmed ||
          trimmed.startsWith("Listing") ||
          trimmed.startsWith("[INFO]") ||
          trimmed.startsWith("警告")
        )
          continue;

        // 解析格式: pkgname/repo,section version arch [flags] 或 pkgname/repo version arch [flags]
        // 注意: repo后面可能有逗号和section，也可能没有
        const match = trimmed.match(
          /^(\S+)\/\S+(?:,\S+)?\s+(\S+)\s+(\S+)\s+\[(.+)\]$/,
        );
        if (!match) {
          logger.debug(`Failed to parse line: ${trimmed}`);
          continue;
        }

        const [, pkgname, version, arch, flags] = match;

        // 从桌面文件获取应用名称和图标
        let appName = pkgname;
        let icon = "";
        const pkgPath = path.join(apmBasePath, pkgname);
        const entriesPath = path.join(pkgPath, "entries", "applications");
        const hasEntries = fs.existsSync(entriesPath);

        if (hasEntries) {
          try {
            const desktopFiles = fs.readdirSync(entriesPath);
            logger.debug(
              `Found desktop files for ${pkgname}: ${desktopFiles.join(", ")}`,
            );
            for (const file of desktopFiles) {
              if (file.endsWith(".desktop")) {
                const desktopPath = path.join(entriesPath, file);
                logger.debug(`Reading desktop file: ${desktopPath}`);
                const content = fs.readFileSync(desktopPath, "utf-8");
                const nameMatch = content.match(/^Name=(.+)$/m);
                const iconMatch = content.match(/^Icon=(.+)$/m);
                if (nameMatch) appName = nameMatch[1].trim();
                if (iconMatch) icon = iconMatch[1].trim();
                logger.debug(
                  `Parsed desktop file for ${pkgname}: name=${appName}, icon=${icon}`,
                );
                break;
              }
            }
          } catch (e) {
            logger.warn(`Failed to read desktop file for ${pkgname}: ${e}`);
          }
        } else {
          logger.debug(`No entries path for ${pkgname}: ${entriesPath}`);
        }

        installedApps.push({
          pkgname,
          name: appName,
          version,
          arch,
          flags,
          origin: "apm",
          icon: icon || undefined,
          isDependency: !hasEntries,
        });
      }

      installedApps.sort((a, b) => {
        const getOrder = (app: { pkgname: string; isDependency: boolean }) => {
          if (app.isDependency) return 2;
          if (app.pkgname.startsWith("amber-pm")) return 1;
          return 0;
        };

        const aOrder = getOrder(a);
        const bOrder = getOrder(b);

        if (aOrder !== bOrder) return aOrder - bOrder;
        return a.pkgname.localeCompare(b.pkgname);
      });

      logger.info(`Found ${installedApps.length} installed APM apps`);
      return { success: true, apps: installedApps };
    } catch (error) {
      logger.error(
        `list-installed failed: ${error instanceof Error ? error.message : String(error)}`,
      );
      return {
        success: false,
        message: error instanceof Error ? error.message : String(error),
        apps: [],
      };
    }
  },
);

ipcMain.handle("list-upgradable", async () => {
  const { code, stdout, stderr } = await runCommandCapture("apm", [
    "list",
    "--upgradable",
  ]);
  if (code !== 0) {
    logger.error(`list-upgradable failed: ${stderr || stdout}`);
    return {
      success: false,
      message: stderr || stdout || `list-upgradable failed with code ${code}`,
      apps: [],
    };
  }

  const apps = parseUpgradableList(stdout);
  return { success: true, apps };
});

ipcMain.handle("check-apm-available", async () => {
  return await checkApmAvailable();
});

ipcMain.handle("check-spark-available", async () => {
  return await checkSparkAvailable();
});

// 显示 APM 安装对话框（在点击安装按钮时提前检查）
// 前端已改为 Vue 弹窗，此后端处理仅作为兜底
ipcMain.handle("show-apm-install-dialog", async (event) => {
  const webContents = event.sender;
  webContents.send("trigger-apm-install-dialog");
  return { success: false, cancelled: true };
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
ipcMain.handle("uninstall-installed", async (_event, payload: any) => {
  const pkgname = typeof payload === "string" ? payload : payload.pkgname;
  const origin = typeof payload === "string" ? "spark" : payload.origin;

  if (!pkgname) {
    logger.warn("uninstall-installed missing pkgname");
    return { success: false, message: "missing pkgname" };
  }

  const superUserCmd = await checkSuperUserCommand();
  const execCommand = superUserCmd || SHELL_CALLER_PATH;
  const execParams = superUserCmd ? [SHELL_CALLER_PATH] : [];

  if (origin === "apm") {
    execParams.push("apm", "remove", "-y", pkgname);
  } else {
    execParams.push("aptss", "remove", "-y", pkgname);
  }

  const { code, stdout, stderr } = await runCommandCapture(
    execCommand,
    execParams,
  );
  const success = code === 0;

  if (success) {
    logger.info(`卸载完成: ${pkgname}`);
  } else {
    logger.error(`卸载失败: ${pkgname} ${stderr || stdout}`);
  }

  return {
    success,
    message: success
      ? "卸载完成"
      : stderr || stdout || `卸载失败，退出码 ${code}`,
  };
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
ipcMain.handle("launch-app", async (_event, payload: any) => {
  const pkgname = typeof payload === "string" ? payload : payload.pkgname;
  const origin = typeof payload === "string" ? "spark" : payload.origin;

  if (!pkgname) {
    logger.warn("No pkgname provided for launch-app");
  }

  let execCommand = "/opt/spark-store/extras/app-launcher";
  let execParams = ["start", pkgname];

  if (origin === "apm") {
    execCommand = "apm";
    execParams = ["launch", pkgname];
  }

  logger.info(
    `Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(" ")}`,
  );

  spawn(execCommand, execParams, {
    shell: false,
    env: process.env,
    detached: true,
    stdio: "ignore",
  }).unref();
});
