/**
 * Cloudflare Pages adapter.
 *
 * Defers bundle generation to the existing `@mandujs/edge/workers` +
 * `mandu build --target=workers` pipeline and only emits the extra
 * Pages-specific bits: a `functions/_middleware.ts` stub (used when the
 * project opts into Pages Functions) and an augmented `wrangler.toml`
 * pointing at the Pages project name.
 *
 * ## Phase 15 vs Phase 13 split
 *
 * Phase 13 ships **artifact-only** behaviour per R0 guidance — the
 * runtime correctness of the edge bundle is Phase 15's responsibility.
 * We emit a warning on every run so users aren't surprised when SSR
 * semantics differ from the Bun/Node target.
 *
 * References:
 *   - https://developers.cloudflare.com/pages/configuration/wrangler-configuration/
 *   - https://developers.cloudflare.com/pages/functions/
 *
 * @module cli/commands/deploy/adapters/cf-pages
 */
import path from "node:path";
import { CLI_ERROR_CODES } from "../../../errors/codes";
import { writeArtifact } from "../artifact-writer";
import {
  getProviderCliStatus,
  type SpawnVersion,
} from "../provider-cli";
import type {
  AdapterArtifact,
  AdapterCheckResult,
  AdapterIssue,
  DeployAdapter,
  DeployOptions,
  DeployResult,
  ProjectContext,
  SecretSpec,
} from "../types";

const MIN_WRANGLER_VERSION = "3.0.0";

const CF_PAGES_SECRETS: ReadonlyArray<SecretSpec> = [
  {
    name: "CLOUDFLARE_API_TOKEN",
    required: true,
    description:
      "Cloudflare API token with Pages:Edit permission, used by wrangler.",
    docsUrl: "https://dash.cloudflare.com/profile/api-tokens",
  },
  {
    name: "CLOUDFLARE_ACCOUNT_ID",
    required: true,
    description: "Cloudflare account id (non-secret but required by wrangler).",
    docsUrl: "https://developers.cloudflare.com/workers/wrangler/configuration/",
  },
];

// ---------------------------------------------------------------------
// wrangler.toml (Pages variant)
// ---------------------------------------------------------------------

export interface CfPagesWranglerOptions {
  /** Cloudflare Pages project name (matches dashboard URL). */
  projectName: string;
  /** `pages_build_output_dir` — what Pages uploads. Defaults to `.mandu/client`. */
  pagesOutputDir?: string;
  /** Compatibility date — matches @mandujs/edge/workers default. */
  compatibilityDate?: string;
  /** Compatibility flags. `nodejs_compat` required for Mandu core imports. */
  compatibilityFlags?: string[];
}

export function renderCfPagesWrangler(options: CfPagesWranglerOptions): string {
  if (!/^[a-z0-9-]{1,58}$/.test(options.projectName)) {
    throw new Error(
      `renderCfPagesWrangler: projectName "${options.projectName}" must match /^[a-z0-9-]{1,58}$/.`
    );
  }
  const outputDir = options.pagesOutputDir ?? ".mandu/client";
  const compatDate = options.compatibilityDate ?? "2025-09-23";
  const flags = options.compatibilityFlags ?? ["nodejs_compat"];
  const lines = [
    "# Generated by `mandu deploy --target=cf-pages`.",
    "# Cloudflare Pages — https://developers.cloudflare.com/pages/configuration/wrangler-configuration/",
    "",
    `name = "${options.projectName}"`,
    `pages_build_output_dir = "${outputDir}"`,
    `compatibility_date = "${compatDate}"`,
    `compatibility_flags = ${JSON.stringify(flags)}`,
    "",
    "# Mandu SSR is emitted by `mandu build --target=workers` to",
    "# .mandu/workers/worker.js — Pages picks this up automatically via",
    "# the `functions/` directory convention below.",
  ];
  return lines.join("\n") + "\n";
}

// ---------------------------------------------------------------------
// Pages Functions middleware stub
// ---------------------------------------------------------------------

export function renderCfPagesMiddleware(): string {
  return `// Generated by \`mandu deploy --target=cf-pages\`.
// Pages Functions catch-all middleware. Delegates to the worker bundle
// emitted by \`mandu build --target=workers\`. Pages Functions run before
// the static asset CDN so dynamic routes hit the worker first.
//
// See https://developers.cloudflare.com/pages/functions/middleware/
import handler from "../.mandu/workers/worker.js";

export const onRequest: PagesFunction = async (context) => {
  if (typeof handler?.fetch !== "function") {
    return new Response(
      "[mandu] Worker bundle missing. Run \`mandu build --target=workers\` before deploy.",
      { status: 500 }
    );
  }
  // Pages Functions \`context.env\` mirrors the Workers env object; we
  // forward it so bindings (D1, KV, R2) propagate into Mandu.
  return handler.fetch(context.request, context.env, context);
};
`;
}

// ---------------------------------------------------------------------
// Adapter
// ---------------------------------------------------------------------

interface CfPagesAdapterOptions {
  spawnImpl?: SpawnVersion;
  deployImpl?: (
    project: ProjectContext,
    options: DeployOptions
  ) => Promise<DeployResult>;
}

export function createCfPagesAdapter(
  internal: CfPagesAdapterOptions = {}
): DeployAdapter {
  return {
    name: "Cloudflare Pages",
    target: "cf-pages",
    minimumCliVersion: { binary: "wrangler", semver: MIN_WRANGLER_VERSION },
    secrets: CF_PAGES_SECRETS,

    async check(project, options): Promise<AdapterCheckResult> {
      const errors: AdapterIssue[] = [];
      const warnings: AdapterIssue[] = [];

      warnings.push({
        code: CLI_ERROR_CODES.DEPLOY_EDGE_RUNTIME_WARNING,
        message:
          "Cloudflare Pages target is artifact-only in Phase 13. Edge runtime compatibility is finalized in Phase 15.",
        hint: "Emit with `mandu build --target=workers` first; inspect the generated worker.js.",
      });

      if (options.execute) {
        const status = await getProviderCliStatus("wrangler", MIN_WRANGLER_VERSION, {
          spawnImpl: internal.spawnImpl,
        });
        if (!status.installed) {
          errors.push({
            code: CLI_ERROR_CODES.DEPLOY_PROVIDER_CLI_MISSING,
            message: "wrangler is not installed.",
            hint: "Install with `bun add -g wrangler` or `npm i -g wrangler`.",
          });
        } else if (!status.meetsMinimum && status.version) {
          errors.push({
            code: CLI_ERROR_CODES.DEPLOY_PROVIDER_CLI_OUTDATED,
            message: `wrangler ${status.version} is older than required ${MIN_WRANGLER_VERSION}.`,
            hint: "Upgrade with `bun add -g wrangler@latest`.",
          });
        }
      }

      void project;
      return { ok: errors.length === 0, errors, warnings };
    },

    async prepare(project, options): Promise<AdapterArtifact[]> {
      const artifacts: AdapterArtifact[] = [];
      const rootDir = project.rootDir;
      const projectName = options.projectName ?? project.projectName;

      const wranglerResult = await writeArtifact({
        forbiddenValues: options.forbiddenSecrets,
        path: path.join(rootDir, "wrangler.toml"),
        content: renderCfPagesWrangler({ projectName }),
        preserveIfExists: true,
      });
      artifacts.push({
        path: wranglerResult.path,
        preserved: wranglerResult.preserved,
        description: wranglerResult.preserved
          ? "Existing wrangler.toml preserved (Phase 15 workers target already managed it)"
          : `Scaffolded wrangler.toml (Pages project=${projectName})`,
      });

      const middlewareResult = await writeArtifact({
        forbiddenValues: options.forbiddenSecrets,
        path: path.join(rootDir, "functions", "_middleware.ts"),
        content: renderCfPagesMiddleware(),
      });
      artifacts.push({
        path: middlewareResult.path,
        description: "Pages Functions middleware delegating to .mandu/workers/worker.js",
      });

      return artifacts;
    },

    async deploy(project, options): Promise<DeployResult> {
      if (internal.deployImpl) {
        return internal.deployImpl(project, options);
      }
      return {
        ok: false,
        errors: [
          {
            code: CLI_ERROR_CODES.DEPLOY_NOT_IMPLEMENTED,
            message: "wrangler pages deploy harness is provided only via DeployAdapter.deployImpl.",
            hint: "Run `wrangler pages deploy .mandu/client` manually after `mandu deploy --target=cf-pages`.",
          },
        ],
      };
    },
  };
}

export const cfPagesAdapter: DeployAdapter = createCfPagesAdapter();
