export const SESSION_REVOKED_HEADER = "X-Parhelia-Session-Revoked";
/**
 * Detects whether a response is actually a Sitecore login redirect
 * (the server returned 200 after following a redirect to /sitecore/login,
 * or the body is the login page / reload script). This must be treated as
 * an auth failure, not as a generic 500/error — otherwise the raw login
 * script body ends up in an error toast.
 */
function isLoginRedirect(response, body) {
    if (response.redirected) {
        const finalUrl = response.url || "";
        if (/\/sitecore\/login/i.test(finalUrl) ||
            /\/identity\//i.test(finalUrl) ||
            /[?&]returnUrl=/i.test(finalUrl)) {
            return true;
        }
    }
    // Body-based detection for auth redirects that don't flip response.redirected
    // (e.g. IIS forms-auth returning the login page inline).
    if (body.includes("topSelfWindow.location.reload") ||
        body.includes("getTopSelfWindow(window)") ||
        /<form[^>]+action="[^"]*\/sitecore\/login/i.test(body) ||
        /<title>[^<]*Sitecore[^<]*Login[^<]*<\/title>/i.test(body)) {
        return true;
    }
    return false;
}
/**
 * Fires a DOM event so the UI can surface a single, consistent
 * "session expired" toast with a Login action, regardless of which
 * service call first noticed the auth failure.
 */
function notifySessionExpired(reason = "unauthorized") {
    if (typeof window === "undefined")
        return;
    window.dispatchEvent(new CustomEvent("parhelia:session-expired", {
        detail: { reason },
    }));
}
/**
 * Extracts a user-friendly error message from the response.
 * Handles HTML error pages by extracting the title or exception details.
 * Also extracts source file information if available.
 */
function extractErrorMessage(message) {
    // Check if it's an HTML error page
    if (message.includes("<!DOCTYPE html>") || message.includes("<html>")) {
        let errorMessage = "";
        // Try to extract the title (which usually contains the error message)
        const titleMatch = message.match(/<title>(.*?)<\/title>/i);
        if (titleMatch && titleMatch[1]) {
            // Remove "Server Error" prefix if present
            errorMessage = titleMatch[1].replace(/^Server Error in '.*?' Application\.\s*/i, "");
        }
        // If no title, try h2 tag (exception message)
        if (!errorMessage) {
            const h2Match = message.match(/<h2>\s*<i>(.*?)<\/i>\s*<\/h2>/i);
            if (h2Match && h2Match[1]) {
                errorMessage = h2Match[1];
            }
        }
        // If no h2, try Exception Details
        if (!errorMessage) {
            const exceptionMatch = message.match(/<b>\s*Exception Details:\s*<\/b>(.*?)(?:<br>|<\/)/i);
            if (exceptionMatch && exceptionMatch[1]) {
                errorMessage = exceptionMatch[1].replace(/<[^>]*>/g, "").trim();
            }
        }
        // Try to extract source file and line information
        const sourceMatch = message.match(/<b>\s*Source File:\s*<\/b>\s*([^<]+)<b>\s*&nbsp;&nbsp;\s*Line:\s*<\/b>\s*(\d+)/i);
        if (sourceMatch && sourceMatch[1] && sourceMatch[2]) {
            const fileName = sourceMatch[1].trim().split("\\").pop();
            errorMessage += ` (${fileName}:${sourceMatch[2]})`;
        }
        if (errorMessage) {
            return errorMessage;
        }
        // If we can't parse it, just strip all HTML tags
        const cleaned = message.replace(/<[^>]*>/g, "").trim();
        // Limit length for toast display
        return cleaned.length > 200 ? cleaned.substring(0, 200) + "..." : cleaned;
    }
    return message;
}
export async function post(url, body, session) {
    if (session)
        url += "?sessionId=" + session;
    let response;
    try {
        response = await fetch(url, {
            method: "POST",
            body: JSON.stringify(body),
            credentials: "include",
            headers: {
                "Content-Type": "application/json",
            },
        });
    }
    catch (err) {
        return {
            type: "error",
            summary: "Network error",
            details: err instanceof Error ? err.message : "Failed to reach server",
            response: undefined,
        };
    }
    return handleResponse(response);
}
export async function get(url, session, signal) {
    if (session)
        url += "?sessionId=" + session;
    let response;
    try {
        response = await fetch(url, {
            method: "GET",
            credentials: "include",
            headers: {
                "Content-Type": "application/json",
                "Cache-Control": "no-cache",
            },
            signal,
        });
    }
    catch (err) {
        return {
            type: "error",
            summary: "Network error",
            details: err instanceof Error ? err.message : "Failed to reach server",
            response: undefined,
        };
    }
    return handleResponse(response);
}
async function handleResponse(response) {
    const isSessionRevoked = response.headers.get(SESSION_REVOKED_HEADER) === "true";
    if (isSessionRevoked &&
        (response.status === 401 || response.status === 403)) {
        return {
            type: "unauthorized",
            summary: "Session revoked",
            details: "You were disconnected because this account is active in another browser.",
            response,
        };
    }
    if (response.status === 400) {
        const data = await response.json();
        return {
            type: "error",
            summary: data.summary ? extractErrorMessage(data.summary) : data.summary,
            details: data.details ? extractErrorMessage(data.details) : data.details,
            rawDetails: data.details || data.summary, // Preserve original for logging
            response,
        };
    }
    if (response.status === 500 || response.redirected) {
        const rawText = await response.text();
        // A Sitecore login redirect can arrive as a 200-with-redirected-url or
        // as a login page body. We must not expose the reload script as an
        // error toast — treat it as an auth failure instead.
        if (isLoginRedirect(response, rawText)) {
            notifySessionExpired("login-redirect");
            return {
                type: "unauthorized",
                summary: "Session expired",
                details: "Please login again to continue.",
                response,
            };
        }
        // Parhelia backend returns JSON errors; prefer its summary over generic "Error"
        let summary = "Error";
        let details = extractErrorMessage(rawText);
        try {
            const data = JSON.parse(rawText);
            if (typeof data.summary === "string" && data.summary.trim()) {
                summary = extractErrorMessage(data.summary);
            }
            if (typeof data.details === "string" && data.details.trim()) {
                details = extractErrorMessage(data.details);
            }
            else if (typeof data.summary === "string" && data.summary.trim()) {
                details = "";
            }
        }
        catch {
            // Not JSON; keep extractErrorMessage result
        }
        return {
            type: "error",
            summary,
            details,
            rawDetails: rawText,
            response,
        };
    }
    if (response.status === 401) {
        notifySessionExpired("unauthorized");
        return {
            type: "unauthorized",
            summary: "Session expired",
            details: "Please login again to continue.",
            response,
        };
    }
    // Handle 403 Forbidden - check if it's a license error
    if (response.status === 403) {
        // Clone response before reading so we can return it
        const clonedResponse = response.clone();
        try {
            const data = await response.json();
            const summary = data.summary || "";
            const details = data.details || "";
            // Check if this is a license validation error
            if (summary.includes("License validation failed") ||
                details.includes("License validation failed") ||
                summary.toLowerCase().includes("license")) {
                // Dispatch event to trigger license dialog
                window.dispatchEvent(new CustomEvent("license:validation-failed", {
                    detail: { summary, details },
                }));
            }
            return {
                type: "error",
                summary: data.summary ? extractErrorMessage(data.summary) : "Forbidden",
                details: data.details
                    ? extractErrorMessage(data.details)
                    : "Access denied",
                rawDetails: details || summary,
                response: clonedResponse,
            };
        }
        catch {
            // If response is not JSON, treat as generic 403
            // Read from clone since original may have been consumed
            const rawText = await clonedResponse.text();
            return {
                type: "error",
                response: clonedResponse,
                summary: "Forbidden",
                details: extractErrorMessage(rawText),
                rawDetails: rawText,
            };
        }
    }
    // Handle other non-success status codes as errors
    // Note: 200 (OK) and 202 (Accepted) are both success responses
    // 202 is used for long-running operations that are accepted but still executing
    if (response.status !== 200 && response.status !== 202) {
        const rawText = await response.text();
        if (isLoginRedirect(response, rawText)) {
            notifySessionExpired("login-redirect");
            return {
                type: "unauthorized",
                summary: "Session expired",
                details: "Please login again to continue.",
                response,
            };
        }
        return {
            type: "error",
            response,
            details: extractErrorMessage(rawText),
            rawDetails: rawText, // Preserve original for logging
        };
    }
    if (!response.headers.get("content-type")?.includes("application/json")) {
        return {
            type: "success",
            response,
        };
    }
    const data = (await response.json());
    return { type: "success", response, data };
}
//# sourceMappingURL=serviceHelper.js.map