/**
 * A utility function to fetch data from the YouTube Data API
 * 
 * @param {string} endpoint - The YouTube API endpoint (e.g., 'search', 'videos', 'channels')
 * @param {Object} params - Query parameters for the request
 * @param {string} apiKey - Your YouTube Data API key
 * @returns {Promise<Object>} - Promise resolving to the API response data
 */
async function fetchYouTubeData(endpoint, params = {}, apiKey) {
    if (!apiKey) {
        throw new Error('YouTube API key is required');
    }

    // Base URL for YouTube Data API v3
    const baseUrl = 'https://www.googleapis.com/youtube/v3';

    // Construct the URL with query parameters
    const url = new URL(`${baseUrl}/${endpoint}`);

    // Add the API key
    url.searchParams.append('key', apiKey);

    // Add all other parameters
    Object.entries(params).forEach(([key, value]) => {
        url.searchParams.append(key, value);
    });

    try {
        // Make the request
        const response = await fetch(url.toString());

        // Check if the request was successful
        if (!response.ok) {
            const errorData = await response.json().catch(() => ({}));
            throw new Error(`YouTube API error: ${response.status} ${response.statusText} ${JSON.stringify(errorData)}`);
        }

        // Parse and return the JSON response
        return await response.json();
    } catch (error) {
        console.error('Error fetching YouTube data:', error);
        throw error;
    }
}

/**
 * Get live chat messages for a specific live stream
 * 
 * @param {string} liveChatId - The ID of the live chat (obtained from a live video's details)
 * @param {string} apiKey - Your YouTube Data API key
 * @param {string} pageToken - Page token for pagination (optional)
 * @param {number} maxResults - Maximum number of results to return (default: 200, max: 2000)
 * @returns {Promise<Object>} - Promise resolving to live chat messages and pagination info
 */
async function getLiveChatMessages(liveChatId, apiKey, pageToken = null, maxResults = 200) {
    if (!liveChatId) {
        throw new Error('Live chat ID is required');
    }

    const params = {
        part: 'snippet,authorDetails',
        liveChatId: liveChatId,
        maxResults: maxResults
    };

    // Add page token if provided
    if (pageToken) {
        params.pageToken = pageToken;
    }

    return fetchYouTubeData('liveChat/messages', params, apiKey);
}

/**
 * Get the live chat ID from a live video ID
 * 
 * @param {string} videoId - The ID of the live video
 * @param {string} apiKey - Your YouTube Data API key
 * @returns {Promise<string|null>} - Promise resolving to the live chat ID or null if not found
 */
async function getLiveChatId(videoId, apiKey) {
    if (!videoId) {
        throw new Error('Video ID is required');
    }

    const params = {
        part: 'liveStreamingDetails',
        id: videoId
    };

    try {
        const response = await fetchYouTubeData('videos', params, apiKey);

        if (response.items && response.items.length > 0 &&
            response.items[0].liveStreamingDetails &&
            response.items[0].liveStreamingDetails.activeLiveChatId) {
            return response.items[0].liveStreamingDetails.activeLiveChatId;
        }

        return null;
    } catch (error) {
        //console.error('Error getting YT live chat ID:', error);
        //throw error;
        logToPage(outputContainer, 'Error getting YT live chat ID');
    }
}

/**
 * Poll for new live chat messages continuously
 * 
 * @param {string} videoId - The ID of the live video
 * @param {string} apiKey - Your YouTube Data API key
 * @param {Function} messageCallback - Callback function to handle new messages
 * @param {number} pollingInterval - Time in milliseconds between polling requests (min 5000 ms recommended)
 * @returns {Object} - Control object with a stop() method to stop polling
 */
function pollLiveChatMessages(videoId, apiKey, messageCallback, pollingInterval = 5000) {
    if (pollingInterval < 5000) {
        console.warn('Polling interval less than 5000ms may lead to quota issues. Recommended minimum is 5000ms.');
    }

    let nextPageToken = null;
    let liveChatId = null;
    let timerId = null;
    let isPolling = false;

    async function poll() {
        if (!isPolling) return;

        try {
            // If we don't have a live chat ID yet, get it first
            if (!liveChatId) {
                liveChatId = await getLiveChatId(videoId, apiKey);
                if (!liveChatId) {
                    console.error('No active live chat found for this video');
                    stop();
                    return;
                }
            }

            // Get messages
            const response = await getLiveChatMessages(liveChatId, apiKey, nextPageToken);

            // Save the next page token
            nextPageToken = response.nextPageToken;

            // Process messages
            if (response.items && response.items.length > 0) {
                messageCallback(response.items);
            }

            // Schedule next poll after recommended polling interval
            const actualPollingInterval = Math.max(
                pollingInterval,
                response.pollingIntervalMillis || pollingInterval
            );

            timerId = setTimeout(poll, actualPollingInterval);
        } catch (error) {
            console.error('Error polling live chat messages:', error);
            // Try again after polling interval
            timerId = setTimeout(poll, pollingInterval);
        }
    }

    function start() {
        if (!isPolling) {
            isPolling = true;
            poll();
        }
    }

    function stop() {
        isPolling = false;
        nextPageToken = null;
        if (timerId) {
            clearTimeout(timerId);
            timerId = null;
        }
    }

    // Start polling immediately
    start();

    // Return control object
    return {
        stop,
        restart: start,
        isActive: () => isPolling
    };
}

// Example usage
function example(outputContainer, YTvideoID) {
    var API_KEY = document.getElementById('YoutubeApiKey').value; // Replace with your actual API key
   

    // Save API key to localStorage
    localStorage.setItem('youtubeApiKey', API_KEY);

    const LIVE_VIDEO_ID = YTvideoID; // Replace with a live video ID

    // Start polling for chat messages
    const chatPoller = pollLiveChatMessages(
        LIVE_VIDEO_ID,
        API_KEY,
        (messages) => {
            // Process new messages
            messages.forEach(msg => {
                const time = formatTimeFromISO(msg.snippet.publishedAt);
                const author = msg.authorDetails.displayName;
                const text = msg.snippet.displayMessage;
                const formattedMsg = `[${time}] | ${author}: ${text}`;
                var ytHighlightRanges = null;
                if (isOpen && window.voteHighlightEnabled && typeof window.findVoteMatchesInText === 'function') {
                    var rawRanges = window.findVoteMatchesInText(text);
                    if (rawRanges.length > 0) {
                        var prefixLen = formattedMsg.length - text.length;
                        ytHighlightRanges = rawRanges.map(function(r) {
                            return { start: r.start + prefixLen, length: r.length };
                        });
                    }
                }
                logToPage(outputContainer, formattedMsg, ytHighlightRanges);

                if (isOpen) {
                    parseMessageForVote(text, 'youtube', author);
                    if (typeof VoteChart !== 'undefined') {
                        VoteChart.setData(stats);
                        VoteChart.render();
                    }
                }
            });
        },
        5000 // Poll every 5 seconds
    );
    return chatPoller;

    // To stop polling later:
    //chatPoller.stop();

    // To restart polling:
    // chatPoller.restart();

    
}

function formatTimeFromISO(isoTimestamp) {
    // Create a Date object from the ISO timestamp
    const date = new Date(isoTimestamp);

    // Extract hours, minutes, and seconds
    const hours = date.getHours().toString().padStart(2, '0');
    const minutes = date.getMinutes().toString().padStart(2, '0');
    const seconds = date.getSeconds().toString().padStart(2, '0');

    // Return formatted time string
    return `${hours}:${minutes}:${seconds}`;
}
function logToPage(outputContainer, message, highlightRanges) {
    //const outputContainer = document.getElementById('outputContainer');
    var logEntry = document.createElement('p');

    if (highlightRanges && highlightRanges.length > 0) {
        var sorted = highlightRanges.slice().sort(function(a, b) { return a.start - b.start; });
        var lastIndex = 0;
        for (var i = 0; i < sorted.length; i++) {
            var range = sorted[i];
            if (range.start > lastIndex) {
                logEntry.appendChild(document.createTextNode(message.substring(lastIndex, range.start)));
            }
            var span = document.createElement('span');
            span.className = 'vote-highlight';
            span.textContent = message.substring(range.start, range.start + range.length);
            logEntry.appendChild(span);
            lastIndex = range.start + range.length;
        }
        if (lastIndex < message.length) {
            logEntry.appendChild(document.createTextNode(message.substring(lastIndex)));
        }
    } else {
        logEntry.textContent = message;
    }

    outputContainer.appendChild(logEntry);

    // Trim old entries to prevent memory leak
    if (typeof window.trimContainer === 'function') {
        window.trimContainer(outputContainer, window.MAX_CHAT_ENTRIES || 300);
    }

    // Check if user is already at the bottom before adding the new message
    // We give a small threshold (200px) to account for minor differences
    const isAtBottom = outputContainer.scrollHeight - outputContainer.clientHeight - outputContainer.scrollTop <= 200;

    // Only auto-scroll if the user was already at the bottom
    if (isAtBottom) {
        outputContainer.scrollTop = outputContainer.scrollHeight;
    }
}