/**
 * ============================================
 * FLOWBOT DCI - REALTIME MODULE v3.0
 * ============================================
 * Reusable real-time polling and notification system
 *
 * Features:
 * - AJAX polling with configurable intervals
 * - Visibility-aware polling (pauses when tab hidden)
 * - Browser notifications
 * - Event-based callback system
 * - Automatic error handling and retry
 * - Memory-efficient cleanup
 */

class FlowbotRealtime {
    constructor(options = {}) {
        // Configuration
        this.baseUrl = options.baseUrl || '/api/v1';
        this.defaultInterval = options.defaultInterval || 2000;
        this.maxRetries = options.maxRetries || 3;
        this.retryDelay = options.retryDelay || 5000;

        // State
        this.polls = new Map();
        this.callbacks = new Map();
        this.retryCount = new Map();
        this.isVisible = !document.hidden;
        this.notificationPermission = Notification.permission;

        // Bind methods
        this.handleVisibilityChange = this.handleVisibilityChange.bind(this);

        // Initialize
        this.init();
    }

    /**
     * Initialize the realtime system
     */
    init() {
        // Listen for visibility changes
        document.addEventListener('visibilitychange', this.handleVisibilityChange);

        // Request notification permission if not determined
        if (this.notificationPermission === 'default') {
            this.requestNotificationPermission();
        }
    }

    /**
     * Handle tab visibility changes
     */
    handleVisibilityChange() {
        this.isVisible = !document.hidden;

        if (this.isVisible) {
            // Resume all paused polls
            this.polls.forEach((poll, key) => {
                if (poll.paused) {
                    this.resumePoll(key);
                }
            });
        } else {
            // Pause all polls when tab is hidden
            this.polls.forEach((poll, key) => {
                if (!poll.paused) {
                    this.pausePoll(key);
                }
            });
        }
    }

    // ==========================================
    // POLLING METHODS
    // ==========================================

    /**
     * Start polling an endpoint
     * @param {string} key - Unique identifier for this poll
     * @param {string} endpoint - API endpoint to poll
     * @param {number} interval - Polling interval in ms
     * @param {object} params - Additional query parameters
     */
    startPoll(key, endpoint, interval = this.defaultInterval, params = {}) {
        // Stop existing poll with same key
        if (this.polls.has(key)) {
            this.stopPoll(key);
        }

        const poll = {
            endpoint,
            interval,
            params,
            paused: false,
            timer: null,
            lastData: null
        };

        this.polls.set(key, poll);
        this.retryCount.set(key, 0);

        // Start polling
        this.executePoll(key);
    }

    /**
     * Execute a single poll request
     */
    async executePoll(key) {
        const poll = this.polls.get(key);
        if (!poll || poll.paused) return;

        try {
            const url = this.buildUrl(poll.endpoint, poll.params);
            const response = await fetch(url);

            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }

            const data = await response.json();

            // Reset retry count on success
            this.retryCount.set(key, 0);

            // Check if data changed
            const dataStr = JSON.stringify(data);
            const changed = poll.lastData !== dataStr;
            poll.lastData = dataStr;

            // Trigger callbacks
            this.triggerCallback(key, 'data', data, changed);

            if (data.success && data.data) {
                this.triggerCallback(key, 'success', data.data, changed);
            }

        } catch (error) {
            const retries = this.retryCount.get(key) || 0;
            this.retryCount.set(key, retries + 1);

            this.triggerCallback(key, 'error', error);

            // Stop polling after max retries
            if (retries >= this.maxRetries) {
                this.triggerCallback(key, 'maxRetries', error);
                this.stopPoll(key);
                return;
            }
        }

        // Schedule next poll
        if (this.polls.has(key) && !this.polls.get(key).paused) {
            poll.timer = setTimeout(() => this.executePoll(key), poll.interval);
        }
    }

    /**
     * Stop a specific poll
     */
    stopPoll(key) {
        const poll = this.polls.get(key);
        if (poll) {
            if (poll.timer) {
                clearTimeout(poll.timer);
            }
            this.polls.delete(key);
            this.retryCount.delete(key);
        }
    }

    /**
     * Pause a specific poll
     */
    pausePoll(key) {
        const poll = this.polls.get(key);
        if (poll) {
            poll.paused = true;
            if (poll.timer) {
                clearTimeout(poll.timer);
                poll.timer = null;
            }
        }
    }

    /**
     * Resume a paused poll
     */
    resumePoll(key) {
        const poll = this.polls.get(key);
        if (poll && poll.paused) {
            poll.paused = false;
            this.executePoll(key);
        }
    }

    /**
     * Stop all polls
     */
    stopAllPolls() {
        this.polls.forEach((poll, key) => {
            this.stopPoll(key);
        });
    }

    /**
     * Update poll interval
     */
    setInterval(key, interval) {
        const poll = this.polls.get(key);
        if (poll) {
            poll.interval = interval;
        }
    }

    // ==========================================
    // DASHBOARD POLLING PRESETS
    // ==========================================

    /**
     * Start dashboard polling with preset intervals
     */
    startDashboardPolling() {
        // Stats - every 10 seconds
        this.startPoll('dashboard-stats', 'dashboard/stats', 10000);

        // Active processes - every 3 seconds
        this.startPoll('dashboard-active', 'dashboard/active', 3000);

        // Activity feed - every 5 seconds
        this.startPoll('dashboard-activity', 'dashboard/activity', 5000, { limit: 20 });
    }

    /**
     * Stop dashboard polling
     */
    stopDashboardPolling() {
        this.stopPoll('dashboard-stats');
        this.stopPoll('dashboard-active');
        this.stopPoll('dashboard-activity');
    }

    // ==========================================
    // PROCESS POLLING PRESETS
    // ==========================================

    /**
     * Start polling a specific process
     */
    startProcessPolling(processId) {
        this.startPoll(`process-${processId}`, `process/${processId}/live`, 2000);
    }

    /**
     * Stop polling a specific process
     */
    stopProcessPolling(processId) {
        this.stopPoll(`process-${processId}`);
    }

    // ==========================================
    // CALLBACK SYSTEM
    // ==========================================

    /**
     * Register a callback for a poll
     * @param {string} key - Poll key
     * @param {string} event - Event type: 'data', 'success', 'error', 'maxRetries'
     * @param {function} callback - Callback function
     */
    on(key, event, callback) {
        const callbackKey = `${key}:${event}`;
        if (!this.callbacks.has(callbackKey)) {
            this.callbacks.set(callbackKey, []);
        }
        this.callbacks.get(callbackKey).push(callback);
        return this;
    }

    /**
     * Remove a callback
     */
    off(key, event, callback) {
        const callbackKey = `${key}:${event}`;
        const callbacks = this.callbacks.get(callbackKey);
        if (callbacks) {
            const index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
        }
        return this;
    }

    /**
     * Trigger callbacks for a poll event
     */
    triggerCallback(key, event, ...args) {
        const callbackKey = `${key}:${event}`;
        const callbacks = this.callbacks.get(callbackKey) || [];
        callbacks.forEach(cb => {
            try {
                cb(...args);
            } catch (e) {
                console.error(`Callback error for ${callbackKey}:`, e);
            }
        });
    }

    /**
     * Shorthand for common callbacks
     */
    onProgressUpdate(processId, callback) {
        return this.on(`process-${processId}`, 'success', callback);
    }

    onProcessComplete(processId, callback) {
        return this.on(`process-${processId}`, 'success', (data) => {
            if (data.status === 'completed' || data.status === 'failed' || data.status === 'cancelled') {
                callback(data);
            }
        });
    }

    onDashboardUpdate(callback) {
        this.on('dashboard-stats', 'success', callback);
        this.on('dashboard-active', 'success', callback);
        this.on('dashboard-activity', 'success', callback);
        return this;
    }

    onError(key, callback) {
        return this.on(key, 'error', callback);
    }

    // ==========================================
    // PROCESS CONTROL METHODS
    // ==========================================

    /**
     * Pause a process
     */
    async pauseProcess(processId) {
        return this.postAction(`process/${processId}/pause`);
    }

    /**
     * Resume a process
     */
    async resumeProcess(processId) {
        return this.postAction(`process/${processId}/resume`);
    }

    /**
     * Cancel a process
     */
    async cancelProcess(processId) {
        return this.postAction(`process/${processId}/cancel`);
    }

    /**
     * Delete a process
     */
    async deleteProcess(processId) {
        return this.postAction(`process/${processId}/delete`);
    }

    /**
     * Generic POST action helper
     */
    async postAction(endpoint, data = {}) {
        try {
            const response = await fetch(this.buildUrl(endpoint), {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });

            return await response.json();
        } catch (error) {
            console.error(`Action failed: ${endpoint}`, error);
            return { success: false, error: error.message };
        }
    }

    // ==========================================
    // NOTIFICATION METHODS
    // ==========================================

    /**
     * Request browser notification permission
     */
    async requestNotificationPermission() {
        if (!('Notification' in window)) {
            console.warn('Browser does not support notifications');
            return false;
        }

        try {
            this.notificationPermission = await Notification.requestPermission();
            return this.notificationPermission === 'granted';
        } catch (e) {
            console.error('Notification permission error:', e);
            return false;
        }
    }

    /**
     * Send a browser notification
     */
    sendNotification(title, body, options = {}) {
        if (this.notificationPermission !== 'granted') {
            console.warn('Notification permission not granted');
            return null;
        }

        const defaultOptions = {
            icon: '/assets/images/logo.png',
            badge: '/assets/images/badge.png',
            tag: 'flowbot-notification',
            requireInteraction: false,
            silent: false
        };

        const notification = new Notification(title, {
            body,
            ...defaultOptions,
            ...options
        });

        // Auto close after 5 seconds
        setTimeout(() => notification.close(), 5000);

        return notification;
    }

    /**
     * Send process completion notification
     */
    notifyProcessComplete(processId, stats) {
        const successRate = stats.total > 0
            ? Math.round((stats.success / stats.total) * 100)
            : 0;

        const title = 'Flowbot DCI - Process Complete';
        const body = `Process ${processId.substring(0, 8)} finished.\n` +
                    `Success: ${stats.success}/${stats.total} (${successRate}%)`;

        return this.sendNotification(title, body, {
            tag: `process-${processId}`,
            requireInteraction: true
        });
    }

    // ==========================================
    // UTILITY METHODS
    // ==========================================

    /**
     * Build URL with query parameters
     */
    buildUrl(endpoint, params = {}) {
        let url = `${this.baseUrl}/${endpoint}`;

        const queryParams = new URLSearchParams();
        Object.entries(params).forEach(([key, value]) => {
            if (value !== null && value !== undefined) {
                queryParams.append(key, value);
            }
        });

        const queryString = queryParams.toString();
        if (queryString) {
            url += `?${queryString}`;
        }

        return url;
    }

    /**
     * Format bytes to human readable
     */
    static formatBytes(bytes, decimals = 2) {
        if (bytes === 0) return '0 Bytes';

        const k = 1024;
        const dm = decimals < 0 ? 0 : decimals;
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

        const i = Math.floor(Math.log(bytes) / Math.log(k));

        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
    }

    /**
     * Format duration from seconds
     */
    static formatDuration(seconds) {
        if (seconds < 60) {
            return `${Math.round(seconds)}s`;
        }

        const minutes = Math.floor(seconds / 60);
        const secs = Math.round(seconds % 60);

        if (minutes < 60) {
            return `${minutes}m ${secs}s`;
        }

        const hours = Math.floor(minutes / 60);
        const mins = minutes % 60;

        return `${hours}h ${mins}m`;
    }

    /**
     * Format relative time
     */
    static formatRelativeTime(date) {
        const now = new Date();
        const diff = now - new Date(date);

        const seconds = Math.floor(diff / 1000);
        const minutes = Math.floor(seconds / 60);
        const hours = Math.floor(minutes / 60);
        const days = Math.floor(hours / 24);

        if (seconds < 60) return 'just now';
        if (minutes < 60) return `${minutes}m ago`;
        if (hours < 24) return `${hours}h ago`;
        if (days < 7) return `${days}d ago`;

        return new Date(date).toLocaleDateString();
    }

    /**
     * Cleanup and destroy
     */
    destroy() {
        this.stopAllPolls();
        this.callbacks.clear();
        document.removeEventListener('visibilitychange', this.handleVisibilityChange);
    }
}

// ==========================================
// URL VALIDATOR CLASS
// ==========================================

class FlowbotUrlValidator {
    constructor(options = {}) {
        this.debounceMs = options.debounceMs || 300;
        this.minUrlLength = options.minUrlLength || 10;
        this.blockedDomains = options.blockedDomains || [
            'localhost', '127.0.0.1', '0.0.0.0'
        ];
        this.socialMediaDomains = [
            'twitter.com', 'x.com', 'facebook.com', 'fb.com',
            'instagram.com', 'tiktok.com', 'linkedin.com',
            'youtube.com', 'youtu.be', 'reddit.com',
            'pinterest.com', 'tumblr.com', 'telegram.org', 't.me'
        ];

        this.debounceTimer = null;
    }

    /**
     * Validate a single URL
     */
    validateUrl(url) {
        const result = {
            url: url.trim(),
            valid: false,
            reason: null,
            domain: null,
            isSocialMedia: false,
            isBlocked: false
        };

        // Empty check
        if (!result.url) {
            result.reason = 'Empty URL';
            return result;
        }

        // Length check
        if (result.url.length < this.minUrlLength) {
            result.reason = 'URL too short';
            return result;
        }

        // Protocol check
        if (!result.url.match(/^https?:\/\//i)) {
            result.reason = 'Missing protocol (http:// or https://)';
            return result;
        }

        // Try to parse URL
        try {
            const parsed = new URL(result.url);
            result.domain = parsed.hostname.replace(/^www\./, '');

            // Check blocked domains
            if (this.blockedDomains.some(d => result.domain.includes(d))) {
                result.reason = 'Blocked domain';
                result.isBlocked = true;
                return result;
            }

            // Check social media
            result.isSocialMedia = this.socialMediaDomains.some(d =>
                result.domain === d || result.domain.endsWith('.' + d)
            );

            result.valid = true;

        } catch (e) {
            result.reason = 'Invalid URL format';
        }

        return result;
    }

    /**
     * Validate multiple URLs
     */
    validateUrls(urls) {
        const results = {
            valid: [],
            invalid: [],
            domains: {},
            stats: {
                total: 0,
                valid: 0,
                invalid: 0,
                duplicates: 0,
                socialMedia: 0,
                blocked: 0
            }
        };

        const seen = new Set();

        urls.forEach(url => {
            results.stats.total++;

            const validation = this.validateUrl(url);

            if (validation.valid) {
                // Check for duplicates
                if (seen.has(validation.url)) {
                    results.stats.duplicates++;
                    return;
                }
                seen.add(validation.url);

                results.valid.push(validation);
                results.stats.valid++;

                // Count by domain
                if (!results.domains[validation.domain]) {
                    results.domains[validation.domain] = {
                        count: 0,
                        isSocialMedia: validation.isSocialMedia
                    };
                }
                results.domains[validation.domain].count++;

                if (validation.isSocialMedia) {
                    results.stats.socialMedia++;
                }
            } else {
                results.invalid.push(validation);
                results.stats.invalid++;

                if (validation.isBlocked) {
                    results.stats.blocked++;
                }
            }
        });

        return results;
    }

    /**
     * Parse URLs from textarea text
     */
    parseUrls(text) {
        return text
            .split(/[\n\r]+/)
            .map(line => line.trim())
            .filter(line => line.length > 0);
    }

    /**
     * Debounced validation for live input
     */
    validateDebounced(urls, callback) {
        if (this.debounceTimer) {
            clearTimeout(this.debounceTimer);
        }

        this.debounceTimer = setTimeout(() => {
            const results = this.validateUrls(urls);
            callback(results);
        }, this.debounceMs);
    }
}

// ==========================================
// TOAST NOTIFICATION CLASS
// ==========================================

class FlowbotToast {
    constructor(options = {}) {
        this.container = options.container || this.createContainer();
        this.duration = options.duration || 5000;
        this.maxToasts = options.maxToasts || 5;
    }

    createContainer() {
        let container = document.getElementById('toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'toast-container';
            container.className = 'toast-container';
            document.body.appendChild(container);
        }
        return container;
    }

    show(message, type = 'info', duration = this.duration) {
        // Remove old toasts if at max
        const toasts = this.container.querySelectorAll('.toast');
        if (toasts.length >= this.maxToasts) {
            toasts[0].remove();
        }

        // Create toast element
        const toast = document.createElement('div');
        toast.className = `toast toast-${type}`;

        const icons = {
            success: '✓',
            error: '✕',
            warning: '⚠',
            info: 'ℹ'
        };

        toast.innerHTML = `
            <span class="toast-icon">${icons[type] || icons.info}</span>
            <span class="toast-message">${message}</span>
            <button class="toast-close" onclick="this.parentElement.remove()">×</button>
        `;

        this.container.appendChild(toast);

        // Trigger animation
        requestAnimationFrame(() => {
            toast.classList.add('toast-show');
        });

        // Auto remove
        if (duration > 0) {
            setTimeout(() => {
                toast.classList.remove('toast-show');
                setTimeout(() => toast.remove(), 300);
            }, duration);
        }

        return toast;
    }

    success(message, duration) {
        return this.show(message, 'success', duration);
    }

    error(message, duration) {
        return this.show(message, 'error', duration);
    }

    warning(message, duration) {
        return this.show(message, 'warning', duration);
    }

    info(message, duration) {
        return this.show(message, 'info', duration);
    }
}

// ==========================================
// EXPORT / GLOBAL
// ==========================================

// Make classes globally available
window.FlowbotRealtime = FlowbotRealtime;
window.FlowbotUrlValidator = FlowbotUrlValidator;
window.FlowbotToast = FlowbotToast;

// Create default instances
window.flowbotRealtime = null;
window.flowbotValidator = null;
window.flowbotToast = null;

// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', () => {
    window.flowbotRealtime = new FlowbotRealtime();
    window.flowbotValidator = new FlowbotUrlValidator();
    window.flowbotToast = new FlowbotToast();
});
