// ../preview-next/src/keepAlive.ts
import { cookies } from "next/headers.js";
import { NextResponse } from "next/server.js";

// ../preview-next/src/constants.ts
var PREVIEW_COOKIE_NAME = "preview_token";
var PREVIEW_REFRESH_COOKIE_NAME = "preview_refresh_token";
var PREVIEW_COOKIE_MAX_AGE = 60 * 60 * 12;

// ../preview-next/src/cookies.ts
function getPreviewCookieOptions(options = {}) {
  const { maxAge = PREVIEW_COOKIE_MAX_AGE, secure } = options;
  const resolvedSecure = secure ?? process.env.NODE_ENV === "production";
  return {
    httpOnly: true,
    secure: resolvedSecure,
    sameSite: "lax",
    path: "/",
    maxAge
  };
}
function setPreviewCookie(res, token, options) {
  res.cookies.set(PREVIEW_COOKIE_NAME, token, getPreviewCookieOptions(options));
  return res;
}
function setPreviewRefreshCookie(res, token, options) {
  res.cookies.set(PREVIEW_REFRESH_COOKIE_NAME, token, getPreviewCookieOptions(options));
  return res;
}
function setPreviewAuthCookies(res, {
  accessToken,
  refreshToken
}, options) {
  if (accessToken) {
    setPreviewCookie(res, accessToken, options);
  }
  if (refreshToken) {
    setPreviewRefreshCookie(res, refreshToken, options);
  }
  return res;
}
function clearPreviewCookies(res) {
  res.cookies.delete(PREVIEW_COOKIE_NAME);
  res.cookies.delete(PREVIEW_REFRESH_COOKIE_NAME);
  return res;
}

// ../preview-next/src/jwt.ts
function base64UrlDecodeToString(value) {
  const normalized = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
  if (typeof globalThis.atob === "function") {
    const binary = globalThis.atob(normalized);
    const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
    return new TextDecoder().decode(bytes);
  }
  return Buffer.from(normalized, "base64").toString("utf8");
}
function decodeJwtPayload(token) {
  const parts = token.split(".");
  if (parts.length < 2) return null;
  const payloadPart = parts[1];
  if (!payloadPart) return null;
  try {
    const payload = base64UrlDecodeToString(payloadPart);
    return JSON.parse(payload);
  } catch {
    return null;
  }
}

// ../preview-next/src/keepAlive.ts
function shouldRefreshToken(accessToken, thresholdSeconds) {
  if (!accessToken) return true;
  try {
    const payload = decodeJwtPayload(accessToken);
    const exp = payload?.exp;
    if (typeof exp !== "number") return true;
    const now = Math.floor(Date.now() / 1e3);
    const expiresIn = exp - now;
    return expiresIn < thresholdSeconds;
  } catch {
    return true;
  }
}
async function refreshAccessToken(params) {
  const { refreshToken, supabaseUrl, supabaseAnonKey, logger, fetcher } = params;
  try {
    logger?.debug?.("preview-keepalive:refresh", {
      refreshTokenPrefix: `${refreshToken.slice(0, 6)}\u2026`
    });
    const response = await fetcher(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        apikey: supabaseAnonKey
      },
      body: JSON.stringify({ refresh_token: refreshToken })
    });
    if (!response.ok) {
      const errorText = await response.text().catch(() => "");
      logger?.warn?.("preview-keepalive:refresh-failed", {
        status: response.status,
        body: errorText
      });
      return null;
    }
    const data = await response.json();
    if (!data.access_token || !data.refresh_token) {
      logger?.warn?.("preview-keepalive:missing-tokens", {
        hasAccess: Boolean(data.access_token),
        hasRefresh: Boolean(data.refresh_token)
      });
      return null;
    }
    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token
    };
  } catch (error) {
    logger?.error?.("preview-keepalive:error", { error });
    return null;
  }
}
function createPreviewKeepAliveRouteHandler(options = {}) {
  const maxAge = options.maxAge ?? PREVIEW_COOKIE_MAX_AGE;
  const refreshThresholdSeconds = options.refreshThresholdSeconds ?? 5 * 60;
  const logger = options.logger;
  const getCookies = options.getCookies ?? (async () => {
    const store = await cookies();
    return {
      get: (name) => store.get(name)
    };
  });
  const fetcher = options.fetcher ?? fetch;
  return async function previewKeepAliveHandler(request) {
    const res = new NextResponse(null, { status: 204 });
    const cookieStore = await getCookies();
    const accessToken = cookieStore.get(PREVIEW_COOKIE_NAME)?.value;
    const refreshToken = cookieStore.get(PREVIEW_REFRESH_COOKIE_NAME)?.value;
    const supabaseUrl = options.supabaseUrl ?? process.env.NEXT_PUBLIC_SUPABASE_URL;
    const supabaseAnonKey = options.supabaseAnonKey ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
    const url = new URL(request.url);
    const forwardedProto = (request.headers.get("x-forwarded-proto") ?? "").split(",")[0]?.trim();
    const protocol = forwardedProto || url.protocol.replace(":", "");
    const isSecure = protocol === "https";
    if (!refreshToken) {
      if (accessToken) {
        setPreviewAuthCookies(res, { accessToken, refreshToken }, { maxAge, secure: isSecure });
      }
      return res;
    }
    const needsRefresh = shouldRefreshToken(accessToken, refreshThresholdSeconds);
    if (!needsRefresh) {
      setPreviewAuthCookies(res, { accessToken, refreshToken }, { maxAge, secure: isSecure });
      return res;
    }
    if (!supabaseUrl || !supabaseAnonKey) {
      logger?.warn?.("preview-keepalive:missing-env");
      const failure = NextResponse.json({ error: "Missing Supabase configuration" }, { status: 401 });
      clearPreviewCookies(failure);
      return failure;
    }
    logger?.debug?.("preview-keepalive:needs-refresh");
    const refreshed = await refreshAccessToken({
      refreshToken,
      supabaseUrl,
      supabaseAnonKey,
      logger,
      fetcher
    });
    if (!refreshed) {
      logger?.warn?.("preview-keepalive:cleared-cookies");
      const failure = NextResponse.json({ error: "Unable to refresh preview session" }, { status: 401 });
      clearPreviewCookies(failure);
      return failure;
    }
    setPreviewAuthCookies(res, refreshed, { maxAge, secure: isSecure });
    return res;
  };
}
export {
  createPreviewKeepAliveRouteHandler
};
