import { readFileSync, writeFileSync, existsSync } from "node:fs";
import { join } from "node:path";

const MANIFEST_FILE = ".release-please-manifest.json";
const CONFIG_FILE = "release-please-config.json";
const INITIAL_VERSION = "0.1.0";

/**
 * Register a new feature package in release-please tracking (ADR-021).
 *
 * Mutates two files at the repo root:
 *   - `.release-please-manifest.json` — adds `"packages/<name>": "0.1.0"`
 *   - `release-please-config.json` — adds the per-package config block
 *
 * Idempotent: if the package is already tracked, both writes are no-ops.
 *
 * Throws if either file is missing — release-please MUST be set up before
 * a feature generator can register against it (ADR-021 land-down sequence).
 */
export function registerFeatureInReleasePlease(
  repoRoot: string,
  featureKebab: string,
): { manifestChanged: boolean; configChanged: boolean } {
  const manifestPath = join(repoRoot, MANIFEST_FILE);
  const configPath = join(repoRoot, CONFIG_FILE);

  if (!existsSync(manifestPath)) {
    throw new Error(
      `Cannot register feature in release-please: ${MANIFEST_FILE} is missing. ` +
        `release-please must be set up before scaffolding features that participate in tracking.`,
    );
  }
  if (!existsSync(configPath)) {
    throw new Error(
      `Cannot register feature in release-please: ${CONFIG_FILE} is missing.`,
    );
  }

  const packagePath = `packages/${featureKebab}`;
  const manifestChanged = addToManifest(manifestPath, packagePath);
  const configChanged = addToConfig(configPath, packagePath, featureKebab);

  return { manifestChanged, configChanged };
}

function addToManifest(manifestPath: string, packagePath: string): boolean {
  const text = readFileSync(manifestPath, "utf8");
  const manifest = JSON.parse(text) as Record<string, string>;
  if (manifest[packagePath]) return false; // already tracked
  manifest[packagePath] = INITIAL_VERSION;
  // Sort keys: root (".") stays first if present; the rest alphabetically by
  // path so insertions are deterministic + future diffs stay minimal.
  const sorted: Record<string, string> = {};
  if (manifest["."] !== undefined) sorted["."] = manifest["."];
  for (const key of Object.keys(manifest).sort()) {
    if (key === ".") continue;
    sorted[key] = manifest[key];
  }
  writeFileSync(manifestPath, JSON.stringify(sorted, null, 2) + "\n");
  return true;
}

function addToConfig(
  configPath: string,
  packagePath: string,
  featureKebab: string,
): boolean {
  const text = readFileSync(configPath, "utf8");
  const config = JSON.parse(text) as {
    packages?: Record<
      string,
      {
        "package-name": string;
        component: string;
        "changelog-path"?: string;
      }
    >;
    [k: string]: unknown;
  };
  if (!config.packages) {
    throw new Error(
      `release-please-config.json has no "packages" key — config is malformed.`,
    );
  }
  if (config.packages[packagePath]) return false; // already tracked
  config.packages[packagePath] = {
    "package-name": `@repo/${featureKebab}`,
    component: featureKebab,
    "changelog-path": "CHANGELOG.md",
  };
  // Sort packages the same way as the manifest: root first, rest alphabetical.
  const sortedPackages: typeof config.packages = {};
  if (config.packages["."]) sortedPackages["."] = config.packages["."];
  for (const key of Object.keys(config.packages).sort()) {
    if (key === ".") continue;
    sortedPackages[key] = config.packages[key];
  }
  config.packages = sortedPackages;
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
  return true;
}
