import { readdir, rm, writeFile } from "node:fs/promises";
import { join, relative } from "node:path";
import { fileURLToPath } from "node:url";

import { validateVersion } from "./bump-version-logic";

export const projectRoot = fileURLToPath(new URL("..", import.meta.url));
export const distWebDir = join(projectRoot, "dist/web");
export const buildDir = join(projectRoot, ".build");
export const packageJsonPath = join(projectRoot, "package.json");

export async function cleanup() {
  await rm(buildDir, { force: true, recursive: true });
}

export async function codeGeneration() {
  console.log("Step 2/3: Code generation...");
  await rm(buildDir, { force: true, recursive: true });
  await Bun.write(join(buildDir, ".gitkeep"), "");

  const packageJson = (await Bun.file(packageJsonPath).json()) as { version: string };
  const version = packageJson.version;
  if (typeof version !== "string") {
    console.error("package.json does not have a valid version field");
    process.exit(1);
  }
  validateVersion(version);

  const allFiles = await scanDir(distWebDir, "/");
  const importLines: string[] = [];
  const fileEntries: string[] = [];
  let indexHtmlVar = "";

  for (let i = 0; i < allFiles.length; i++) {
    const urlPath = allFiles[i]!;
    const varName = `f${i}`;
    const filePath = toImportSpecifier(buildDir, join(distWebDir, urlPath.slice(1)));
    importLines.push(`import ${varName} from "./${filePath}" with { type: "file" };`);

    if (urlPath === "/index.html") {
      indexHtmlVar = varName;
    } else {
      fileEntries.push(`  "${urlPath}": Bun.file(${varName}),`);
    }
  }

  if (!indexHtmlVar) {
    console.error("index.html not found in dist/web/");
    process.exit(1);
  }

  const staticAssetsTs = [
    `import type { StaticAssets } from "../src/server/static";`,
    "",
    ...importLines,
    "",
    `export const staticAssets: StaticAssets = {`,
    `  files: {`,
    ...fileEntries,
    `  },`,
    `  indexHtml: Bun.file(${indexHtmlVar}),`,
    `};`,
    "",
  ].join("\n");

  await writeFile(join(buildDir, "static-assets.ts"), staticAssetsTs);

  const serverEntryTs = [
    `import { bootstrap } from "../src/server/bootstrap";`,
    `import { readRuntimeConfig } from "../src/server/config";`,
    `import { staticAssets } from "./static-assets";`,
    "",
    `const APP_VERSION = "${version}" as const;`,
    "",
    `async function main() {`,
    `  const { configPath } = readRuntimeConfig();`,
    `  await bootstrap({ configPath, mode: "production", staticAssets, version: APP_VERSION });`,
    `}`,
    "",
    `void main().catch((error) => {`,
    `  console.error("启动失败:", error instanceof Error ? error.message : error);`,
    `  process.exit(1);`,
    `});`,
    "",
  ].join("\n");

  await writeFile(join(buildDir, "server-entry.ts"), serverEntryTs);

  return version;
}

export async function scanDir(dir: string, prefix: string): Promise<string[]> {
  const entries = await readdir(dir, { withFileTypes: true });
  const paths: string[] = [];
  for (const entry of entries) {
    const fullPath = join(dir, entry.name);
    const urlPath = `${prefix}${entry.name}`;
    if (entry.isDirectory()) {
      paths.push(...(await scanDir(fullPath, `${urlPath}/`)));
    } else {
      paths.push(urlPath);
    }
  }
  return paths;
}

export function toImportSpecifier(
  fromDir: string,
  targetPath: string,
  relativePath: (from: string, to: string) => string = relative,
) {
  return relativePath(fromDir, targetPath).replaceAll("\\", "/");
}

export async function viteBuild() {
  console.log("Step 1/3: Vite build...");
  const proc = Bun.spawn(["bunx", "--bun", "vite", "build"], {
    cwd: projectRoot,
    stderr: "inherit",
    stdout: "inherit",
  });
  const exitCode = await proc.exited;
  if (exitCode !== 0) {
    console.error("Vite build failed");
    process.exit(1);
  }
}
