/**
 * RE-R3c — pure string emitters for the autonomous-trigger artifacts (research-engine).
 *
 * No clock, no fs, no env, no AI: every value the emitters render is injected via `ScheduleSpec`
 * (the CLI is the only edge that reads `process.execPath` / `import.meta.url` / `defaultStorePath`).
 * Mirrors `brief.ts`'s `renderBrief` purity → byte-deterministic given inputs, fully testable.
 *
 * Print-first (C2): these are STRINGS. `launchdPlist`/`crontabLine` emit the artifact; the install/
 * uninstall instructions emit the exact command the operator runs. Nothing here ever executes
 * `launchctl` or the cron table — the emitted recipes are surfaced for the operator to run.
 */

export interface ScheduleSpec {
  platform: "launchd" | "cron";
  label: string;
  nodeBin: string;
  wrapperPath: string;
  args: string[];
  hour: number;
  minute: number;
  logPath: string;
  workingDir: string;
  /** Canonical injected environment — always { NODE_BIN, LINKEDIN_STUDIO_DATA(resolved-absolute) }. */
  env: Record<string, string>;
}

/** Defensive XML escaping for well-formedness (paths are normally safe, but escape regardless). */
function xmlEscape(s: string): string {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

/**
 * The launchd plist (macOS). A pinned, well-formed `<?xml … !DOCTYPE plist …>` template firing
 * the wrapper daily via StartCalendarInterval. `RunAtLoad` is false (a calendar job, not boot-time).
 */
export function launchdPlist(spec: ScheduleSpec): string {
  const programArguments = ["/bin/bash", spec.wrapperPath, ...spec.args]
    .map((a) => `      <string>${xmlEscape(a)}</string>`)
    .join("\n");
  const environment = Object.entries(spec.env)
    .map(([k, v]) => `      <key>${xmlEscape(k)}</key>\n      <string>${xmlEscape(v)}</string>`)
    .join("\n");
  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>${xmlEscape(spec.label)}</string>
  <key>ProgramArguments</key>
  <array>
${programArguments}
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Hour</key>
    <integer>${spec.hour}</integer>
    <key>Minute</key>
    <integer>${spec.minute}</integer>
  </dict>
  <key>EnvironmentVariables</key>
  <dict>
${environment}
  </dict>
  <key>WorkingDirectory</key>
  <string>${xmlEscape(spec.workingDir)}</string>
  <key>StandardOutPath</key>
  <string>${xmlEscape(spec.logPath)}</string>
  <key>StandardErrorPath</key>
  <string>${xmlEscape(spec.logPath)}</string>
  <key>RunAtLoad</key>
  <false/>
</dict>
</plist>
`;
}

/**
 * The cron schedule line (Linux). Returns the line as a STRING; never executes the cron table.
 * `<min> <hour> * * * <env-prefix> /bin/bash <wrapper> <args…> >> <log> 2>&1  # <label>`.
 */
export function crontabLine(spec: ScheduleSpec): string {
  const envPrefix = Object.entries(spec.env)
    .map(([k, v]) => `${k}=${v}`)
    .join(" ");
  const prefix = envPrefix.length > 0 ? `${envPrefix} ` : "";
  return (
    `${spec.minute} ${spec.hour} * * * ${prefix}/bin/bash ${spec.wrapperPath} ` +
    `${spec.args.join(" ")} >> ${spec.logPath} 2>&1  # ${spec.label}`
  );
}

/** The exact activation command for the operator to run (print-first; the tool never runs it). */
export function installInstructions(spec: ScheduleSpec, plistTargetPath?: string): string {
  if (spec.platform === "launchd") {
    return `Wrote ${plistTargetPath}. Activate it with:\n  launchctl bootstrap gui/$(id -u) ${plistTargetPath}`;
  }
  return `Add the line above to your schedule with:\n  (crontab -l 2>/dev/null; echo '${crontabLine(spec)}') | crontab -`;
}

/** The symmetric removal command (print-first). */
export function uninstallInstructions(spec: ScheduleSpec, plistTargetPath?: string): string {
  if (spec.platform === "launchd") {
    return `Deactivate + remove with:\n  launchctl bootout gui/$(id -u)/${spec.label} && rm ${plistTargetPath}`;
  }
  return `Remove the scheduled line with:\n  crontab -l | grep -vF '# ${spec.label}' | crontab -`;
}

/** The reverse-DNS plugin namespace — domain-general, no vendor/sector token. */
export function defaultLabel(): string {
  return "com.linkedin-studio.trends.daily";
}
