import fs from 'node:fs';
import path from 'node:path';

export const OPENCLAW_PLUGIN_SDK_PREFIX = 'openclaw/plugin-sdk/';

const OPENCLAW_PLUGIN_SDK_SPECIFIER_RE = /(["'])openclaw\/plugin-sdk\/([^"'\r\n]+)\1/g;

function assertSafePluginSdkSubpath(subpath) {
  if (!subpath || subpath.startsWith('/') || subpath.startsWith('\\')) {
    throw new Error(`Invalid OpenClaw plugin-sdk import subpath: ${JSON.stringify(subpath)}`);
  }

  const parts = subpath.split(/[\\/]+/);
  if (parts.some((part) => !part || part === '.' || part === '..')) {
    throw new Error(`Invalid OpenClaw plugin-sdk import subpath: ${JSON.stringify(subpath)}`);
  }
}

export function toImportSpecifier(relativePath) {
  const normalized = relativePath.split(path.sep).join('/');
  if (normalized.startsWith('./') || normalized.startsWith('../')) {
    return normalized;
  }
  return `./${normalized}`;
}

export function resolvePluginSdkTarget(distDir, subpath) {
  assertSafePluginSdkSubpath(subpath);

  const targetSubpath = subpath.endsWith('.js') ? subpath : `${subpath}.js`;
  return path.join(distDir, 'plugin-sdk', ...targetSubpath.split(/[\\/]+/));
}

export function rewriteOpenClawPluginSdkSpecifiers(content, options) {
  const { filePath, distDir } = options;
  let replacements = 0;

  const nextContent = content.replace(
    OPENCLAW_PLUGIN_SDK_SPECIFIER_RE,
    (match, quote, subpath) => {
      const target = resolvePluginSdkTarget(distDir, subpath);
      if (!fs.existsSync(target)) {
        throw new Error(
          `Cannot rewrite ${match} in ${filePath}: missing bundled SDK target ${target}`,
        );
      }

      replacements++;
      const relativePath = path.relative(path.dirname(filePath), target);
      return `${quote}${toImportSpecifier(relativePath)}${quote}`;
    },
  );

  return {
    content: nextContent,
    replacements,
  };
}

function listJavaScriptFiles(rootDir) {
  const files = [];
  const stack = [rootDir];

  while (stack.length > 0) {
    const current = stack.pop();
    let entries;
    try {
      entries = fs.readdirSync(current, { withFileTypes: true });
    } catch {
      continue;
    }

    for (const entry of entries) {
      const fullPath = path.join(current, entry.name);
      if (entry.isDirectory()) {
        stack.push(fullPath);
        continue;
      }
      if (entry.isFile() && entry.name.endsWith('.js')) {
        files.push(fullPath);
      }
    }
  }

  return files;
}

export function patchExtensionOpenClawSelfImports(outputDir) {
  const distDir = path.join(outputDir, 'dist');
  const extensionsDir = path.join(distDir, 'extensions');
  if (!fs.existsSync(extensionsDir)) {
    return {
      filesScanned: 0,
      filesPatched: 0,
      specifiersPatched: 0,
    };
  }

  let filesScanned = 0;
  let filesPatched = 0;
  let specifiersPatched = 0;

  for (const filePath of listJavaScriptFiles(extensionsDir)) {
    filesScanned++;
    const content = fs.readFileSync(filePath, 'utf8');
    if (!content.includes(OPENCLAW_PLUGIN_SDK_PREFIX)) {
      continue;
    }

    const result = rewriteOpenClawPluginSdkSpecifiers(content, {
      filePath,
      distDir,
    });

    if (result.replacements > 0 && result.content !== content) {
      fs.writeFileSync(filePath, result.content, 'utf8');
      filesPatched++;
      specifiersPatched += result.replacements;
    }
  }

  return {
    filesScanned,
    filesPatched,
    specifiersPatched,
  };
}