import * as fs from "node:fs";
import { existsSync, mkdirSync, createWriteStream, unlinkSync, chmodSync, readdirSync, rmSync, renameSync, writeFileSync, readFileSync } from "node:fs";
import * as path from "node:path";
import { join, basename } from "node:path";
import { pipeline } from "node:stream/promises";
import { Readable } from "node:stream";
import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { e as errMsg } from "./auth-records-Bq2XO-Mg.mjs";
import "zod";
function getPlatformInfo() {
  return {
    platform: process.platform,
    arch: process.arch
  };
}
function buildBinaryPath(dataDir, name, platform) {
  const ext = (platform ?? process.platform) === "win32" ? ".exe" : "";
  return join(dataDir, "deps", `${name}${ext}`);
}
function findInPath(name) {
  try {
    execFileSync(name, ["--version"], { stdio: "pipe", timeout: 5e3 });
    return name;
  } catch {
    return null;
  }
}
async function downloadBinary(opts) {
  const { name, url, targetDir, targetName, logger, isArchive, archiveFormat, archiveInnerPath } = opts;
  const targetPath = join(targetDir, targetName);
  if (existsSync(targetPath)) {
    logger.debug("Binary already exists", { meta: { name, targetPath } });
    return targetPath;
  }
  mkdirSync(targetDir, { recursive: true });
  logger.info("Downloading binary", { meta: { name, url } });
  const response = await fetch(url, { redirect: "follow" });
  if (!response.ok || !response.body) {
    throw new Error(`Failed to download ${name}: ${response.status} ${response.statusText}`);
  }
  if (isArchive) {
    const ext = archiveFormat ?? "tar.gz";
    const tmpArchive = join(targetDir, `${name}-download.${ext}`);
    const nodeStream = Readable.fromWeb(response.body);
    await pipeline(nodeStream, createWriteStream(tmpArchive));
    const tmpExtractDir = join(targetDir, `${name}-extract`);
    mkdirSync(tmpExtractDir, { recursive: true });
    if (ext === "zip") {
      try {
        execFileSync("unzip", ["-o", "-q", tmpArchive, "-d", tmpExtractDir], { stdio: "pipe" });
      } catch {
        execFileSync("tar", ["-xf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
      }
    } else if (ext === "tar.xz") {
      execFileSync("tar", ["-xJf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
    } else {
      execFileSync("tar", ["-xzf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
    }
    if (archiveInnerPath) {
      const { copyFileSync } = await import("node:fs");
      const sourcePath = join(tmpExtractDir, archiveInnerPath);
      if (!existsSync(sourcePath)) {
        throw new Error(`Binary not found in archive at ${archiveInnerPath}`);
      }
      copyFileSync(sourcePath, targetPath);
    }
    unlinkSync(tmpArchive);
    const { rmSync: rmSync2 } = await import("node:fs");
    rmSync2(tmpExtractDir, { recursive: true, force: true });
  } else {
    const nodeStream = Readable.fromWeb(response.body);
    await pipeline(nodeStream, createWriteStream(targetPath));
  }
  chmodSync(targetPath, 493);
  logger.info("Binary downloaded", { meta: { name, targetPath } });
  return targetPath;
}
async function ensureBinary(opts) {
  const ext = process.platform === "win32" ? ".exe" : "";
  const targetName = `${opts.name}${ext}`;
  const targetPath = join(opts.targetDir, targetName);
  if (existsSync(targetPath)) {
    opts.logger.debug("Binary found at target path", { meta: { name: opts.name, targetPath } });
    return targetPath;
  }
  const inPath = findInPath(opts.name);
  if (inPath) {
    opts.logger.info("Binary found in system PATH", { meta: { name: opts.name } });
    return inPath;
  }
  return downloadBinary({
    name: opts.name,
    url: opts.downloadUrl,
    targetDir: opts.targetDir,
    targetName,
    logger: opts.logger,
    isArchive: opts.isArchive,
    archiveFormat: opts.archiveFormat,
    archiveInnerPath: opts.archiveInnerPath
  });
}
const FFMPEG_VERSION = "7.1";
function getFfmpegDownloadUrl(platform, arch) {
  switch (platform) {
    case "linux": {
      const archMap = { x64: "amd64", arm64: "arm64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported Linux architecture: ${arch}`);
      return `https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${a}-static.tar.xz`;
    }
    case "darwin": {
      const archMap = { arm64: "arm64", x64: "amd64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported macOS architecture: ${arch}`);
      return `https://www.osxexperts.net/ffmpeg${FFMPEG_VERSION.replace(".", "")}arm.zip`;
    }
    default:
      throw new Error(`Unsupported platform: ${platform}`);
  }
}
function getFfmpegArchiveInfo(platform) {
  switch (platform) {
    case "linux":
      return {
        isArchive: true,
        archiveFormat: "tar.xz",
        archiveInnerPath: `ffmpeg-${FFMPEG_VERSION}-amd64-static/ffmpeg`
      };
    case "darwin":
      return {
        isArchive: true,
        archiveFormat: "zip",
        archiveInnerPath: "ffmpeg"
      };
    default:
      throw new Error(`Unsupported platform: ${platform}`);
  }
}
async function ensureFfmpeg(dataDir, logger) {
  const depsDir = join(dataDir, "deps");
  const platform = process.platform;
  const arch = process.arch;
  const archiveInfo = getFfmpegArchiveInfo(platform);
  return ensureBinary({
    name: "ffmpeg",
    targetDir: depsDir,
    downloadUrl: getFfmpegDownloadUrl(platform, arch),
    logger,
    ...archiveInfo
  });
}
const PYTHON_VERSION = "3.12.12";
const PP_BASE = `https://github.com/bjia56/portable-python/releases/download/cpython-v${PYTHON_VERSION}-build.0`;
function getPythonDownloadUrl(platform, arch) {
  switch (platform) {
    case "linux": {
      const archMap = { x64: "x86_64", arm64: "aarch64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported Linux architecture for Python: ${arch}`);
      return `${PP_BASE}/python-headless-${PYTHON_VERSION}-linux-${a}.zip`;
    }
    case "darwin": {
      return `${PP_BASE}/python-headless-${PYTHON_VERSION}-darwin-universal2.zip`;
    }
    default:
      throw new Error(`Unsupported platform for portable Python: ${platform}`);
  }
}
async function ensurePython(dataDir, logger) {
  const pythonDir = join(dataDir, "deps", "python");
  const pythonBin = join(pythonDir, "bin", "python3");
  if (existsSync(pythonBin)) {
    logger.debug("Portable Python found", { meta: { pythonBin } });
    return pythonBin;
  }
  logger.info("Downloading portable Python (headless)", { meta: { version: PYTHON_VERSION } });
  const depsDir = join(dataDir, "deps");
  mkdirSync(depsDir, { recursive: true });
  const url = getPythonDownloadUrl(process.platform, process.arch);
  const tmpArchive = join(depsDir, "python-download.zip");
  logger.info("Python download source", { meta: { url } });
  const response = await fetch(url, { redirect: "follow" });
  if (!response.ok || !response.body) {
    logger.warn("Failed to download Python — ML detection will not be available", { meta: { status: response.status } });
    return null;
  }
  const nodeStream = Readable.fromWeb(response.body);
  await pipeline(nodeStream, createWriteStream(tmpArchive));
  try {
    execFileSync("unzip", ["-o", "-q", tmpArchive, "-d", depsDir], { stdio: "pipe" });
  } catch {
    execFileSync("python3", ["-m", "zipfile", "-e", tmpArchive, depsDir], { stdio: "pipe" });
  }
  const { unlinkSync: unlinkSync2 } = await import("node:fs");
  unlinkSync2(tmpArchive);
  if (!existsSync(pythonBin)) {
    const candidates = readdirSync(depsDir).filter(
      (name) => name.startsWith("python-") && existsSync(join(depsDir, name, "bin", "python3"))
    );
    if (candidates.length === 0) {
      logger.warn("Python extraction succeeded but no python3 binary found in any subdir", {
        meta: { depsDir }
      });
      return null;
    }
    const extractedRoot = join(depsDir, candidates[0]);
    if (existsSync(pythonDir)) {
      rmSync(pythonDir, { recursive: true, force: true });
    }
    renameSync(extractedRoot, pythonDir);
  }
  if (!existsSync(pythonBin)) {
    logger.warn("Python extraction succeeded but binary not found at expected path", {
      meta: { pythonBin }
    });
    return null;
  }
  chmodSync(pythonBin, 493);
  if (process.platform === "darwin") {
    const realBin = join(pythonDir, "bin", "python3.real");
    if (!existsSync(realBin)) {
      renameSync(pythonBin, realBin);
      const archFlag = process.arch === "arm64" ? "-arm64" : "-x86_64";
      writeFileSync(
        pythonBin,
        `#!/bin/sh
exec /usr/bin/arch ${archFlag} "${realBin}" "$@"
`
      );
      chmodSync(pythonBin, 493);
      const pyAlt = join(pythonDir, "bin", "python");
      if (existsSync(pyAlt)) {
        try {
          rmSync(pyAlt, { force: true });
        } catch {
        }
        writeFileSync(
          pyAlt,
          `#!/bin/sh
exec /usr/bin/arch ${archFlag} "${realBin}" "$@"
`
        );
        chmodSync(pyAlt, 493);
      }
      logger.info("Locked python3 to host arch via shell stub", {
        meta: { archFlag, realBin }
      });
    }
  }
  logger.info("Portable Python installed", { meta: { version: PYTHON_VERSION, pythonBin } });
  return pythonBin;
}
async function installPythonPackages(pythonPath, packages, logger) {
  if (packages.length === 0) return;
  logger.info("Installing Python packages", { meta: { packages } });
  try {
    execFileSync(pythonPath, ["-m", "pip", "install", "--quiet", ...packages], {
      stdio: "pipe",
      timeout: 3e5
      // 5 minutes for large packages like torch
    });
    logger.info("Python packages installed successfully");
  } catch (err) {
    logger.error("Failed to install Python packages", { meta: { error: errMsg(err) } });
    throw err;
  }
}
async function installPythonRequirements(pythonPath, requirementsFile, logger) {
  if (!existsSync(requirementsFile)) {
    throw new Error(`Requirements file not found: ${requirementsFile}`);
  }
  const contents = readFileSync(requirementsFile);
  const hash = createHash("sha256").update(contents).digest("hex").slice(0, 16);
  const fileBase = basename(requirementsFile).replace(/\.txt$/i, "");
  const pythonRoot = join(pythonPath, "..", "..");
  const markerDir = join(pythonRoot, ".requirements-installed");
  const markerPath = join(markerDir, `${fileBase}-${hash}.marker`);
  if (existsSync(markerPath)) {
    logger.debug("Python requirements already installed", { meta: { requirementsFile, hash } });
    return;
  }
  logger.info("Installing Python requirements", { meta: { requirementsFile, hash } });
  try {
    execFileSync(
      pythonPath,
      ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", "-r", requirementsFile],
      {
        stdio: "pipe",
        timeout: 6e5
        // 10 minutes — coremltools + tensorflow can be slow
      }
    );
  } catch (err) {
    logger.error("Failed to install Python requirements", {
      meta: { requirementsFile, error: errMsg(err) }
    });
    throw err;
  }
  mkdirSync(markerDir, { recursive: true });
  writeFileSync(markerPath, `${requirementsFile}
${(/* @__PURE__ */ new Date()).toISOString()}
`);
  logger.info("Python requirements installed", { meta: { requirementsFile, hash } });
}
const STORAGE_LOCATION_TYPES = [
  "data",
  "media",
  "recordings",
  "recordings-high",
  "recordings-low",
  "recordings-clips",
  "event-images",
  "models",
  "addons-data",
  "cache",
  "logs",
  "backups"
];
const DEFAULT_LOCATION_SUBDIRS = {
  "data": "db",
  "media": "media",
  "recordings": "recordings",
  "recordings-high": "recordings-high",
  "recordings-low": "recordings-low",
  "recordings-clips": "recordings-clips",
  "event-images": "event-images",
  "models": "models",
  "addons-data": "addons-data",
  "cache": "/tmp/camstack-cache",
  "logs": "logs",
  "backups": "backups"
};
class FilesystemStorageProvider {
  id = "local";
  name = "Local Filesystem";
  supportedLocations = [...STORAGE_LOCATION_TYPES];
  rootPath;
  locationPaths;
  constructor(rootPath, overrides) {
    this.rootPath = path.resolve(rootPath);
    this.locationPaths = /* @__PURE__ */ new Map();
    for (const loc of STORAGE_LOCATION_TYPES) {
      const override = overrides?.[loc];
      if (override) {
        this.locationPaths.set(loc, path.resolve(override));
      } else {
        const subdir = DEFAULT_LOCATION_SUBDIRS[loc];
        this.locationPaths.set(
          loc,
          path.isAbsolute(subdir) ? subdir : path.join(this.rootPath, subdir)
        );
      }
    }
  }
  resolve({ location, relativePath }) {
    const base = this.locationPaths.get(location);
    if (!base) throw new Error(`Unknown storage location: ${location}`);
    return path.join(base, relativePath);
  }
  async write({ location, relativePath, data }) {
    const filePath = this.resolve({ location, relativePath });
    await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
    if (Buffer.isBuffer(data)) {
      await fs.promises.writeFile(filePath, data);
    } else {
      const writeStream = fs.createWriteStream(filePath);
      await new Promise((resolve, reject) => {
        data.pipe(writeStream);
        writeStream.on("finish", resolve);
        writeStream.on("error", reject);
      });
    }
  }
  async read({ location, relativePath }) {
    return fs.promises.readFile(this.resolve({ location, relativePath }));
  }
  async exists({ location, relativePath }) {
    try {
      await fs.promises.access(this.resolve({ location, relativePath }));
      return true;
    } catch {
      return false;
    }
  }
  async list({ location, prefix }) {
    const base = this.locationPaths.get(location);
    if (!base) return [];
    const dir = prefix ? path.join(base, prefix) : base;
    try {
      const entries = await fs.promises.readdir(dir, { withFileTypes: true });
      return entries.map((e) => prefix ? `${prefix}/${e.name}` : e.name);
    } catch {
      return [];
    }
  }
  async delete({ location, relativePath }) {
    const filePath = this.resolve({ location, relativePath });
    await fs.promises.rm(filePath, { force: true });
  }
  async getAvailableSpace({ location }) {
    const base = this.locationPaths.get(location);
    if (!base) return null;
    try {
      let target = base;
      while (!fs.existsSync(target)) {
        const parent = path.dirname(target);
        if (!parent || parent === target) return null;
        target = parent;
      }
      const stats = await fs.promises.statfs(target);
      return stats.bavail * stats.bsize;
    } catch {
      return null;
    }
  }
  /** Get the resolved path for a location type */
  getLocationPath(location) {
    const p = this.locationPaths.get(location);
    if (!p) throw new Error(`Unknown storage location: ${location}`);
    return p;
  }
  /** Get the root path */
  getRootPath() {
    return this.rootPath;
  }
}
export {
  FilesystemStorageProvider,
  PYTHON_VERSION,
  buildBinaryPath,
  downloadBinary,
  ensureBinary,
  ensureFfmpeg,
  ensurePython,
  findInPath,
  getFfmpegDownloadUrl,
  getPlatformInfo,
  getPythonDownloadUrl,
  installPythonPackages,
  installPythonRequirements
};
//# sourceMappingURL=node.mjs.map
