/**
 * Vercel Edge bundle emitter.
 *
 * Runs as the last step of `mandu build --target=vercel-edge`. Produces:
 *
 *   api/_mandu.ts              — Vercel Edge Function entry (picked up by
 *                                Vercel's file-based routing)
 *   .mandu/vercel/register.ts  — SSR handler registration wiring
 *   .mandu/vercel/manifest.json — cloned RoutesManifest
 *   vercel.json                — generated if absent; preserved otherwise
 *
 * Vercel's build pipeline bundles the Edge Function for us, so we only
 * emit TypeScript sources. The `api/_mandu.ts` entry carries the
 * `export const config = { runtime: "edge" }` marker Vercel requires.
 */

import path from "path";
import fs from "fs/promises";
import { existsSync } from "fs";
import type { RoutesManifest } from "@mandujs/core";
import { generateVercelEdgeConfig } from "@mandujs/edge/vercel";
import { generateRegisterSource, inferProjectName } from "./edge-shared";

const VERCEL_OUTPUT_DIR = ".mandu/vercel";
const VERCEL_ENTRY_FILENAME = "_mandu.ts";
const VERCEL_ENTRY_DIR = "api"; // Vercel requires Edge Functions under `api/`
const VERCEL_REGISTER_FILENAME = "register.ts";
const VERCEL_MANIFEST_FILENAME = "manifest.json";
const VERCEL_CONFIG_FILENAME = "vercel.json";

export interface EmitVercelEdgeBundleOptions {
  /** Absolute path to the project root. */
  rootDir: string;
  /** Parsed routes manifest — same object the main build consumed. */
  manifest: RoutesManifest;
  /** CSS link path (from the main build). Forwarded to the Vercel handler. */
  cssPath: string | false;
  /** Override the Vercel project name. Defaults to a slug of the package name. */
  projectName?: string;
}

export interface EmitVercelEdgeBundleResult {
  /** Absolute path to the generated api/_mandu.ts. */
  edgeEntryPath: string;
  /** Whether vercel.json was newly generated (vs preserved). */
  vercelConfigGenerated: boolean;
  /** Absolute path to vercel.json. */
  vercelConfigPath: string;
}

export async function emitVercelEdgeBundle(
  options: EmitVercelEdgeBundleOptions
): Promise<EmitVercelEdgeBundleResult> {
  const { rootDir, manifest, cssPath, projectName: explicitName } = options;

  console.log(`\n▲ Building Vercel Edge bundle...`);

  const outputDir = path.join(rootDir, VERCEL_OUTPUT_DIR);
  const apiDir = path.join(rootDir, VERCEL_ENTRY_DIR);
  await fs.mkdir(outputDir, { recursive: true });
  await fs.mkdir(apiDir, { recursive: true });

  // 1. Manifest copy under .mandu/vercel/.
  const manifestPath = path.join(outputDir, VERCEL_MANIFEST_FILENAME);
  await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));

  // 2. register.ts under .mandu/vercel/.
  const registerSource = generateRegisterSource(manifest, {
    targetLabel: "vercel-edge",
    rootDir,
    outputDir,
  });
  const registerPath = path.join(outputDir, VERCEL_REGISTER_FILENAME);
  await fs.writeFile(registerPath, registerSource);

  // 3. api/_mandu.ts — the Vercel-visible Edge Function entry.
  const edgeEntryPath = path.join(apiDir, VERCEL_ENTRY_FILENAME);
  const entrySource = generateVercelEdgeEntrySource({
    manifestRelativePath: relativeToApi(apiDir, manifestPath),
    registerRelativePath: relativeToApi(apiDir, registerPath),
    cssPath,
  });
  await fs.writeFile(edgeEntryPath, entrySource);

  // 4. Emit or preserve vercel.json.
  const projectName =
    (explicitName ?? (await inferProjectName(rootDir))) || "mandu-vercel";
  const vercelConfigPath = path.join(rootDir, VERCEL_CONFIG_FILENAME);
  const vercelConfigExists = existsSync(vercelConfigPath);

  if (!vercelConfigExists) {
    const json = generateVercelEdgeConfig({
      projectName,
      functionPath: `${VERCEL_ENTRY_DIR}/${VERCEL_ENTRY_FILENAME}`,
    });
    await fs.writeFile(vercelConfigPath, json);
  }

  console.log(`   ✅ api/${VERCEL_ENTRY_FILENAME}`);
  console.log(`   📄 vercel.json ${vercelConfigExists ? "(preserved)" : "(generated)"}`);
  console.log(`   Output: ${VERCEL_OUTPUT_DIR}/`);
  console.log(`\n   Next: vercel deploy`);

  return {
    edgeEntryPath,
    vercelConfigGenerated: !vercelConfigExists,
    vercelConfigPath,
  };
}

/**
 * Generate `api/_mandu.ts`. Vercel inspects the `config` export — without
 * `runtime: "edge"` our handler would be deployed as a Node.js Serverless
 * Function, which would fail to `import` the edge adapter.
 */
function generateVercelEdgeEntrySource(opts: {
  manifestRelativePath: string;
  registerRelativePath: string;
  cssPath: string | false;
}): string {
  const cssPathExpr =
    opts.cssPath === false ? "false" : JSON.stringify(opts.cssPath);
  return [
    `// Auto-generated by mandu build --target=vercel-edge.`,
    `// Vercel Edge Function entry.`,
    `export const config = { runtime: "edge" };`,
    ``,
    `import manifest from "${opts.manifestRelativePath}" with { type: "json" };`,
    `import { createVercelEdgeHandler } from "@mandujs/edge/vercel";`,
    `import "${opts.registerRelativePath}";`,
    ``,
    `const fetch = createVercelEdgeHandler(manifest as any, {`,
    `  cssPath: ${cssPathExpr},`,
    `});`,
    ``,
    `export default fetch;`,
    ``,
  ].join("\n");
}

function relativeToApi(apiDir: string, absPath: string): string {
  let rel = path.relative(apiDir, absPath).replace(/\\/g, "/");
  if (!rel.startsWith(".")) rel = `./${rel}`;
  return rel;
}
