/**
 * Canonical REST fetch wrapper: one implementation for Studio (Featured, pickers).
 * Adds X-WP-Nonce, JSON, AbortController timeout. Returns { ok, data, message, status }.
 * Does not show UI; callers use ctx.errors.report / normalizeAndReport on failure.
 */
(function () {
    'use strict';

    if (typeof window.FotogenicAI === 'undefined') {
        window.FotogenicAI = {};
    }
    if (typeof window.FotogenicAI.Shared === 'undefined') {
        window.FotogenicAI.Shared = {};
    }
    if (typeof window.FotogenicAI.Shared.api === 'undefined') {
        window.FotogenicAI.Shared.api = {};
    }
    const i18n = window.FotogenicAI?.Shared?.i18n?.restFetch || {};
    const t = (key, fallback) => (typeof i18n[key] === 'string' && i18n[key] ? i18n[key] : fallback);
    const AUTH_ERROR_CODES = new Set([
        'fotogenic_ai_auth_session_expired',
        'fotogenic_ai_missing_auth_session',
    ]);

    function isAuthExpiredResponse(payload) {
        const code = String(payload?.code || '').toLowerCase();
        return AUTH_ERROR_CODES.has(code);
    }

    function buildRestRequestUrl(restUrl, path) {
        const normalizedBase = String(restUrl || '').replace(/\/$/, '');
        const normalizedPath = String(path || '');
        const queryIndex = normalizedPath.indexOf('?');
        const routePath = (queryIndex >= 0 ? normalizedPath.slice(0, queryIndex) : normalizedPath).replace(/^\/+/, '');
        const queryString = queryIndex >= 0 ? normalizedPath.slice(queryIndex + 1) : '';
        const baseUrl = new URL(normalizedBase, window.location.href);

        if (!baseUrl.searchParams.has('rest_route')) {
            return `${normalizedBase}/${routePath}${queryString ? `?${queryString}` : ''}`;
        }

        const baseRoute = String(baseUrl.searchParams.get('rest_route') || '').replace(/\/$/, '');
        baseUrl.searchParams.set('rest_route', `${baseRoute}/${routePath}`);
        const queryParams = new URLSearchParams(queryString);
        queryParams.forEach((value, key) => baseUrl.searchParams.append(key, value));
        return baseUrl.toString();
    }

    /**
     * Create a REST fetch function.
     * @param {string} restUrl - Base REST URL.
     * @param {string} restNonce - X-WP-Nonce value.
     * @param {Object} [options] - onMissingData, onNonceExpired, setNonceExpired, onAuthExpired, setAuthExpired.
     * @returns {(path: string, options?: { method?, body?, timeoutMs?, signal? }) => Promise<{ ok: boolean, data: unknown, message?: string, status?: number, authExpired?: boolean, handledAuthExpired?: boolean }>}
     */
    function createRestFetch(restUrl, restNonce, options = {}) {
        const globalFetch = typeof fetch === 'function' ? fetch : window.fetch;
        return async function restFetch(path, opts = {}) {
            if (!restUrl || !restNonce) {
                if (typeof options.onMissingData === 'function') {
                    options.onMissingData();
                }
                const message = t('restContextMissing', 'REST context is missing.');
                return { ok: false, data: { message }, message, status: 0 };
            }

            const useCallerSignal = opts.signal != null;
            const controller = useCallerSignal ? null : new AbortController();
            const timeoutMs = typeof opts.timeoutMs === 'number' ? opts.timeoutMs : 20000;
            const timeoutId = controller ? window.setTimeout(() => controller.abort(), timeoutMs) : null;

            try {
                const response = await globalFetch(buildRestRequestUrl(restUrl, path), {
                    method: opts.method || 'GET',
                    credentials: 'same-origin',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-WP-Nonce': restNonce,
                    },
                    body: opts.body ? JSON.stringify(opts.body) : undefined,
                    signal: useCallerSignal ? opts.signal : controller.signal,
                });
                if (timeoutId) clearTimeout(timeoutId);

                if (response.status === 204) {
                    return { ok: true, data: {}, status: 204 };
                }

                const contentType = response.headers.get('content-type') || '';
                if (contentType.includes('application/json')) {
                    const data = await response.json();
                    const code = String(data?.code || '').toLowerCase();
                    const invalidNonce = !response.ok && (
                        code === 'rest_cookie_invalid_nonce' ||
                        code === 'fotogenic_ai_invalid_nonce' ||
                        code === 'invalid_nonce'
                    );
                    if (invalidNonce) {
                        if (typeof options.setNonceExpired === 'function') {
                            options.setNonceExpired(true);
                        }
                        if (typeof options.onNonceExpired === 'function') {
                            options.onNonceExpired();
                        }
                    }
                    const authExpired = !response.ok && isAuthExpiredResponse(data);
                    if (authExpired) {
                        if (typeof options.setAuthExpired === 'function') {
                            options.setAuthExpired(true, data);
                        }
                        if (typeof options.onAuthExpired === 'function') {
                            options.onAuthExpired({
                                path,
                                status: response.status,
                                code,
                                data,
                            });
                        }
                    }
                    return {
                        ok: response.ok,
                        data,
                        message: data?.message,
                        status: response.status,
                        authExpired,
                        handledAuthExpired: authExpired,
                    };
                }

                const text = await response.text();
                const message = text || t('unexpectedResponse', 'Unexpected response from server.');
                return {
                    ok: response.ok,
                    data: { message },
                    message,
                    status: response.status,
                };
            } catch (error) {
                if (timeoutId) clearTimeout(timeoutId);
                const aborted = error?.name === 'AbortError';
                const message = aborted ? t('requestTimedOut', 'Request timed out.') : t('networkError', 'Network error.');
                return { ok: false, data: { message }, message, status: 0, aborted };
            }
        };
    }

    window.FotogenicAI.Shared.api.restFetch = {
        buildRestRequestUrl,
        createRestFetch,
    };
})();
