import { chmod, mkdir, rm, writeFile } from 'node:fs/promises';
import path from 'node:path';

import { assertContainedRealPath } from './path-containment';

export type MaterializedSecret = { name: string; value: string };

// Format matches the historical `NAME="value"` env-file layout the agent CLIs
// source; values are JSON-quoted so newlines/quotes survive.
export const formatMaterializedEnv = (secrets: MaterializedSecret[]) =>
  `${secrets
    .map((secret) => `${secret.name}=${JSON.stringify(secret.value)}`)
    .join('\n')}\n`;

// Resolves the contained env-file path inside the checkout and writes it 0600
// so plaintext secrets never sit world/group-readable in the persistent home.
export const writeMaterializedEnv = async (
  repoDir: string,
  envFilePath: string,
  secrets: MaterializedSecret[],
): Promise<string> => {
  const envPath = await assertContainedRealPath(repoDir, envFilePath, {
    forWrite: true,
  });
  await mkdir(path.dirname(envPath), { recursive: true });
  await writeFile(envPath, formatMaterializedEnv(secrets), { mode: 0o600 });
  // writeFile's mode only applies on create; chmod unconditionally tightens an
  // already-existing (possibly 0644) file so plaintext secrets never leak.
  await chmod(envPath, 0o600);
  return envPath;
};

// Removes a previously materialized env file; no-ops when the path is unset or
// already gone so it is safe on every teardown path.
export const removeMaterializedEnv = async (envPath: string | undefined) => {
  if (!envPath) return;
  await rm(envPath, { force: true });
};
