/**
 * StaticQ Media Worker - R2 Storage
 *
 * Serves media files from Cloudflare R2 with optional origin fallback.
 * Generated by StaticQ WordPress Plugin
 *
 * @see https://staticq.io
 */

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const path = url.pathname;

        // Configuration
        const CONFIG = {
            UPLOADS_PREFIX: '{{UPLOADS_PREFIX}}',
            BUCKET_PREFIX: '{{BUCKET_PREFIX}}',
            CACHE_TTL: {{CACHE_TTL}},
            ENABLE_FALLBACK: {{ENABLE_FALLBACK}},
            ENABLE_VARIANT_FALLBACK: {{ENABLE_VARIANT_FALLBACK}},
            VARIANT_FALLBACK_CDN_TTL: {{VARIANT_FALLBACK_CDN_TTL}}
        };

        // Only handle uploads path
        if (!path.startsWith(CONFIG.UPLOADS_PREFIX)) {
            return fetch(request);
        }

        // Build the R2 key from the raw URL path (preserves percent-encoding).
        // Some files may have been uploaded with encoded characters in the key.
        const rawKey = CONFIG.BUCKET_PREFIX + path.slice(CONFIG.UPLOADS_PREFIX.length);

        // Build a decoded fallback key: decode percent-encoding, then normalize
        // Unicode whitespace (U+202F narrow no-break space, U+00A0 non-breaking
        // space) to regular space. Only used if different from rawKey.
        const decodedPath = decodeURIComponent(path).replace(/[\u202F\u00A0]/g, ' ');
        const decodedKey = CONFIG.BUCKET_PREFIX + decodedPath.slice(CONFIG.UPLOADS_PREFIX.length);

        // Build a U+202F variant: macOS uses narrow no-break space in screenshot
        // filenames; older uploads may have U+202F in the R2 key. Only used if
        // different from both rawKey and decodedKey.
        const u202fPath = decodeURIComponent(path).replace(/ /g, '\u202F');
        const u202fKey = CONFIG.BUCKET_PREFIX + u202fPath.slice(CONFIG.UPLOADS_PREFIX.length);

        // Check cache first
        const cacheKey = new Request(url.toString());
        const cache = caches.default;
        let cachedResponse = await cache.match(cacheKey);

        if (cachedResponse) {
            const resp = new Response(cachedResponse.body, cachedResponse);
            resp.headers.set('x-staticq-cache', 'HIT');
            return resp;
        }

        // 1. Try R2 bucket: raw key first, then decoded/normalized fallbacks
        try {
            let object = await env.MEDIA_BUCKET.get(rawKey);

            if (object === null && decodedKey !== rawKey) {
                object = await env.MEDIA_BUCKET.get(decodedKey);
            }

            if (object === null && u202fKey !== rawKey && u202fKey !== decodedKey) {
                object = await env.MEDIA_BUCKET.get(u202fKey);
            }

            if (object !== null) {
                const headers = new Headers();
                object.writeHttpMetadata(headers);
                headers.set('etag', object.httpEtag);
                headers.set('cache-control', 'public, max-age=' + CONFIG.CACHE_TTL);
                headers.set('x-staticq-source', 'r2');
                headers.set('x-staticq-cache', 'MISS');

                const response = new Response(object.body, { headers });
                ctx.waitUntil(cache.put(cacheKey, response.clone()));
                return response;
            }
        } catch (e) {
            console.error('StaticQ R2 error:', e.message);
        }

{{ORIGIN_FALLBACK_CODE}}
{{VARIANT_FALLBACK_CODE}}
{{NOT_FOUND_CODE}}
    }
};