import { createHash } from "node:crypto";
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { basename, join, relative } from "node:path";

export interface StagedFileIntegrity {
  path: string;
  bytes: number;
  sha256: string;
}

export interface StagedPackageIntegrity {
  name: string;
  version: string;
  files: StagedFileIntegrity[];
}

const forbiddenStageName =
  /^(?:\.env(?:\..*)?|focus-shims\.d\.ts|tsconfig\.focus\.json|id_rsa|id_ed25519|.*\.(?:pem|key|p12|pfx))$/i;
const highConfidenceSecret =
  /-----BEGIN ((?:RSA |EC |OPENSSH )?PRIVATE KEY)-----\r?\n[A-Za-z0-9+/=\r\n]{80,}\r?\n-----END \1-----|\bAKIA[0-9A-Z]{16}\b|\bnpm_[A-Za-z0-9]{30,}\b|\bgh[pousr]_[A-Za-z0-9]{30,}\b/;

function listFiles(root: string): string[] {
  const files: string[] = [];
  for (const entry of readdirSync(root, { recursive: true, withFileTypes: true })) {
    if (!entry.isFile()) continue;
    files.push(join(entry.parentPath, entry.name));
  }
  return files.sort((a, b) => {
    const left = relative(root, a).replace(/\\/g, "/");
    const right = relative(root, b).replace(/\\/g, "/");
    return left < right ? -1 : left > right ? 1 : 0;
  });
}

function manifestTargets(value: unknown): string[] {
  if (typeof value === "string") return [value];
  if (!value || typeof value !== "object") return [];
  return Object.values(value).flatMap(manifestTargets);
}

export function validateStagedManifest(stage: string, manifest: Record<string, unknown>): void {
  const name = String(manifest.name ?? "unknown package");
  const requiredStrings = ["name", "version", "description", "license", "main", "types"];
  for (const field of requiredStrings) {
    if (typeof manifest[field] !== "string" || !String(manifest[field]).trim()) {
      throw new Error(`${name} staged manifest requires a non-empty ${field}.`);
    }
  }
  if (!/^@wrnexus\/[a-z0-9-]+$/.test(name)) throw new Error(`${name} has an invalid package name.`);
  if (manifest.private !== undefined) throw new Error(`${name} staged manifest must omit private.`);
  if ((manifest.publishConfig as any)?.registry !== "https://registry.npmjs.org/") {
    throw new Error(`${name} staged manifest has an unexpected registry.`);
  }
  if (!["restricted", "public"].includes(String((manifest.publishConfig as any)?.access))) {
    throw new Error(`${name} staged manifest requires explicit public or restricted access.`);
  }
  if ((manifest.engines as any)?.bun !== ">=1.3.0") {
    throw new Error(`${name} staged manifest requires the supported Bun engine.`);
  }
  for (const field of ["repository", "homepage", "bugs", "keywords", "files", "exports"]) {
    if (manifest[field] === undefined)
      throw new Error(`${name} staged manifest requires ${field}.`);
  }
  for (const ranges of [
    manifest.dependencies,
    manifest.optionalDependencies,
    manifest.peerDependencies,
  ]) {
    if (!ranges || typeof ranges !== "object") continue;
    for (const [dependency, range] of Object.entries(ranges)) {
      if (String(range).startsWith("workspace:")) {
        throw new Error(`${name} leaked workspace range for ${dependency}.`);
      }
    }
  }

  const targets = [
    ...manifestTargets(manifest.main),
    ...manifestTargets(manifest.module),
    ...manifestTargets(manifest.types),
    ...manifestTargets(manifest.bin),
    ...manifestTargets(manifest.exports),
  ];
  for (const target of new Set(targets)) {
    if (!target.startsWith("./")) throw new Error(`${name} has unsafe package target ${target}.`);
    const normalized = target.slice(2);
    const concrete = normalized.includes("*")
      ? normalized.slice(0, normalized.indexOf("*"))
      : normalized;
    if (!existsSync(join(stage, concrete))) {
      throw new Error(`${name} package target does not exist: ${target}.`);
    }
  }
}

export function validateAndHashStage(
  stage: string,
  manifest: Record<string, unknown>,
): StagedPackageIntegrity {
  const name = String(manifest.name ?? "unknown package");
  validateStagedManifest(stage, manifest);
  const files = listFiles(stage);
  if (!files.length) throw new Error(`${name} staged no files.`);
  const integrity: StagedFileIntegrity[] = [];

  for (const file of files) {
    const path = relative(stage, file).replace(/\\/g, "/");
    const filename = basename(file);
    if (forbiddenStageName.test(filename)) {
      throw new Error(`${name} staged forbidden secret-like file ${path}.`);
    }
    if (path.endsWith(".map")) {
      throw new Error(`${name} staged source map ${path}; package source maps are disabled.`);
    }
    if (path.endsWith(".ts") && !path.endsWith(".d.ts")) {
      throw new Error(`${name} staged TypeScript source ${path}.`);
    }

    const value = readFileSync(file);
    if (value.byteLength <= 2 * 1024 * 1024) {
      const source = value.toString("utf8");
      if (highConfidenceSecret.test(source)) {
        throw new Error(`${name} staged content matching a high-confidence secret in ${path}.`);
      }
    }
    integrity.push({
      path,
      bytes: value.byteLength,
      sha256: createHash("sha256").update(value).digest("hex"),
    });
  }

  return {
    name,
    version: String(manifest.version ?? "unknown"),
    files: integrity,
  };
}
