import { execFile } from "node:child_process";
import { randomUUID } from "node:crypto";
import { copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { promisify } from "node:util";
import { mergeWindowsConfiguration } from "./windows-config-sync-lib.mjs";

const exec = promisify(execFile);
const sourceRoot = resolve(import.meta.dirname, "..");
const developmentPath = join(sourceRoot, "config.local.json");
const localData = process.env.LOCALAPPDATA;
if (!localData) throw new Error("local_app_data_unavailable");
const installedDirectory = join(localData, "CodeRemoteManager");
const installedPath = join(installedDirectory, "config.local.json");

let installedText;
try {
  installedText = await readFile(installedPath, "utf8");
} catch (error) {
  if (error.code === "ENOENT") {
    console.log("Windows configuration is not installed; nothing to synchronize.");
    process.exit(0);
  }
  throw error;
}

const development = JSON.parse(await readFile(developmentPath, "utf8"));
const installed = JSON.parse(installedText);
const merged = mergeWindowsConfiguration(installed, development);
const backupDirectory = join(installedDirectory, "config-backups");
const suffix = `${new Date().toISOString().replaceAll(/[:.]/g, "")}.${randomUUID().replaceAll("-", "")}`;
const backupPath = join(backupDirectory, `config.local.sync.${suffix}.json`);
const pendingPath = join(installedDirectory, `config.local.${process.pid}.${randomUUID()}.pending`);
const validatorPath = join(sourceRoot, "apps", "backend", "src", "config-validator-cli.js");

await mkdir(backupDirectory, { recursive: true });
await writeFile(pendingPath, `${JSON.stringify(merged, null, 2)}\n`, { flag: "wx" });
try {
  await validate(pendingPath);
  await copyFile(installedPath, backupPath);
  try {
    await copyFile(pendingPath, installedPath);
    await validate(installedPath);
  } catch (error) {
    await copyFile(backupPath, installedPath);
    await validate(installedPath);
    throw error;
  }
  await pruneSyncBackups(backupDirectory, 3);
} finally {
  await rm(pendingPath, { force: true });
}

console.log(`Windows configuration synchronized (${merged.accounts.length} accounts, ${merged.workspaces.length} workspaces).`);

async function validate(configPath) {
  await exec(process.execPath, [validatorPath], {
    cwd: sourceRoot,
    env: { ...process.env, CODE_REMOTE_MANAGER_CONFIG_PATH: configPath },
    windowsHide: true,
    maxBuffer: 1024 * 1024
  });
}

async function pruneSyncBackups(directory, keep) {
  const entries = await readdir(directory, { withFileTypes: true });
  const backups = [];
  for (const entry of entries) {
    if (!entry.isFile() || !entry.name.startsWith("config.local.sync.") || !entry.name.endsWith(".json")) continue;
    const path = join(directory, entry.name);
    backups.push({ path, modified: (await stat(path)).mtimeMs });
  }
  backups.sort((left, right) => right.modified - left.modified || right.path.localeCompare(left.path));
  await Promise.all(backups.slice(keep).map((backup) => rm(backup.path, { force: true })));
}
