import { headers } from 'next/headers';
import { FragmentNotFoundError, PageNotFoundError, } from '../content/provider.js';
import { S3ContentProvider } from '../providers/s3.js';
import { getUncachedEdgeConfigValue } from './edge-config.js';
const RELEASE_HEADER = 'x-gradial-release-id';
export { FragmentNotFoundError, PageNotFoundError };
export async function getPage(route = '', config = {}) {
    const provider = await resolveProvider(config);
    return await provider.getPage(route);
}
export async function getFragment(name, config = {}) {
    const provider = await resolveProvider(config);
    return await provider.getFragment(name);
}
export async function getRoutes(config = {}) {
    const provider = await resolveProvider(config);
    const routes = await provider.listRoutes();
    return routes.map((route) => route.path).sort();
}
export async function getRenderInput(route = '/', config = {}) {
    const provider = await resolveProvider(config);
    const [page, siteConfig] = await Promise.all([
        provider.getPage(route),
        provider.getSiteConfig(),
    ]);
    return {
        route,
        domain: siteConfig.domain || 'www.aci.local',
        locale: siteConfig.defaultLocale || 'en-us',
        siteConfig,
        page,
    };
}
export async function routeFromNextParams(params) {
    if (!params)
        return '/';
    const resolved = await params;
    if (!resolved.slug || resolved.slug.length === 0)
        return '/';
    return `/${resolved.slug.join('/')}/`;
}
export async function generateGradialStaticParams() {
    const { FileContentProvider } = await import('../providers/file.js');
    const provider = new FileContentProvider();
    const routes = await provider.listRoutes();
    return routes.map((entry) => ({
        slug: entry.path === '/' ? undefined : entry.path.replace(/^\/|\/$/g, '').split('/'),
    }));
}
export class ReleaseAssetNotFoundError extends Error {
    constructor(releaseId, assetPath, cause) {
        super(`ACI release asset not found: ${releaseId}/${assetPath}`);
        this.name = 'ReleaseAssetNotFoundError';
        this.cause = cause;
    }
}
export async function getReleaseAssetResponse(releaseId, assetPath, config = {}) {
    const clean = cleanAssetPath(assetPath);
    if (!releaseId || !clean) {
        throw new ReleaseAssetNotFoundError(releaseId, assetPath);
    }
    const provider = new S3ContentProvider({
        bucket: config.bucket,
        keyPrefix: releaseKey(config, releaseId),
        region: config.region,
        accessKeyId: config.accessKeyId,
        secretAccessKey: config.secretAccessKey,
        sessionToken: config.sessionToken,
        endpoint: config.endpoint,
    });
    const key = joinKey(provider.config.keyPrefix, 'assets', clean);
    const res = await provider.fetchRaw(key);
    if (!res.ok || !res.body) {
        throw new ReleaseAssetNotFoundError(releaseId, clean, await safeResponseText(res));
    }
    const headers = new Headers();
    copyHeader(res.headers, headers, 'content-type');
    copyHeader(res.headers, headers, 'content-length');
    copyHeader(res.headers, headers, 'etag');
    copyHeader(res.headers, headers, 'last-modified');
    headers.set('cache-control', 'public, max-age=31536000, immutable');
    return new Response(res.body, { status: 200, headers });
}
function cleanAssetPath(value) {
    const clean = value.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
    if (!clean || clean.split('/').some((s) => s === '..' || s === '.'))
        return '';
    return clean;
}
function copyHeader(from, to, name) {
    const value = from.get(name);
    if (value)
        to.set(name, value);
}
async function safeResponseText(res) {
    try {
        return await res.text();
    }
    catch {
        return '';
    }
}
export async function resolveReleaseId(config = {}) {
    if (config.releaseId)
        return config.releaseId;
    const headerReleaseId = await releaseIdFromHeaders();
    if (headerReleaseId)
        return headerReleaseId;
    if (process.env.ACI_RELEASE_ID)
        return process.env.ACI_RELEASE_ID;
    const activeReleaseId = await activeReleaseFromEdgeConfig(config);
    if (activeReleaseId)
        return activeReleaseId;
    throw new Error('ACI release ID is not available from headers, ACI_RELEASE_ID, or Edge Config');
}
async function resolveProvider(config) {
    const releaseId = await resolveReleaseId(config);
    return new S3ContentProvider({
        bucket: config.bucket,
        keyPrefix: releaseKey(config, releaseId),
        region: config.region,
        accessKeyId: config.accessKeyId,
        secretAccessKey: config.secretAccessKey,
        sessionToken: config.sessionToken,
        endpoint: config.endpoint,
    });
}
async function releaseIdFromHeaders() {
    try {
        const current = await headers();
        return current.get(RELEASE_HEADER) || '';
    }
    catch {
        return '';
    }
}
async function activeReleaseFromEdgeConfig(config) {
    const edgeConfig = config.edgeConfig || process.env.EDGE_CONFIG || '';
    const siteId = config.siteId || process.env.ACI_SITE_ID || '';
    if (!edgeConfig || !siteId)
        return '';
    const active = await activeReleasePointer(edgeConfig, 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(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 releaseKey(config, releaseId) {
    return joinKey(config.keyPrefix || process.env.ACI_S3_KEY_PREFIX || '', 'releases', releaseId);
}
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, '_');
}
function joinKey(...parts) {
    return parts
        .flatMap((part) => part.split('/'))
        .map((part) => part.trim())
        .filter(Boolean)
        .join('/');
}
