const fs = require("fs");
const path = require("path");

const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[0-9A-Za-z.-]+)?$/;

function readJsonWithTrailingNewline(filePath) {
  const raw = fs.readFileSync(filePath, "utf8");
  return { data: JSON.parse(raw), hadTrailingNewline: raw.endsWith("\n") };
}

function writeJson(filePath, data, hadTrailingNewline) {
  const serialized = JSON.stringify(data, null, 2);
  fs.writeFileSync(
    filePath,
    hadTrailingNewline ? serialized + "\n" : serialized,
  );
}

function syncVersion(version, options = {}) {
  if (!SEMVER.test(version || "")) {
    throw new Error(`invalid version "${version}" (expected X.Y.Z)`);
  }

  const root = options.root || path.join(__dirname, "..");
  const pkgPath = path.join(root, "package.json");
  const lockPath = path.join(root, "package-lock.json");

  const changed = [];

  const pkg = readJsonWithTrailingNewline(pkgPath);
  if (pkg.data.version !== version) {
    pkg.data.version = version;
    writeJson(pkgPath, pkg.data, pkg.hadTrailingNewline);
    changed.push("package.json");
  }

  if (fs.existsSync(lockPath)) {
    const lock = readJsonWithTrailingNewline(lockPath);
    let lockChanged = false;

    if (lock.data.version !== version) {
      lock.data.version = version;
      lockChanged = true;
    }
    if (lock.data.packages && lock.data.packages[""]) {
      if (lock.data.packages[""].version !== version) {
        lock.data.packages[""].version = version;
        lockChanged = true;
      }
    }

    if (lockChanged) {
      writeJson(lockPath, lock.data, lock.hadTrailingNewline);
      changed.push("package-lock.json");
    }
  }

  return changed;
}

module.exports = { syncVersion };

if (require.main === module) {
  const args = process.argv.slice(2);
  const idx = args.indexOf("--version");
  const version = idx !== -1 ? args[idx + 1] : undefined;

  try {
    const changed = syncVersion(version);
    if (changed.length === 0) {
      console.log(`sync-version: already at ${version}, no change`);
    } else {
      console.log(`sync-version: set ${version} in ${changed.join(", ")}`);
    }
  } catch (err) {
    console.error(`sync-version: ${err.message}`);
    process.exit(1);
  }
}
