import { yellow } from "kleur/colors";
import { ActionQueryStringInvalidError } from "../../core/errors/errors-data.js";
import { AstroError } from "../../core/errors/errors.js";
import { defineMiddleware } from "../../core/middleware/index.js";
import { ACTION_QUERY_PARAMS } from "../consts.js";
import { cloneRequestFromConsumedBody, formContentTypes, hasContentType } from "./utils.js";
import { getAction } from "./virtual/get-action.js";
import {
  serializeActionResult
} from "./virtual/shared.js";
const onRequest = defineMiddleware(async (context, next) => {
  if (context._isPrerendered) {
    if (context.request.method === "POST") {
      console.warn(
        yellow("[astro:actions]"),
        'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".'
      );
    }
    return next();
  }
  const locals = context.locals;
  if (locals._actionPayload) return next();
  const actionPayload = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.json();
  if (actionPayload) {
    if (!isActionPayload(actionPayload)) {
      throw new Error("Internal: Invalid action payload in cookie.");
    }
    return renderResult({ context, next, ...actionPayload });
  }
  const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
  if (context.request.method === "POST" && actionName) {
    return handlePost({ context, next, actionName });
  }
  if (context.request.method === "POST") {
    return handlePostLegacy({ context, next });
  }
  return next();
});
async function renderResult({
  context,
  next,
  actionResult,
  actionName
}) {
  const locals = context.locals;
  locals._actionPayload = { actionResult, actionName };
  const response = await next();
  context.cookies.delete(ACTION_QUERY_PARAMS.actionPayload);
  if (actionResult.type === "error") {
    return new Response(response.body, {
      status: actionResult.status,
      statusText: actionResult.type,
      headers: response.headers
    });
  }
  return response;
}
async function handlePost({
  context,
  next,
  actionName
}) {
  const { request } = context;
  const baseAction = await getAction(actionName);
  if (!baseAction) {
    throw new AstroError({
      ...ActionQueryStringInvalidError,
      message: ActionQueryStringInvalidError.message(actionName)
    });
  }
  const contentType = request.headers.get("content-type");
  let formData;
  if (contentType && hasContentType(contentType, formContentTypes)) {
    formData = await request.formData();
    Object.assign(context, { request: cloneRequestFromConsumedBody(request, formData) });
  }
  const action = baseAction.bind(context);
  const actionResult = await action(formData);
  if (context.url.searchParams.get(ACTION_QUERY_PARAMS.actionRedirect) === "false") {
    return renderResult({
      context,
      next,
      actionName,
      actionResult: serializeActionResult(actionResult)
    });
  }
  return redirectWithResult({ context, actionName, actionResult });
}
async function redirectWithResult({
  context,
  actionName,
  actionResult
}) {
  context.cookies.set(ACTION_QUERY_PARAMS.actionPayload, {
    actionName,
    actionResult: serializeActionResult(actionResult)
  });
  if (actionResult.error) {
    const referer = context.request.headers.get("Referer");
    if (!referer) {
      throw new Error("Internal: Referer unexpectedly missing from Action POST request.");
    }
    return context.redirect(referer);
  }
  return context.redirect(context.url.pathname);
}
async function handlePostLegacy({ context, next }) {
  const { request } = context;
  if (context.url.pathname.startsWith("/_actions")) return next();
  const contentType = request.headers.get("content-type");
  let formData;
  if (contentType && hasContentType(contentType, formContentTypes)) {
    formData = await request.clone().formData();
  }
  if (!formData) return next();
  const actionName = formData.get(ACTION_QUERY_PARAMS.actionName);
  if (!actionName) return next();
  const baseAction = await getAction(actionName);
  if (!baseAction) {
    throw new AstroError({
      ...ActionQueryStringInvalidError,
      message: ActionQueryStringInvalidError.message(actionName)
    });
  }
  const action = baseAction.bind(context);
  const actionResult = await action(formData);
  return redirectWithResult({ context, actionName, actionResult });
}
function isActionPayload(json) {
  if (typeof json !== "object" || json == null) return false;
  if (!("actionResult" in json) || typeof json.actionResult !== "object") return false;
  if (!("actionName" in json) || typeof json.actionName !== "string") return false;
  return true;
}
export {
  onRequest
};
