/**
 * TextBuffer Module
 * Manages text processing and sentence detection for the UI
 */
import { BaseModule } from './base-module.js';

class TextBufferModule extends BaseModule {
    constructor() {
        super('text-buffer', 'Text Buffer');
        this.buffer = '';
        this.sentenceEndRegex = /[.!?]\s+/g;
        this.onSentenceReadyCallback = null;
        this.processingLock = false;
        this.processingQueue = [];
        this.isProcessingActive = false;
        this.paragraphCounter = 0;
        this.currentTextBlockId = 0;
        this.markupParser = null;
        this.dependencies = ['markup-parser'];
        
        // Bind methods using parent's bindMethods utility
        this.bindMethods([
            'addBlock',
            'addBlocks',
            'processNextFromQueue',
            'processSentences',
            'processNextSentence',
            'clear',
            'getBuffer',
            'getStatus',
            'setOnSentenceReady'
        ]);
    }

    /**
     * Initialize the module
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async initialize() {
        try {
            this.markupParser = this.getModule('markup-parser');
            if (!this.markupParser) {
                console.warn("TextBuffer: Markup parser not found, using plain paragraph splitting");
            }

            // Use parent's addEventListener for automatic cleanup
            this.addEventListener(document, 'ui:paragraph:complete', (event) => {
                if (this.processingQueue.length > 0 && !this.isProcessingActive) {
                    console.log('TextBuffer: Previous paragraph complete, processing next from queue');
                    this.processNextFromQueue();
                }
            });
            
            // Use parent's addEventListener for automatic cleanup
            this.addEventListener(document, 'animation:complete', () => {
                if (this.processingQueue.length > 0 && !this.isProcessingActive) {
                    console.log('TextBuffer: Animations complete, processing next from queue');
                    this.processNextFromQueue();
                }
            });
            
            this.reportProgress(100, "Text buffer ready");
            return true;
        } catch (error) {
            console.error("Error initializing Text Buffer:", error);
            return false;
        }
    }

    /**
     * Set callback function for when a sentence is ready
     * @param {Function} callback - Function to call with the sentence and completion callback
     */
    setOnSentenceReady(callback) {
        if (typeof callback === 'function') {
            this.onSentenceReadyCallback = callback;
            console.log("Text Buffer: Sentence ready callback set");
            
            // Process any queued text immediately
            if (this.processingQueue.length > 0 && !this.isProcessingActive) {
                this.processNextFromQueue();
            }
        } else {
            console.warn("Text Buffer: Invalid sentence ready callback provided");
        }
    }

    /**
     * Add an already parsed render block to the processing queue.
     * Engine protocols should prefer this over re-serializing tags into text markup.
     * @param {Object} block - Parsed paragraph/media/heading block
     */
    addBlock(block) {
        if (!block || !block.type) return;

        if (block.type === 'paragraph') {
            const paragraphId = block.id || `paragraph-${this.paragraphCounter + 1}`;
            this.processingQueue.push({
                ...block,
                id: paragraphId,
                paragraphIndex: this.paragraphCounter,
                textBlockId: this.currentTextBlockId,
                text: String(block.text || '').trim(),
                layoutText: block.layoutText || block.text || ''
            });
            this.paragraphCounter += 1;
        } else {
            this.processingQueue.push({
                ...block,
                id: block.id || `${block.type}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
            });

            if (block.type === 'image') {
                this.currentTextBlockId += 1;
            }
        }

        if (!this.isProcessingActive && this.onSentenceReadyCallback) {
            this.processNextFromQueue();
        } else {
            console.log(`TextBuffer: ${block.type} block queued for processing`);
        }
    }

    addBlocks(blocks = []) {
        if (!Array.isArray(blocks)) return;
        blocks.forEach(block => this.addBlock(block));
    }

    /**
     * Process the next text fragment from the queue
     */
    processNextFromQueue() {
        if (this.processingQueue.length === 0 || this.isProcessingActive) {
            return;
        }
        
        this.isProcessingActive = true;
        const paragraph = this.processingQueue.shift();
        
        console.log(`TextBuffer: Processing next fragment from queue, remaining: ${this.processingQueue.length}`);

        this.buffer = paragraph.text;

        super.dispatchEvent('buffer:sentence', {
            sentence: paragraph.text,
            paragraph,
            remaining: this.processingQueue.length
        });

        if (this.onSentenceReadyCallback) {
            this.onSentenceReadyCallback(paragraph, () => {});
            this.buffer = '';
            this.isProcessingActive = false;
            this.processingLock = false;

            if (this.processingQueue.length > 0) {
                queueMicrotask(() => this.processNextFromQueue());
            } else {
                super.dispatchEvent('buffer:empty', {});
            }
        } else {
            this.isProcessingActive = false;
        }
    }

    /**
     * Process complete sentences in the buffer
     */
    processSentences() {
        // If already processing, don't start another processing cycle
        if (this.processingLock) {
            return;
        }
        
        this.processingLock = true;
        
        // If the buffer is empty, release the lock and check queue
        if (this.buffer.length === 0) {
            this.processingLock = false;
            
            // If no more text to process, end processing
            if (this.processingQueue.length === 0) {
                this.isProcessingActive = false;
                return;
            }
            
            // Process the next text fragment
            this.processNextFromQueue();
            return;
        }
        
        // Process the next sentence
        this.processNextSentence();
    }
    
    /**
     * Process the next sentence in the buffer
     */
    processNextSentence() {
        // Check for different sentence endings
        const patterns = [/[.!?]\s+/, /[.!?]$/, /\n/];
        let match = null;
        let endIndex = -1;
        
        // Try to find the first sentence ending
        for (const pattern of patterns) {
            match = this.buffer.match(pattern);
            if (match) {
                endIndex = match.index + match[0].length;
                break;
            }
        }
        
        if (endIndex === -1) {
            // No complete sentence found
            this.processingLock = false;
            this.isProcessingActive = false;
            return;
        }
        
        const sentence = this.buffer.substring(0, endIndex);
        
        // Remove the processed sentence from buffer
        this.buffer = this.buffer.substring(endIndex);
        
        console.log(`TextBuffer: Processing sentence: "${sentence.trim()}"`); 
        
        // Use parent's dispatchEvent method
        super.dispatchEvent('buffer:sentence', {
            sentence: sentence,
            remaining: this.buffer.length
        });
        
        // Call the callback if set
        if (this.onSentenceReadyCallback) {
            this.onSentenceReadyCallback(sentence, () => {
                // After processing is complete, check for more sentences
                this.processingLock = false; // Release lock immediately to allow processing of next sentence
                
                // Check if there are more sentences to process
                if (this.buffer.length > 0) {
                    // Use requestAnimationFrame to prevent stack overflow and ensure UI update between sentences
                    requestAnimationFrame(() => {
                        this.processSentences();
                    });
                } else if (this.processingQueue.length > 0) {
                    // No more sentences in buffer but we have more text in the queue
                    requestAnimationFrame(() => {
                        this.isProcessingActive = false;
                        this.processNextFromQueue();
                    });
                } else {
                    // All processed
                    this.isProcessingActive = false;
                    super.dispatchEvent('buffer:empty', {});
                }
            });
        } else {
            // No callback set, just release lock and continue processing
            this.processingLock = false;
            
            if (this.buffer.length > 0) {
                // Use requestAnimationFrame instead of setTimeout for better performance
                requestAnimationFrame(() => {
                    this.processSentences();
                });
            } else if (this.processingQueue.length > 0) {
                requestAnimationFrame(() => {
                    this.isProcessingActive = false;
                    this.processNextFromQueue();
                });
            } else {
                this.isProcessingActive = false;
            }
        }
    }

    /**
     * Clear the text buffer
     */
    clear() {
        this.buffer = '';
        this.processingQueue = [];
        this.isProcessingActive = false;
        this.processingLock = false;
        this.paragraphCounter = 0;
        this.currentTextBlockId = 0;
    }

    /**
     * Get the current buffer content
     * @returns {string} - Current buffer content
     */
    getBuffer() {
        return this.buffer;
    }
    
    /**
     * Get the current processing status
     * @returns {Object} - Processing status object
     */
    getStatus() {
        return {
            bufferLength: this.buffer.length,
            queueLength: this.processingQueue.length,
            isProcessing: this.isProcessingActive,
            isLocked: this.processingLock
        };
    }
}

// Create the singleton instance
const TextBuffer = new TextBufferModule();

// Export the module
export { TextBuffer };
