import path from 'path';

import { STARTUP_PRECONDITION_EXIT_CODE } from '../src/startup-preconditions.js';
import type { ServiceDef } from './service-defs.js';

export function buildRuntimePathEnv(nodePath: string, homeDir: string): string {
  return `${path.dirname(nodePath)}:/usr/local/bin:/usr/bin:/bin:${homeDir}/.local/bin:${homeDir}/.npm-global/bin`;
}

function buildLaunchdEnvironmentEntries(
  nodePath: string,
  homeDir: string,
  extraEnv?: Record<string, string>,
): string[] {
  const envEntries = [
    `        <key>PATH</key>`,
    `        <string>${buildRuntimePathEnv(nodePath, homeDir)}</string>`,
    `        <key>HOME</key>`,
    `        <string>${homeDir}</string>`,
  ];
  if (extraEnv) {
    for (const [k, v] of Object.entries(extraEnv)) {
      envEntries.push(`        <key>${k}</key>`);
      envEntries.push(`        <string>${v}</string>`);
    }
  }
  return envEntries;
}

function buildSystemdEnvironmentLines(
  nodePath: string,
  homeDir: string,
  extraEnv?: Record<string, string>,
): string[] {
  const envLines = [
    `Environment=HOME=${homeDir}`,
    `Environment=PATH=${buildRuntimePathEnv(nodePath, homeDir)}`,
  ];
  if (extraEnv) {
    for (const [k, v] of Object.entries(extraEnv)) {
      envLines.push(`Environment=${k}=${v}`);
    }
  }
  return envLines;
}

export function buildLaunchdPlist(
  def: ServiceDef,
  projectRoot: string,
  nodePath: string,
  homeDir: string,
): string {
  const envEntries = buildLaunchdEnvironmentEntries(
    nodePath,
    homeDir,
    def.extraEnv,
  );

  return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${def.launchdLabel}</string>
    <key>ProgramArguments</key>
    <array>
        <string>${nodePath}</string>
        <string>${projectRoot}/dist/index.js</string>
    </array>
    <key>WorkingDirectory</key>
    <string>${projectRoot}</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>EnvironmentVariables</key>
    <dict>
${envEntries.join('\n')}
    </dict>
    <key>StandardOutPath</key>
    <string>${projectRoot}/logs/${def.logName}.log</string>
    <key>StandardErrorPath</key>
    <string>${projectRoot}/logs/${def.logName}.error.log</string>
</dict>
</plist>`;
}

export function buildSystemdUnit(
  def: ServiceDef,
  projectRoot: string,
  nodePath: string,
  homeDir: string,
  runningAsRoot: boolean,
): string {
  const envLines = buildSystemdEnvironmentLines(
    nodePath,
    homeDir,
    def.extraEnv,
  );

  const envFileLine = def.environmentFile
    ? `EnvironmentFile=${def.environmentFile}\n`
    : '';

  return `[Unit]
Description=${def.description}
After=network.target

[Service]
${envFileLine}Type=simple
ExecStart=${nodePath} ${projectRoot}/dist/index.js
WorkingDirectory=${projectRoot}
Restart=always
RestartSec=5
RestartPreventExitStatus=${STARTUP_PRECONDITION_EXIT_CODE}
# Per-turn agent runners do heavy file I/O (git clone, builds, video/ffmpeg
# scratch) inside this service cgroup. The kernel keeps those reads as
# reclaimable page cache, which makes systemd's MemoryCurrent climb to several
# GB even though real (anon) usage stays a few hundred MB. MemoryHigh applies
# gentle reclaim pressure so that cache is dropped before the number balloons,
# while MemoryMax stays unset (infinity) so a legitimate burst is never
# OOM-killed — it is throttled at worst.
MemoryAccounting=yes
MemoryHigh=3G
${envLines.join('\n')}
StandardOutput=append:${projectRoot}/logs/${def.logName}.log
StandardError=append:${projectRoot}/logs/${def.logName}.error.log

[Install]
WantedBy=${runningAsRoot ? 'multi-user.target' : 'default.target'}`;
}

export function buildStackRestartSystemdUnit(
  projectRoot: string,
  nodePath: string,
  homeDir: string,
): string {
  const envLines = buildSystemdEnvironmentLines(nodePath, homeDir);

  return `[Unit]
Description=EJClaw Stack Restart Orchestrator
After=network.target

[Service]
Type=oneshot
WorkingDirectory=${projectRoot}
${envLines.join('\n')}
ExecStart=${nodePath} ${projectRoot}/setup/restart-stack.ts --direct
`;
}
