// GrabZilla 2.1 - Application State Management
// Centralized state management with event system

class AppState {
    constructor() {
        this.videos = [];
        this.history = []; // Array of completed download history entries
        this.config = {
            savePath: this.getDefaultDownloadsPath(),
            defaultQuality: window.AppConfig?.APP_CONFIG?.DEFAULT_QUALITY || '1080p',
            defaultFormat: window.AppConfig?.APP_CONFIG?.DEFAULT_FORMAT || 'None',
            filenamePattern: window.AppConfig?.APP_CONFIG?.DEFAULT_FILENAME_PATTERN || '%(title)s.%(ext)s',
            cookieFile: null,
            maxHistoryEntries: 100, // Maximum number of history entries to keep
            clipboardConsent: null // null = not asked, true = allowed, false = denied
        };
        this.ui = {
            isDownloading: false,
            selectedVideos: [],
            sortBy: 'createdAt',
            sortOrder: 'desc',
            keyboardNavigationActive: false,
            currentFocusIndex: -1
        };
        this.listeners = new Map();
        this.downloadQueue = [];
        this.downloadStats = {
            totalDownloads: 0,
            successfulDownloads: 0,
            failedDownloads: 0,
            totalBytesDownloaded: 0
        };
    }

    // Get default downloads path based on platform
    getDefaultDownloadsPath() {
        const defaultPaths = window.AppConfig?.DEFAULT_PATHS || {
            darwin: '~/Downloads/GrabZilla_Videos',
            win32: 'C:\\Users\\Admin\\Desktop\\GrabZilla_Videos',
            linux: '~/Downloads/GrabZilla_Videos'
        };

        if (window.electronAPI) {
            try {
                const platform = window.electronAPI.getPlatform();
                return defaultPaths[platform] || defaultPaths.linux;
            } catch (error) {
                logger.warn('Failed to get platform:', error);
                return defaultPaths.win32;
            }
        }
        return defaultPaths.win32;
    }

    // Video management methods
    addVideo(video) {
        if (!(video instanceof window.Video)) {
            throw new Error('Invalid video object');
        }

        // Check for duplicate URLs
        const existingVideo = this.videos.find(v => v.getNormalizedUrl() === video.getNormalizedUrl());
        if (existingVideo) {
            throw new Error('Video URL already exists in the list');
        }

        this.videos.push(video);
        this.emit('videoAdded', { video });
        return video;
    }

    // Add multiple videos from URLs
    async addVideosFromUrls(urls, options = {}) {
        const { allowDuplicates = false } = options;

        const results = {
            successful: [],
            failed: [],
            duplicates: []
        };

        // Filter out duplicates first (unless allowDuplicates is true)
        const uniqueUrls = [];
        for (const url of urls) {
            if (allowDuplicates) {
                // Skip duplicate check
                uniqueUrls.push(url);
            } else {
                const normalizedUrl = window.URLValidator ? window.URLValidator.normalizeUrl(url) : url;
                const existingVideo = this.videos.find(v => v.getNormalizedUrl() === normalizedUrl);

                if (existingVideo) {
                    results.duplicates.push({ url, reason: 'URL already exists' });
                } else {
                    uniqueUrls.push(url);
                }
            }
        }

        // Create videos immediately for instant UI feedback (non-blocking)
        for (const url of uniqueUrls) {
            try {
                const video = window.Video.fromUrl(url);
                this.addVideo(video);
                results.successful.push(video);
            } catch (error) {
                results.failed.push({ url, error: error.message });
            }
        }

        // Prefetch metadata in background (non-blocking, parallel for better UX)
        // Videos will update automatically via Video.fromUrl() metadata fetch
        if (uniqueUrls.length > 0 && window.MetadataService) {
            logger.debug(`[Batch Metadata] Starting background fetch for ${uniqueUrls.length} URLs...`);
            const startTime = performance.now();

            // Don't await - let it run in background
            window.MetadataService.prefetchMetadata(uniqueUrls)
                .then(() => {
                    const duration = performance.now() - startTime;
                    logger.debug(`[Batch Metadata] Completed in ${Math.round(duration)}ms (${Math.round(duration / uniqueUrls.length)}ms avg/video)`);
                })
                .catch(error => {
                    logger.warn('[Batch Metadata] Batch prefetch failed:', error.message);
                });
        }

        this.emit('videosAdded', { results });
        return results;
    }

    // Reorder videos in the array
    reorderVideos(fromIndex, toIndex) {
        if (fromIndex === toIndex) return;

        if (fromIndex < 0 || fromIndex >= this.videos.length ||
            toIndex < 0 || toIndex > this.videos.length) {
            throw new Error('Invalid indices for reordering');
        }

        // Remove from old position
        const [movedVideo] = this.videos.splice(fromIndex, 1);

        // Insert at new position
        const adjustedToIndex = fromIndex < toIndex ? toIndex - 1 : toIndex;
        this.videos.splice(adjustedToIndex, 0, movedVideo);

        this.emit('videosReordered', {
            fromIndex,
            toIndex: adjustedToIndex,
            videoId: movedVideo.id
        });

        logger.debug(`Reordered video from position ${fromIndex} to ${adjustedToIndex}`);
    }

    // Remove video from state
    removeVideo(videoId) {
        const index = this.videos.findIndex(v => v.id === videoId);
        if (index === -1) {
            throw new Error('Video not found');
        }

        const removedVideo = this.videos.splice(index, 1)[0];

        // Remove from selected videos if present
        this.ui.selectedVideos = this.ui.selectedVideos.filter(id => id !== videoId);

        this.emit('videoRemoved', { video: removedVideo });
        return removedVideo;
    }

    // Remove multiple videos
    removeVideos(videoIds) {
        const removedVideos = [];
        const errors = [];

        for (const videoId of videoIds) {
            try {
                const removed = this.removeVideo(videoId);
                removedVideos.push(removed);
            } catch (error) {
                errors.push({ videoId, error: error.message });
            }
        }

        this.emit('videosRemoved', { removedVideos, errors });
        return { removedVideos, errors };
    }

    // Update video in state
    updateVideo(videoId, properties) {
        const video = this.videos.find(v => v.id === videoId);
        if (!video) {
            throw new Error('Video not found');
        }

        const oldProperties = { ...video };
        video.update(properties);
        this.emit('videoUpdated', { video, oldProperties });
        return video;
    }

    // Get video by ID
    getVideo(videoId) {
        return this.videos.find(v => v.id === videoId);
    }

    // Get all videos
    getVideos() {
        return [...this.videos];
    }

    // Get videos by status
    getVideosByStatus(status) {
        return this.videos.filter(v => v.status === status);
    }

    // Get selected videos
    getSelectedVideos() {
        return this.videos.filter(v => this.ui.selectedVideos.includes(v.id));
    }

    // Clear all videos
    clearVideos() {
        const removedVideos = [...this.videos];
        this.videos = [];
        this.ui.selectedVideos = [];
        this.downloadQueue = [];
        this.emit('videosCleared', { removedVideos });
        return removedVideos;
    }

    // Selection management
    selectVideo(videoId, multiSelect = false) {
        if (!multiSelect) {
            this.ui.selectedVideos = [videoId];
        } else {
            if (!this.ui.selectedVideos.includes(videoId)) {
                this.ui.selectedVideos.push(videoId);
            }
        }
        this.emit('videoSelectionChanged', { selectedVideos: this.ui.selectedVideos });
    }

    deselectVideo(videoId) {
        this.ui.selectedVideos = this.ui.selectedVideos.filter(id => id !== videoId);
        this.emit('videoSelectionChanged', { selectedVideos: this.ui.selectedVideos });
    }

    selectAllVideos() {
        this.ui.selectedVideos = this.videos.map(v => v.id);
        this.emit('videoSelectionChanged', { selectedVideos: this.ui.selectedVideos });
    }

    deselectAllVideos() {
        this.ui.selectedVideos = [];
        this.emit('videoSelectionChanged', { selectedVideos: this.ui.selectedVideos });
    }

    toggleVideoSelection(videoId) {
        if (this.ui.selectedVideos.includes(videoId)) {
            this.deselectVideo(videoId);
        } else {
            this.selectVideo(videoId, true);
        }
    }

    // Sort videos
    sortVideos(sortBy, sortOrder = 'asc') {
        this.ui.sortBy = sortBy;
        this.ui.sortOrder = sortOrder;

        this.videos.sort((a, b) => {
            let valueA, valueB;

            switch (sortBy) {
                case 'title':
                    valueA = a.getDisplayName().toLowerCase();
                    valueB = b.getDisplayName().toLowerCase();
                    break;
                case 'duration':
                    valueA = a.duration || '00:00';
                    valueB = b.duration || '00:00';
                    break;
                case 'status':
                    valueA = a.status;
                    valueB = b.status;
                    break;
                case 'quality':
                    valueA = a.quality;
                    valueB = b.quality;
                    break;
                case 'format':
                    valueA = a.format;
                    valueB = b.format;
                    break;
                case 'createdAt':
                default:
                    valueA = a.createdAt.getTime();
                    valueB = b.createdAt.getTime();
                    break;
            }

            if (sortOrder === 'desc') {
                return valueA > valueB ? -1 : valueA < valueB ? 1 : 0;
            } else {
                return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
            }
        });

        this.emit('videosSorted', { sortBy, sortOrder });
    }

    // Configuration management
    updateConfig(newConfig) {
        const oldConfig = { ...this.config };
        Object.assign(this.config, newConfig);
        this.emit('configUpdated', { config: this.config, oldConfig });
    }

    // Clipboard consent management
    hasClipboardConsent() {
        return this.config.clipboardConsent === true;
    }

    hasClipboardConsentAnswer() {
        return this.config.clipboardConsent !== null;
    }

    setClipboardConsent(allowed) {
        this.config.clipboardConsent = allowed;
        this.emit('clipboardConsentChanged', { allowed });
        // Persist to localStorage
        this.saveState();
    }

    resetClipboardConsent() {
        this.config.clipboardConsent = null;
        this.emit('clipboardConsentChanged', { allowed: null });
        this.saveState();
    }

    // UI state management
    updateUI(newUIState) {
        const oldUIState = { ...this.ui };
        Object.assign(this.ui, newUIState);
        this.emit('uiUpdated', { ui: this.ui, oldUIState });
    }

    // Download queue management
    addToDownloadQueue(videoIds) {
        const newItems = videoIds.filter(id => !this.downloadQueue.includes(id));
        this.downloadQueue.push(...newItems);
        this.emit('downloadQueueUpdated', { downloadQueue: this.downloadQueue });
    }

    removeFromDownloadQueue(videoId) {
        this.downloadQueue = this.downloadQueue.filter(id => id !== videoId);
        this.emit('downloadQueueUpdated', { downloadQueue: this.downloadQueue });
    }

    clearDownloadQueue() {
        this.downloadQueue = [];
        this.emit('downloadQueueUpdated', { downloadQueue: this.downloadQueue });
    }

    // Event system for state changes
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }

    off(event, callback) {
        if (this.listeners.has(event)) {
            const callbacks = this.listeners.get(event);
            const index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
        }
    }

    emit(event, data) {
        if (this.listeners.has(event)) {
            this.listeners.get(event).forEach(callback => {
                try {
                    callback(data);
                } catch (error) {
                    logger.error(`Error in event listener for ${event}:`, error.message);
                }
            });
        }
    }

    // Statistics and analytics
    getStats() {
        const statusCounts = {
            total: this.videos.length,
            ready: 0,
            downloading: 0,
            converting: 0,
            completed: 0,
            error: 0
        };

        this.videos.forEach(video => {
            statusCounts[video.status] = (statusCounts[video.status] || 0) + 1;
        });

        return {
            ...statusCounts,
            selected: this.ui.selectedVideos.length,
            queueLength: this.downloadQueue.length,
            downloadStats: { ...this.downloadStats }
        };
    }

    // Update download statistics
    updateDownloadStats(stats) {
        Object.assign(this.downloadStats, stats);
        this.emit('downloadStatsUpdated', { stats: this.downloadStats });
    }

    // Keyboard navigation support
    setKeyboardNavigationActive(active) {
        this.ui.keyboardNavigationActive = active;
        if (!active) {
            this.ui.currentFocusIndex = -1;
        }
        this.emit('keyboardNavigationChanged', { active });
    }

    setCurrentFocusIndex(index) {
        this.ui.currentFocusIndex = Math.max(-1, Math.min(index, this.videos.length - 1));
        this.emit('focusIndexChanged', { index: this.ui.currentFocusIndex });
    }

    // State persistence
    toJSON() {
        return {
            videos: this.videos.map(v => v.toJSON()),
            history: this.history,
            config: this.config,
            ui: {
                ...this.ui,
                selectedVideos: this.ui.selectedVideos // Keep selected videos
            },
            downloadQueue: this.downloadQueue,
            downloadStats: this.downloadStats,
            timestamp: new Date().toISOString()
        };
    }

    // State restoration
    fromJSON(data) {
        try {
            // Restore videos
            this.videos = (data.videos || []).map(v => window.Video.fromJSON(v));

            // Restore history
            this.history = data.history || [];

            // Restore config with defaults
            this.config = {
                ...this.config,
                ...data.config
            };

            // Restore UI state with defaults
            this.ui = {
                ...this.ui,
                ...data.ui,
                keyboardNavigationActive: false, // Reset navigation state
                currentFocusIndex: -1
            };

            // Restore download queue and stats
            this.downloadQueue = data.downloadQueue || [];
            this.downloadStats = {
                ...this.downloadStats,
                ...data.downloadStats
            };

            this.emit('stateImported', { data });
            return true;
        } catch (error) {
            logger.error('Failed to restore state from JSON:', error.message);
            return false;
        }
    }

    // Validation and cleanup
    validateState() {
        // Remove invalid videos
        this.videos = this.videos.filter(video => {
            try {
                return video instanceof window.Video && video.url;
            } catch (error) {
                logger.warn('Removing invalid video:', error);
                return false;
            }
        });

        // Clean up selected videos
        const videoIds = this.videos.map(v => v.id);
        this.ui.selectedVideos = this.ui.selectedVideos.filter(id => videoIds.includes(id));

        // Clean up download queue
        this.downloadQueue = this.downloadQueue.filter(id => videoIds.includes(id));

        this.emit('stateValidated');
    }

    // Add completed video to download history
    addToHistory(video) {
        const historyEntry = {
            id: video.id,
            url: video.url,
            title: video.title,
            thumbnail: video.thumbnail,
            duration: video.duration,
            quality: video.quality,
            format: video.format,
            filename: video.filename,
            downloadedAt: new Date().toISOString()
        };

        // Add to beginning of history array
        this.history.unshift(historyEntry);

        // Keep only maxHistoryEntries
        if (this.history.length > this.config.maxHistoryEntries) {
            this.history = this.history.slice(0, this.config.maxHistoryEntries);
        }

        this.emit('historyUpdated', { entry: historyEntry });
    }

    // Get all history entries
    getHistory() {
        return this.history;
    }

    // Clear all history
    clearHistory() {
        this.history = [];
        this.emit('historyCleared');
    }

    // Remove specific history entry
    removeHistoryEntry(entryId) {
        const index = this.history.findIndex(entry => entry.id === entryId);
        if (index !== -1) {
            const removed = this.history.splice(index, 1)[0];
            this.emit('historyEntryRemoved', { entry: removed });
        }
    }

    // Reset to initial state
    reset() {
        this.videos = [];
        this.history = [];
        this.ui.selectedVideos = [];
        this.ui.currentFocusIndex = -1;
        this.downloadQueue = [];
        this.downloadStats = {
            totalDownloads: 0,
            successfulDownloads: 0,
            failedDownloads: 0,
            totalBytesDownloaded: 0
        };
        this.emit('stateReset');
    }
}

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
    // Node.js environment
    module.exports = AppState;
} else {
    // Browser environment - attach to window
    window.AppState = AppState;
}