import { NextResponse } from 'next/server';
import { getUncachedEdgeConfigValue } from './edge-config.js';
import { validatePreviewToken } from './preview.js';
const RELEASE_HEADER = 'x-gradial-release-id';
const ROUTE_HEADER = 'x-gradial-route';
const DISPATCH_HEADER = 'x-gradial-dispatch';
export function createGradialMiddleware(config) {
    return async function gradialMiddleware(request) {
        const requestHeaders = gradialHeaders(request.headers);
        const preview = await releaseFromPreviewToken(request, config);
        if (preview.invalid) {
            return new Response('invalid preview token', { status: 403 });
        }
        const releaseId = preview.releaseId || await activeReleaseFromEdgeConfig(config);
        if (!releaseId) {
            return NextResponse.next({ request: { headers: requestHeaders } });
        }
        requestHeaders.set(RELEASE_HEADER, releaseId);
        requestHeaders.set(ROUTE_HEADER, request.nextUrl.pathname);
        requestHeaders.set(DISPATCH_HEADER, 'ssr-page');
        return NextResponse.next({
            request: {
                headers: requestHeaders,
            },
        });
    };
}
function gradialHeaders(headers) {
    const next = new Headers(headers);
    next.delete(RELEASE_HEADER);
    next.delete(ROUTE_HEADER);
    next.delete(DISPATCH_HEADER);
    return next;
}
async function activeReleaseFromEdgeConfig(config) {
    const edgeConfig = config.edgeConfig || process.env.EDGE_CONFIG || '';
    if (!edgeConfig || !config.siteId)
        return '';
    const active = await activeReleasePointer(edgeConfig, config.siteId);
    if (!active.releaseId)
        return '';
    const deploymentId = config.deploymentId || process.env.VERCEL_DEPLOYMENT_ID || '';
    if (!deploymentId)
        return active.releaseId;
    const compatibility = await getEdgeConfigJSON(edgeConfig, deploymentCompatibilityKey(config.siteId, deploymentId));
    if (!compatibility)
        return active.releaseId;
    return clampRelease(active.releaseId, active.sequence || 0, compatibility);
}
async function getEdgeConfigJSON(edgeConfig, key) {
    const value = await getEdgeConfigValue(edgeConfig, key);
    if (!value)
        return null;
    if (typeof value === 'string') {
        try {
            return JSON.parse(value);
        }
        catch {
            return null;
        }
    }
    return value;
}
async function getEdgeConfigValue(edgeConfig, key) {
    return getUncachedEdgeConfigValue(edgeConfig, key);
}
function activeReleaseKey(siteId) {
    return `activeRelease_${edgeKeySegment(siteId)}`;
}
function deploymentCompatibilityKey(siteId, deploymentId) {
    return `deploymentCompatibility_${edgeKeySegment(siteId)}_${edgeKeySegment(deploymentId)}`;
}
async function activeReleasePointer(edgeConfig, siteId) {
    const value = await getEdgeConfigValue(edgeConfig, activeReleaseKey(siteId));
    if (!value)
        return { releaseId: '' };
    if (typeof value === 'string') {
        try {
            const parsed = JSON.parse(value);
            if (parsed.releaseId)
                return parsed;
        }
        catch {
            return { releaseId: value };
        }
        return { releaseId: value };
    }
    const pointer = value;
    return { releaseId: pointer.releaseId || '', sequence: pointer.sequence };
}
function clampRelease(activeReleaseId, activeSequence, compatibility) {
    if (!activeSequence)
        return activeReleaseId;
    if (compatibility.minSequence && activeSequence < compatibility.minSequence && compatibility.minReleaseId) {
        return compatibility.minReleaseId;
    }
    if (compatibility.maxSequence && activeSequence > compatibility.maxSequence && compatibility.maxReleaseId) {
        return compatibility.maxReleaseId;
    }
    return activeReleaseId;
}
function edgeKeySegment(value) {
    return value.replace(/[^A-Za-z0-9_-]/g, '_');
}
async function releaseFromPreviewToken(request, config) {
    const token = previewTokenFromRequest(request);
    if (!token)
        return { releaseId: '', invalid: false };
    const signKey = config.previewSignKey || process.env.ACI_PREVIEW_SIGN_KEY || '';
    if (!signKey)
        return { releaseId: '', invalid: true };
    const claims = await validatePreviewToken(token, signKey);
    if (!claims || claims.scope !== 'preview' || !claims.releaseId)
        return { releaseId: '', invalid: true };
    if (claims.siteId && config.siteId && claims.siteId !== config.siteId)
        return { releaseId: '', invalid: true };
    if (!claims.expiresAt || Date.parse(claims.expiresAt) <= Date.now())
        return { releaseId: '', invalid: true };
    return { releaseId: claims.releaseId, invalid: false };
}
function previewTokenFromRequest(request) {
    const queryToken = request.nextUrl.searchParams.get('preview_token');
    if (queryToken)
        return queryToken;
    const cookieToken = request.cookies.get('aci_preview')?.value || '';
    if (cookieToken)
        return cookieToken;
    const auth = request.headers.get('authorization') || '';
    return auth.startsWith('Bearer ') ? auth.slice('Bearer '.length) : '';
}
