/**
 * Nonce Manager Utility
 *
 * Handles nonce fetching, caching, and automatic refresh for the Limb Chatbot plugin.
 * Provides a centralized way to manage authentication nonces across different components.
 *
 * @package Limb_Chatbot
 * @since 1.0.7
 */

/**
 * Nonce refresh timeout ID
 * @type {number|null}
 */
let nonceRefreshTimeoutId = null;

/**
 * Get nonce from data attributes on a given element
 *
 * @param {HTMLElement} element - The element to get nonce attributes from
 * @returns {boolean} Whether the nonce was successfully retrieved from attributes
 */
export const getNonceFromAttributes = (element) => {
    // Ensure LimbChatbot.rest exists
    if (!LimbChatbot?.rest) {
        return false;
    }

    if (!element) {
        return false;
    }

    const nonce = element.getAttribute('data-nonce');
    const expires = element.getAttribute('data-nonce-expires');

    // Check if both attributes exist and are not empty strings
    if (nonce && expires) {
        // Add nonce to LimbChatbot.rest object
        LimbChatbot.rest.nonce = nonce;

        // Parse expires as number (it's a UTC timestamp in seconds)
        const expiresUtc = parseInt(expires, 10);

        if (!isNaN(expiresUtc)) {
            // Schedule next nonce refresh based on expires timestamp
            scheduleNonceRefresh(expiresUtc);
        }

        // Remove attributes from the element
        element.removeAttribute('data-nonce');
        element.removeAttribute('data-nonce-expires');

        return true;
    }

    return false;
};

/**
 * Fetch fresh nonce from the server
 *
 * @returns {Promise<boolean>} Whether the nonce was successfully fetched
 */
export const fetchNonce = async () => {
    // Ensure LimbChatbot.rest and url exist
    if (!LimbChatbot?.rest?.url) {
        console.error('Failed to fetch nonce: LimbChatbot.rest.url is not defined');
        return false;
    }

    try {
        const response = await fetch(`${LimbChatbot.rest.url}plugin/nonce`, {
            method: 'GET',
            cache: 'no-store', // Prevent caching
            headers: {
                'Content-Type': 'application/json',
            },
        });

        if (!response.ok) {
            throw new Error(`Failed to fetch nonce: ${response.status}`);
        }

        const data = await response.json();

        if (data.nonce) {
            // Update the global nonce
            LimbChatbot.rest.nonce = data.nonce;

            // Schedule next nonce refresh based on expires timestamp
            if (data.expires) {
                scheduleNonceRefresh(data.expires);
            }

            return true;
        }

        return false;
    } catch (error) {
        console.error('Failed to fetch nonce:', error);
        return false;
    }
};

/**
 * Schedule the next nonce refresh based on expires timestamp
 *
 * @param {number} expiresUtc - Expiration timestamp in UTC seconds
 */
export const scheduleNonceRefresh = (expiresUtc) => {
    // Clear any existing timeout
    if (nonceRefreshTimeoutId) {
        clearTimeout(nonceRefreshTimeoutId);
        nonceRefreshTimeoutId = null;
    }

    // Calculate time until expiration (in milliseconds)
    const now = Math.floor(Date.now() / 1000); // Current time in UTC seconds
    const timeUntilExpiry = (expiresUtc - now) * 1000;

    // If already expired or expires very soon, refresh immediately (with small delay)
    if (timeUntilExpiry <= 0) {
        // Refresh immediately but with a small delay to avoid blocking
        nonceRefreshTimeoutId = setTimeout(async () => {
            await fetchNonce();
        }, 100);
        return;
    }

    // Refresh 30 seconds before expiration to ensure smooth operation
    // Minimum refresh interval of 10 seconds to prevent too frequent requests
    const refreshIn = Math.max(timeUntilExpiry - 30000, 10000);

    nonceRefreshTimeoutId = setTimeout(async () => {
        await fetchNonce();
    }, refreshIn);
};

/**
 * Ensure nonce is available before making API calls
 * Tries to get nonce from element attributes first, then fetches from server if needed
 *
 * @param {HTMLElement|null} element - Optional element to check for nonce attributes
 * @returns {Promise<boolean>} Whether a valid nonce is available
 */
export const ensureNonce = async (element = null) => {
    // Ensure LimbChatbot.rest exists
    if (!LimbChatbot?.rest) {
        console.error('Failed to ensure nonce: LimbChatbot.rest is not defined');
        return false;
    }

    // If nonce already exists and is not empty, consider it valid
    if (LimbChatbot.rest.nonce && LimbChatbot.rest.nonce.trim() !== '') {
        return true;
    }

    // First, try to get nonce from data attributes if element is provided
    if (element) {
        const fromAttributes = getNonceFromAttributes(element);
        if (fromAttributes) {
            return true;
        }
    }

    // If attributes don't exist, fetch nonce via API
    return await fetchNonce();
};