#!/usr/bin/env bun
// Canonicalize Bun-managed install indirection without modifying package contents.

import {
  lstat,
  readdir,
  readlink,
  symlink,
  unlink,
} from "node:fs/promises";
import { dirname, join, relative, resolve } from "node:path";

const nodeModulesRoot = resolve(process.cwd(), process.argv[2] ?? "node_modules");

async function pathExists(path: string): Promise<boolean> {
  try {
    await lstat(path);
    return true;
  } catch {
    return false;
  }
}

function lexicalSort(values: Iterable<string>): string[] {
  return [...values].sort((left, right) => left.localeCompare(right, "en"));
}

function normalizeRelativeTarget(fromDir: string, targetPath: string): string {
  const target = relative(fromDir, targetPath) || ".";
  return target.replaceAll(/\/+/g, "/");
}

async function rewriteSymlink(linkPath: string, targetPath: string): Promise<void> {
  const nextTarget = normalizeRelativeTarget(dirname(linkPath), targetPath);
  const currentTarget = await readlink(linkPath).catch(() => null);
  if (currentTarget === nextTarget) {
    return;
  }

  await unlink(linkPath);
  await symlink(nextTarget, linkPath);
}

async function canonicalizeManagedSymlink(linkPath: string): Promise<void> {
  const linkDir = dirname(linkPath);
  const currentTarget = await readlink(linkPath).catch(() => null);
  if (currentTarget === null || currentTarget.length === 0) {
    return;
  }

  const resolvedTarget = resolve(linkDir, currentTarget);
  if (await pathExists(resolvedTarget)) {
    await rewriteSymlink(linkPath, resolvedTarget);
    return;
  }

  const normalizedBrokenTarget = normalizeRelativeTarget(linkDir, resolvedTarget);
  if (currentTarget !== normalizedBrokenTarget) {
    await unlink(linkPath);
    await symlink(normalizedBrokenTarget, linkPath);
  }
}

async function readSortedDir(path: string): Promise<string[]> {
  return lexicalSort(await readdir(path));
}

function isNodeModulesPackageEntry(path: string): boolean {
  const parent = dirname(path);
  const grandparent = dirname(parent);
  return parent.endsWith("/node_modules") || grandparent.endsWith("/node_modules");
}

async function walkNodeModules(path: string): Promise<void> {
  const entries = await readSortedDir(path);
  for (const entry of entries) {
    const entryPath = join(path, entry);
    const stats = await lstat(entryPath);

    if (stats.isSymbolicLink()) {
      const relativePath = relative(nodeModulesRoot, entryPath);
      const inBunStore = relativePath === ".bun" || relativePath.startsWith(".bun/");
      const isWorkspaceLink = isNodeModulesPackageEntry(entryPath);
      if (inBunStore || isWorkspaceLink) {
        await canonicalizeManagedSymlink(entryPath);
      }
      continue;
    }

    if (!stats.isDirectory()) {
      continue;
    }

    if (entry === ".bin") {
      continue;
    }

    await walkNodeModules(entryPath);
  }
}

if (await pathExists(nodeModulesRoot)) {
  await walkNodeModules(nodeModulesRoot);
}
