// Force strict mode and setup for ESM
"use strict";
import {
  Storage
} from "./chunk-AVQDCKNF.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/commands/channel/pidfile.ts
init_esbuild_shims();
import {
  existsSync,
  readFileSync,
  writeFileSync,
  mkdirSync,
  unlinkSync,
  openSync,
  closeSync,
  constants,
  ftruncateSync,
  writeSync
} from "node:fs";
import * as path from "node:path";
function pidFilePath() {
  return path.join(Storage.getGlobalQwenDir(), "channels", "service.pid");
}
__name(pidFilePath, "pidFilePath");
function isValidPid(pid) {
  return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0;
}
__name(isValidPid, "isValidPid");
function parseServiceInfo(value) {
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    return null;
  }
  const info = value;
  const owner = info.owner ?? "channel";
  if (owner !== "channel" && owner !== "serve") return null;
  if (!isValidPid(info.pid) || typeof info.startedAt !== "string" || Number.isNaN(Date.parse(info.startedAt)) || !Array.isArray(info.channels) || !info.channels.every((channel) => typeof channel === "string")) {
    return null;
  }
  if (info.servePid !== void 0 && !isValidPid(info.servePid)) return null;
  if (info.workerPid !== void 0 && !isValidPid(info.workerPid)) return null;
  return {
    owner,
    pid: info.pid,
    startedAt: info.startedAt,
    channels: info.channels,
    ...info.servePid !== void 0 ? { servePid: info.servePid } : {},
    ...info.workerPid !== void 0 ? { workerPid: info.workerPid } : {}
  };
}
__name(parseServiceInfo, "parseServiceInfo");
function unlinkPidFile(filePath) {
  try {
    unlinkSync(filePath);
  } catch {
  }
}
__name(unlinkPidFile, "unlinkPidFile");
function isProcessAlive(pid) {
  if (!isValidPid(pid)) {
    return false;
  }
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}
__name(isProcessAlive, "isProcessAlive");
function readServiceInfo() {
  const filePath = pidFilePath();
  if (!existsSync(filePath)) return null;
  let parsed;
  try {
    parsed = JSON.parse(readFileSync(filePath, "utf-8"));
  } catch {
    unlinkPidFile(filePath);
    return null;
  }
  const info = parseServiceInfo(parsed);
  if (!info) {
    unlinkPidFile(filePath);
    return null;
  }
  if (!isProcessAlive(info.pid)) {
    unlinkPidFile(filePath);
    return null;
  }
  return info;
}
__name(readServiceInfo, "readServiceInfo");
function writeInfo(info, flag = "w") {
  const filePath = pidFilePath();
  const dir = path.dirname(filePath);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }
  writeFileSync(filePath, JSON.stringify(info, null, 2), {
    encoding: "utf-8",
    flag
  });
}
__name(writeInfo, "writeInfo");
function fileExistsError(message) {
  const err = new Error(message);
  err.code = "EEXIST";
  return err;
}
__name(fileExistsError, "fileExistsError");
function writeServiceInfo(channels) {
  const info = {
    owner: "channel",
    pid: process.pid,
    startedAt: (/* @__PURE__ */ new Date()).toISOString(),
    channels
  };
  writeInfo(info, "wx");
}
__name(writeServiceInfo, "writeServiceInfo");
function writeServeServiceInfo({
  channels,
  servePid = process.pid,
  workerPid
}) {
  const buildInfo = /* @__PURE__ */ __name((startedAt) => ({
    owner: "serve",
    pid: servePid,
    startedAt,
    channels,
    servePid,
    ...workerPid !== void 0 ? { workerPid } : {}
  }), "buildInfo");
  const filePath = pidFilePath();
  let fd;
  try {
    fd = openSync(filePath, constants.O_RDWR | constants.O_NOFOLLOW);
  } catch (err) {
    if (err.code === "ENOENT") {
      writeInfo(buildInfo((/* @__PURE__ */ new Date()).toISOString()), "wx");
      return;
    }
    throw err;
  }
  try {
    let existing = null;
    try {
      existing = parseServiceInfo(JSON.parse(readFileSync(fd, "utf-8")));
    } catch {
    }
    if (!existing || existing.owner !== "serve" || existing.pid !== servePid || existing.servePid !== servePid) {
      throw fileExistsError(
        "Channel service pidfile is owned by another process."
      );
    }
    const info = buildInfo(existing.startedAt);
    ftruncateSync(fd, 0);
    writeSync(fd, JSON.stringify(info, null, 2), 0, "utf-8");
  } finally {
    closeSync(fd);
  }
}
__name(writeServeServiceInfo, "writeServeServiceInfo");
function reserveServeServiceInfo({
  channels,
  servePid = process.pid
}) {
  const info = {
    owner: "serve",
    pid: servePid,
    startedAt: (/* @__PURE__ */ new Date()).toISOString(),
    channels,
    servePid
  };
  writeInfo(info, "wx");
}
__name(reserveServeServiceInfo, "reserveServeServiceInfo");
function removeServiceInfo() {
  const filePath = pidFilePath();
  if (existsSync(filePath)) {
    unlinkPidFile(filePath);
  }
}
__name(removeServiceInfo, "removeServiceInfo");
function removeServeServiceInfo(servePid = process.pid) {
  const filePath = pidFilePath();
  if (!existsSync(filePath)) return false;
  let parsed;
  try {
    parsed = JSON.parse(readFileSync(filePath, "utf-8"));
  } catch {
    return false;
  }
  const info = parseServiceInfo(parsed);
  if (!info || info.owner !== "serve" || info.servePid !== servePid || info.pid !== servePid) {
    return false;
  }
  unlinkPidFile(filePath);
  return true;
}
__name(removeServeServiceInfo, "removeServeServiceInfo");
function signalService(pid, signal = "SIGTERM") {
  if (!isValidPid(pid)) {
    return false;
  }
  try {
    process.kill(pid, signal);
    return true;
  } catch {
    return false;
  }
}
__name(signalService, "signalService");
async function waitForExit(pid, timeoutMs = 5e3, pollMs = 200) {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    if (!isProcessAlive(pid)) return true;
    await new Promise((r) => setTimeout(r, pollMs));
  }
  return !isProcessAlive(pid);
}
__name(waitForExit, "waitForExit");

export {
  readServiceInfo,
  writeServiceInfo,
  writeServeServiceInfo,
  reserveServeServiceInfo,
  removeServiceInfo,
  removeServeServiceInfo,
  signalService,
  waitForExit
};
