import { promises as fs } from "node:fs";
import path from "node:path";
import { NextResponse } from "next/server";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const DEFAULT_GENERATED_ROOT = path.resolve(
  process.cwd(),
  "..",
  "rust",
  "crates",
  "tree-shell-runtime-wasm",
  "generated",
);
const JS_ASSET_NAME = "mnote-tree-shell-runtime.js";
const WASM_ASSET_NAME = "mnote-tree-shell-runtime_bg.wasm";

type TreeShellRuntimeManifest = {
  jsGlueAssetPath: string;
  wasmAssetPath: string;
  assetPaths: string[];
  generatedRootPath: string;
};

function toPosixPath(value: string): string {
  return value.split(path.sep).join("/");
}

function resolveGeneratedRoot(): string {
  return path.resolve(process.env.TREE_SHELL_RUNTIME_GENERATED_ROOT || DEFAULT_GENERATED_ROOT);
}

function toAssetRoutePath(fileName: string): string {
  return `/api/tree-shell-runtime/${fileName}`;
}

function sanitizeRelativePath(asset: string[]): string | null {
  if (!Array.isArray(asset) || asset.length === 0) {
    return null;
  }
  const decoded = asset.map((segment) => decodeURIComponent(segment));
  const joined = decoded.join("/");
  if (!joined || joined.includes("\0")) {
    return null;
  }
  const normalized = path.posix.normalize(joined);
  if (normalized === "." || normalized.startsWith("../") || normalized.includes("/../")) {
    return null;
  }
  return normalized;
}

async function fileExists(absolutePath: string): Promise<boolean> {
  try {
    await fs.access(absolutePath);
    return true;
  } catch {
    return false;
  }
}

async function buildManifest(): Promise<TreeShellRuntimeManifest> {
  const generatedRoot = resolveGeneratedRoot();
  const assetCandidates = [JS_ASSET_NAME, WASM_ASSET_NAME];
  const existingAssets: string[] = [];

  for (const assetName of assetCandidates) {
    const absolutePath = path.join(generatedRoot, assetName);
    if (await fileExists(absolutePath)) {
      existingAssets.push(toAssetRoutePath(assetName));
    }
  }

  return {
    jsGlueAssetPath: toAssetRoutePath(JS_ASSET_NAME),
    wasmAssetPath: toAssetRoutePath(WASM_ASSET_NAME),
    assetPaths: existingAssets,
    generatedRootPath: toPosixPath(generatedRoot),
  };
}

function guessContentType(absolutePath: string): string {
  const extension = path.extname(absolutePath).toLowerCase();
  if (extension === ".js" || extension === ".mjs") {
    return "application/javascript; charset=utf-8";
  }
  if (extension === ".wasm") {
    return "application/wasm";
  }
  if (extension === ".json") {
    return "application/json; charset=utf-8";
  }
  return "application/octet-stream";
}

export async function GET(
  _request: Request,
  context: { params: Promise<{ asset?: string[] }> },
) {
  const params = await context.params;
  const asset = params.asset ?? [];

  if (asset.length === 1 && asset[0] === "manifest.json") {
    try {
      const manifest = await buildManifest();
      return NextResponse.json(manifest, {
        headers: { "Cache-Control": "no-store" },
      });
    } catch (error) {
      return NextResponse.json(
        {
          error: "无法生成 tree shell runtime 清单",
          detail: error instanceof Error ? error.message : "unknown",
        },
        { status: 500 },
      );
    }
  }

  const relativePath = sanitizeRelativePath(asset);
  if (!relativePath) {
    return NextResponse.json({ error: "非法 tree shell runtime 资源路径" }, { status: 400 });
  }

  const generatedRoot = resolveGeneratedRoot();
  const absolutePath = path.resolve(generatedRoot, relativePath);
  if (!absolutePath.startsWith(generatedRoot + path.sep)) {
    return NextResponse.json({ error: "越界访问 tree shell runtime 资源被拒绝" }, { status: 403 });
  }

  try {
    const fileContent = await fs.readFile(absolutePath);
    return new NextResponse(fileContent, {
      status: 200,
      headers: {
        "Content-Type": guessContentType(absolutePath),
        "Cache-Control": "no-store",
      },
    });
  } catch (error) {
    if ((error as NodeJS.ErrnoException)?.code === "ENOENT") {
      return NextResponse.json({ error: "tree shell runtime 资源不存在" }, { status: 404 });
    }
    return NextResponse.json(
      {
        error: "读取 tree shell runtime 资源失败",
        detail: error instanceof Error ? error.message : "unknown",
      },
      { status: 500 },
    );
  }
}
