const { spawn } = require('child_process');
const { promisify } = require('util');
const fetch = require('node-fetch');
const path = require('path');
const fs = require('fs').promises;
const { app, BrowserWindow } = require('electron');
const LocalAIServiceBase = require('./localAIServiceBase');
const { spawnAsync } = require('../utils/spawnHelper');
const { DOWNLOAD_CHECKSUMS } = require('../config/checksums');
const ollamaModelRepository = require('../repositories/ollamaModel');

class OllamaService extends LocalAIServiceBase {
    constructor() {
        super('OllamaService');
        this.baseUrl = 'http://localhost:11434';
        this.warmingModels = new Map();
        this.warmedModels = new Set();
        this.lastWarmUpAttempt = new Map();
        
        // Request management system
        this.activeRequests = new Map();
        this.requestTimeouts = new Map();
        this.healthStatus = {
            lastHealthCheck: 0,
            consecutive_failures: 0,
            is_circuit_open: false
        };
        
        // Configuration
        this.requestTimeout = 0; // Delete timeout
        this.warmupTimeout = 120000; // 120s for model warmup
        this.healthCheckInterval = 60000; // 1min between health checks
        this.circuitBreakerThreshold = 3;
        this.circuitBreakerCooldown = 30000; // 30s
        
        // Supported models are determined dynamically from installed models
        this.supportedModels = {};
        
        // Start health monitoring
        this._startHealthMonitoring();
    }

    // 모든 윈도우에 이벤트 브로드캐스트
    _broadcastToAllWindows(eventName, data = null) {
        BrowserWindow.getAllWindows().forEach(win => {
            if (win && !win.isDestroyed()) {
                if (data !== null) {
                    win.webContents.send(eventName, data);
                } else {
                    win.webContents.send(eventName);
                }
            }
        });
    }

    async getStatus() {
        try {
            const installed = await this.isInstalled();
            if (!installed) {
                return { success: true, installed: false, running: false, models: [] };
            }

            const running = await this.isServiceRunning();
            if (!running) {
                return { success: true, installed: true, running: false, models: [] };
            }

            const models = await this.getInstalledModels();
            return { success: true, installed: true, running: true, models };
        } catch (error) {
            console.error('[OllamaService] Error getting status:', error);
            return { success: false, error: error.message, installed: false, running: false, models: [] };
        }
    }

    getOllamaCliPath() {
        if (this.getPlatform() === 'darwin') {
            return '/Applications/Ollama.app/Contents/Resources/ollama';
        }
        return 'ollama';
    }

    /**
     * Professional request management with AbortController-based cancellation
     */
    async _makeRequest(url, options = {}, operationType = 'default') {
        const requestId = `${operationType}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        
        // Circuit breaker check
        if (this._isCircuitOpen()) {
            throw new Error('Service temporarily unavailable (circuit breaker open)');
        }
        
        // Request deduplication for health checks
        if (operationType === 'health' && this.activeRequests.has('health')) {
            console.log('[OllamaService] Health check already in progress, returning existing promise');
            return this.activeRequests.get('health');
        }
        
        const controller = new AbortController();
        const timeout = options.timeout || this.requestTimeout;
        
        // Set up timeout mechanism only if timeout > 0
        let timeoutId = null;
        if (timeout > 0) {
            timeoutId = setTimeout(() => {
                controller.abort();
                this.activeRequests.delete(requestId);
                this._recordFailure();
            }, timeout);
            
            this.requestTimeouts.set(requestId, timeoutId);
        }
        
        const requestPromise = this._executeRequest(url, {
            ...options,
            signal: controller.signal
        }, requestId);
        
        // Store active request for deduplication and cleanup
        this.activeRequests.set(operationType === 'health' ? 'health' : requestId, requestPromise);
        
        try {
            const result = await requestPromise;
            this._recordSuccess();
            return result;
        } catch (error) {
            this._recordFailure();
            if (error.name === 'AbortError') {
                throw new Error(`Request timeout after ${timeout}ms`);
            }
            throw error;
        } finally {
            if (timeoutId !== null) {
                clearTimeout(timeoutId);
                this.requestTimeouts.delete(requestId);
            }
            this.activeRequests.delete(operationType === 'health' ? 'health' : requestId);
        }
    }
    
    async _executeRequest(url, options, requestId) {
        try {
            console.log(`[OllamaService] Executing request ${requestId} to ${url}`);
            const response = await fetch(url, options);
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            return response;
        } catch (error) {
            console.error(`[OllamaService] Request ${requestId} failed:`, error.message);
            throw error;
        }
    }
    
    _isCircuitOpen() {
        if (!this.healthStatus.is_circuit_open) return false;
        
        // Check if cooldown period has passed
        const now = Date.now();
        if (now - this.healthStatus.lastHealthCheck > this.circuitBreakerCooldown) {
            console.log('[OllamaService] Circuit breaker cooldown expired, attempting recovery');
            this.healthStatus.is_circuit_open = false;
            this.healthStatus.consecutive_failures = 0;
            return false;
        }
        
        return true;
    }
    
    _recordSuccess() {
        this.healthStatus.consecutive_failures = 0;
        this.healthStatus.is_circuit_open = false;
        this.healthStatus.lastHealthCheck = Date.now();
    }
    
    _recordFailure() {
        this.healthStatus.consecutive_failures++;
        this.healthStatus.lastHealthCheck = Date.now();
        
        if (this.healthStatus.consecutive_failures >= this.circuitBreakerThreshold) {
            console.warn(`[OllamaService] Circuit breaker opened after ${this.healthStatus.consecutive_failures} failures`);
            this.healthStatus.is_circuit_open = true;
        }
    }
    
    _startHealthMonitoring() {
        // Passive health monitoring - only when requests are made
        console.log('[OllamaService] Health monitoring system initialized');
    }
    
    /**
     * Cleanup all active requests and resources
     */
    _cleanup() {
        console.log(`[OllamaService] Cleaning up ${this.activeRequests.size} active requests`);
        
        // Cancel all active requests
        for (const [requestId, promise] of this.activeRequests) {
            if (this.requestTimeouts.has(requestId)) {
                clearTimeout(this.requestTimeouts.get(requestId));
                this.requestTimeouts.delete(requestId);
            }
        }
        
        this.activeRequests.clear();
        this.requestTimeouts.clear();
    }

    async isInstalled() {
        try {
            const platform = this.getPlatform();
            
            if (platform === 'darwin') {
                try {
                    await fs.access('/Applications/Ollama.app');
                    return true;
                } catch {
                    const ollamaPath = await this.checkCommand(this.getOllamaCliPath());
                    return !!ollamaPath;
                }
            } else {
                const ollamaPath = await this.checkCommand(this.getOllamaCliPath());
                return !!ollamaPath;
            }
        } catch (error) {
            console.log('[OllamaService] Ollama not found:', error.message);
            return false;
        }
    }

    async isServiceRunning() {
        try {
            const response = await this._makeRequest(`${this.baseUrl}/api/tags`, {
                method: 'GET',
                timeout: this.requestTimeout
            }, 'health');
            
            return response.ok;
        } catch (error) {
            console.log(`[OllamaService] Service health check failed: ${error.message}`);
            return false;
        }
    }

    async startService() {
        const platform = this.getPlatform();
        
        try {
            if (platform === 'darwin') {
                try {
                    await spawnAsync('open', ['-a', 'Ollama']);
                    await this.waitForService(() => this.isServiceRunning());
                    return true;
                } catch {
                    spawn(this.getOllamaCliPath(), ['serve'], {
                        detached: true,
                        stdio: 'ignore'
                    }).unref();
                    await this.waitForService(() => this.isServiceRunning());
                    return true;
                }
            } else {
                spawn(this.getOllamaCliPath(), ['serve'], {
                    detached: true,
                    stdio: 'ignore',
                    shell: platform === 'win32'
                }).unref();
                await this.waitForService(() => this.isServiceRunning());
                return true;
            }
        } catch (error) {
            console.error('[OllamaService] Failed to start service:', error);
            throw error;
        }
    }

    async stopService() {
        return await this.shutdown();
    }

    async getInstalledModels() {
        try {
            const response = await this._makeRequest(`${this.baseUrl}/api/tags`, {
                method: 'GET',
                timeout: this.requestTimeout
            }, 'models');
            
            const data = await response.json();
            return data.models || [];
        } catch (error) {
            console.error('[OllamaService] Failed to get installed models:', error.message);
            return [];
        }
    }

    async getInstalledModelsList() {
        try {
            const { stdout } = await spawnAsync(this.getOllamaCliPath(), ['list']);
            const lines = stdout.split('\n').filter(line => line.trim());
            
            // Skip header line (NAME, ID, SIZE, MODIFIED)
            const modelLines = lines.slice(1);
            
            const models = [];
            for (const line of modelLines) {
                if (!line.trim()) continue;
                
                // Parse line: "model:tag    model_id    size    modified_time"
                const parts = line.split(/\s+/);
                if (parts.length >= 3) {
                    models.push({
                        name: parts[0],
                        id: parts[1],
                        size: parts[2] + (parts[3] === 'GB' || parts[3] === 'MB' ? ' ' + parts[3] : ''),
                        status: 'installed'
                    });
                }
            }
            
            return models;
        } catch (error) {
            console.log('[OllamaService] Failed to get installed models via CLI, falling back to API');
            // Fallback to API if CLI fails
            const apiModels = await this.getInstalledModels();
            return apiModels.map(model => ({
                name: model.name,
                id: model.digest || 'unknown',
                size: model.size || 'Unknown',
                status: 'installed'
            }));
        }
    }

    async getModelSuggestions() {
        try {
            // Get actually installed models
            const installedModels = await this.getInstalledModelsList();
            
            // Get user input history from storage (we'll implement this in the frontend)
            // For now, just return installed models
            return installedModels;
        } catch (error) {
            console.error('[OllamaService] Failed to get model suggestions:', error);
            return [];
        }
    }

    async isModelInstalled(modelName) {
        const models = await this.getInstalledModels();
        return models.some(model => model.name === modelName);
    }

    async pullModel(modelName) {
        if (!modelName?.trim()) {
            throw new Error(`Invalid model name: ${modelName}`);
        }

        console.log(`[OllamaService] Starting to pull model: ${modelName} via API`);
        
        try {
            const response = await fetch(`${this.baseUrl}/api/pull`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    model: modelName,
                    stream: true
                })
            });

            if (!response.ok) {
                throw new Error(`Pull API failed: ${response.status} ${response.statusText}`);
            }

            // Handle Node.js streaming response
            return new Promise((resolve, reject) => {
                let buffer = '';
                
                response.body.on('data', (chunk) => {
                    buffer += chunk.toString();
                    const lines = buffer.split('\n');
                    
                    // Keep incomplete line in buffer
                    buffer = lines.pop() || '';
                    
                    // Process complete lines
                    for (const line of lines) {
                        if (!line.trim()) continue;
                        
                        try {
                            const data = JSON.parse(line);
                            const progress = this._parseOllamaPullProgress(data, modelName);
                            
                            if (progress !== null) {
                                this.setInstallProgress(modelName, progress);
                                this._broadcastToAllWindows('ollama:pull-progress', { 
                                    model: modelName, 
                                    progress,
                                    status: data.status || 'downloading'
                                });
                                console.log(`[OllamaService] API Progress: ${progress}% for ${modelName} (${data.status || 'downloading'})`);
                            }

                            // Handle completion
                            if (data.status === 'success') {
                                console.log(`[OllamaService] Successfully pulled model: ${modelName}`);
                                this._broadcastToAllWindows('ollama:pull-complete', { model: modelName });
                                this.clearInstallProgress(modelName);
                                resolve();
                                return;
                            }
                        } catch (parseError) {
                            console.warn('[OllamaService] Failed to parse response line:', line);
                        }
                    }
                });

                response.body.on('end', () => {
                    // Process any remaining data in buffer
                    if (buffer.trim()) {
                        try {
                            const data = JSON.parse(buffer);
                            if (data.status === 'success') {
                                console.log(`[OllamaService] Successfully pulled model: ${modelName}`);
                                this._broadcastToAllWindows('ollama:pull-complete', { model: modelName });
                            }
                        } catch (parseError) {
                            console.warn('[OllamaService] Failed to parse final buffer:', buffer);
                        }
                    }
                    this.clearInstallProgress(modelName);
                    resolve();
                });

                response.body.on('error', (error) => {
                    console.error(`[OllamaService] Stream error for ${modelName}:`, error);
                    this.clearInstallProgress(modelName);
                    reject(error);
                });
            });
        } catch (error) {
            this.clearInstallProgress(modelName);
            console.error(`[OllamaService] Pull model failed:`, error);
            throw error;
        }
    }

    _parseOllamaPullProgress(data, modelName) {
        // Handle Ollama API response format
        if (data.status === 'success') {
            return 100;
        }

        // Handle downloading progress
        if (data.total && data.completed !== undefined) {
            const progress = Math.round((data.completed / data.total) * 100);
            return Math.min(progress, 99); // Don't show 100% until success
        }

        // Handle status-based progress
        const statusProgress = {
            'pulling manifest': 5,
            'downloading': 10,
            'verifying sha256 digest': 90,
            'writing manifest': 95,
            'removing any unused layers': 98
        };

        if (data.status && statusProgress[data.status] !== undefined) {
            return statusProgress[data.status];
        }

        return null;
    }



    async installMacOS(onProgress) {
        console.log('[OllamaService] Installing Ollama on macOS using DMG...');
        
        try {
            const dmgUrl = 'https://ollama.com/download/Ollama.dmg';
            const tempDir = app.getPath('temp');
            const dmgPath = path.join(tempDir, 'Ollama.dmg');
            const mountPoint = path.join(tempDir, 'OllamaMount');

            console.log('[OllamaService] Step 1: Downloading Ollama DMG...');
            onProgress?.({ stage: 'downloading', message: 'Downloading Ollama installer...', progress: 0 });
            const checksumInfo = DOWNLOAD_CHECKSUMS.ollama.dmg;
            await this.downloadWithRetry(dmgUrl, dmgPath, {
                expectedChecksum: checksumInfo?.sha256,
                onProgress: (progress) => {
                    onProgress?.({ stage: 'downloading', message: `Downloading... ${progress}%`, progress });
                }
            });
            
            console.log('[OllamaService] Step 2: Mounting DMG...');
            onProgress?.({ stage: 'mounting', message: 'Mounting disk image...', progress: 0 });
            await fs.mkdir(mountPoint, { recursive: true });
            await spawnAsync('hdiutil', ['attach', dmgPath, '-mountpoint', mountPoint]);
            onProgress?.({ stage: 'mounting', message: 'Disk image mounted.', progress: 100 });
            
            console.log('[OllamaService] Step 3: Installing Ollama.app...');
            onProgress?.({ stage: 'installing', message: 'Installing Ollama application...', progress: 0 });
            await spawnAsync('cp', ['-R', `${mountPoint}/Ollama.app`, '/Applications/']);
            onProgress?.({ stage: 'installing', message: 'Application installed.', progress: 100 });
            
            console.log('[OllamaService] Step 4: Setting up CLI path...');
            onProgress?.({ stage: 'linking', message: 'Creating command-line shortcut...', progress: 0 });
            try {
                const script = `do shell script "mkdir -p /usr/local/bin && ln -sf '${this.getOllamaCliPath()}' '/usr/local/bin/ollama'" with administrator privileges`;
                await spawnAsync('osascript', ['-e', script]);
                onProgress?.({ stage: 'linking', message: 'Shortcut created.', progress: 100 });
            } catch (linkError) {
                console.error('[OllamaService] CLI symlink creation failed:', linkError.message);
                onProgress?.({ stage: 'linking', message: 'Shortcut creation failed (permissions?).', progress: 100 });
                // Not throwing an error, as the app might still work
            }
            
            console.log('[OllamaService] Step 5: Cleanup...');
            onProgress?.({ stage: 'cleanup', message: 'Cleaning up installation files...', progress: 0 });
            await spawnAsync('hdiutil', ['detach', mountPoint]);
            await fs.unlink(dmgPath).catch(() => {});
            await fs.rmdir(mountPoint).catch(() => {});
            onProgress?.({ stage: 'cleanup', message: 'Cleanup complete.', progress: 100 });
            
            console.log('[OllamaService] Ollama installed successfully on macOS');
            
            await new Promise(resolve => setTimeout(resolve, 2000));
            
            return true;
        } catch (error) {
            console.error('[OllamaService] macOS installation failed:', error);
            throw new Error(`Failed to install Ollama on macOS: ${error.message}`);
        }
    }

    async installWindows(onProgress) {
        console.log('[OllamaService] Installing Ollama on Windows...');
        
        try {
            const exeUrl = 'https://ollama.com/download/OllamaSetup.exe';
            const tempDir = app.getPath('temp');
            const exePath = path.join(tempDir, 'OllamaSetup.exe');

            console.log('[OllamaService] Step 1: Downloading Ollama installer...');
            onProgress?.({ stage: 'downloading', message: 'Downloading Ollama installer...', progress: 0 });
            const checksumInfo = DOWNLOAD_CHECKSUMS.ollama.exe;
            await this.downloadWithRetry(exeUrl, exePath, {
                expectedChecksum: checksumInfo?.sha256,
                onProgress: (progress) => {
                    onProgress?.({ stage: 'downloading', message: `Downloading... ${progress}%`, progress });
                }
            });
            
            console.log('[OllamaService] Step 2: Running silent installation...');
            onProgress?.({ stage: 'installing', message: 'Installing Ollama...', progress: 0 });
            await spawnAsync(exePath, ['/VERYSILENT', '/NORESTART']);
            onProgress?.({ stage: 'installing', message: 'Installation complete.', progress: 100 });
            
            console.log('[OllamaService] Step 3: Cleanup...');
            onProgress?.({ stage: 'cleanup', message: 'Cleaning up installation files...', progress: 0 });
            await fs.unlink(exePath).catch(() => {});
            onProgress?.({ stage: 'cleanup', message: 'Cleanup complete.', progress: 100 });
            
            console.log('[OllamaService] Ollama installed successfully on Windows');
            
            await new Promise(resolve => setTimeout(resolve, 3000));
            
            return true;
        } catch (error) {
            console.error('[OllamaService] Windows installation failed:', error);
            throw new Error(`Failed to install Ollama on Windows: ${error.message}`);
        }
    }

    async installLinux() {
        console.log('[OllamaService] Installing Ollama on Linux...');
        console.log('[OllamaService] Automatic installation on Linux is not supported for security reasons.');
        console.log('[OllamaService] Please install Ollama manually:');
        console.log('[OllamaService] 1. Visit https://ollama.com/download/linux');
        console.log('[OllamaService] 2. Follow the official installation instructions');
        console.log('[OllamaService] 3. Or use your package manager if available');
        throw new Error('Manual installation required on Linux. Please visit https://ollama.com/download/linux');
    }



    async warmUpModel(modelName, forceRefresh = false) {
        if (!modelName?.trim()) {
            console.warn(`[OllamaService] Invalid model name for warm-up`);
            return false;
        }

        // Check if already warmed (and not forcing refresh)
        if (!forceRefresh && this.warmedModels.has(modelName)) {
            console.log(`[OllamaService] Model ${modelName} already warmed up, skipping`);
            return true;
        }

        // Check if currently warming - return existing Promise
        if (this.warmingModels.has(modelName)) {
            console.log(`[OllamaService] Model ${modelName} is already warming up, joining existing operation`);
            return await this.warmingModels.get(modelName);
        }

        // Check rate limiting (prevent too frequent attempts)
        const lastAttempt = this.lastWarmUpAttempt.get(modelName);
        const now = Date.now();
        if (lastAttempt && (now - lastAttempt) < 5000) { // 5 second cooldown
            console.log(`[OllamaService] Rate limiting warm-up for ${modelName}, try again in ${5 - Math.floor((now - lastAttempt) / 1000)}s`);
            return false;
        }

        // Create and store the warming Promise
        const warmingPromise = this._performWarmUp(modelName);
        this.warmingModels.set(modelName, warmingPromise);
        this.lastWarmUpAttempt.set(modelName, now);

        try {
            const result = await warmingPromise;
            
            if (result) {
                this.warmedModels.add(modelName);
                console.log(`[OllamaService] Model ${modelName} successfully warmed up`);
            }
            
            return result;
        } finally {
            // Always clean up the warming Promise
            this.warmingModels.delete(modelName);
        }
    }

    async _performWarmUp(modelName) {
        console.log(`[OllamaService] Starting warm-up for model: ${modelName}`);
        
        try {
            const response = await this._makeRequest(`${this.baseUrl}/api/chat`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    model: modelName,
                    messages: [
                        { role: 'user', content: 'Hi' }
                    ],
                    stream: false,
                    options: {
                        num_predict: 1, // Minimal response
                        temperature: 0
                    }
                }),
                timeout: this.warmupTimeout
            }, `warmup_${modelName}`);

            return true;
        } catch (error) {
            // Check if it's a 404 error (model not found/installed)
            if (error.message.includes('HTTP 404') || error.message.includes('Not Found')) {
                console.log(`[OllamaService] Model ${modelName} not found (404), attempting to install...`);
                
                try {
                    // Try to install the model
                    await this.pullModel(modelName);
                    console.log(`[OllamaService] Successfully installed model ${modelName}, retrying warm-up...`);
                    
                    // Update database to reflect installation
                    await ollamaModelRepository.updateInstallStatus(modelName, true, false);
                    
                    // Retry warm-up after installation
                    const retryResponse = await this._makeRequest(`${this.baseUrl}/api/chat`, {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            model: modelName,
                            messages: [
                                { role: 'user', content: 'Hi' }
                            ],
                            stream: false,
                            options: {
                                num_predict: 1,
                                temperature: 0
                            }
                        }),
                        timeout: this.warmupTimeout
                    }, `warmup_retry_${modelName}`);
                    
                    console.log(`[OllamaService] Successfully warmed up model ${modelName} after installation`);
                    return true;
                    
                } catch (installError) {
                    console.error(`[OllamaService] Failed to auto-install model ${modelName}:`, installError.message);
                    await ollamaModelRepository.updateInstallStatus(modelName, false, false);
                    return false;
                }
            } else {
                console.error(`[OllamaService] Failed to warm up model ${modelName}:`, error.message);
                return false;
            }
        }
    }

    async autoWarmUpSelectedModel() {
        try {
            // Get selected model from ModelStateService
            const modelStateService = global.modelStateService;
            if (!modelStateService) {
                console.log('[OllamaService] ModelStateService not available for auto warm-up');
                return false;
            }

            const selectedModels = modelStateService.getSelectedModels();
            const llmModelId = selectedModels.llm;
            
            // Check if it's an Ollama model
            const provider = modelStateService.getProviderForModel('llm', llmModelId);
            if (provider !== 'ollama') {
                console.log('[OllamaService] Selected LLM is not Ollama, skipping warm-up');
                return false;
            }

            // Check if Ollama service is running
            const isRunning = await this.isServiceRunning();
            if (!isRunning) {
                console.log('[OllamaService] Ollama service not running, clearing warm-up cache');
                this._clearWarmUpCache();
                return false;
            }

            // 설치 여부 체크 제거 - _performWarmUp에서 자동으로 설치 처리
            console.log(`[OllamaService] Auto-warming up selected model: ${llmModelId} (will auto-install if needed)`);
            return await this.warmUpModel(llmModelId);
            
        } catch (error) {
            console.error('[OllamaService] Auto warm-up failed:', error);
            return false;
        }
    }

    _clearWarmUpCache() {
        this.warmedModels.clear();
        this.warmingModels.clear();
        this.lastWarmUpAttempt.clear();
        console.log('[OllamaService] Warm-up cache cleared');
    }

    getWarmUpStatus() {
        return {
            warmedModels: Array.from(this.warmedModels),
            warmingModels: Array.from(this.warmingModels.keys()),
            lastAttempts: Object.fromEntries(this.lastWarmUpAttempt)
        };
    }

    async shutdown(force = false) {
        console.log(`[OllamaService] Shutdown initiated (force: ${force})`);
        
        if (!force && this.warmingModels.size > 0) {
            const warmingList = Array.from(this.warmingModels.keys());
            console.log(`[OllamaService] Waiting for ${warmingList.length} models to finish warming: ${warmingList.join(', ')}`);
            
            const warmingPromises = Array.from(this.warmingModels.values());
            try {
                // Use Promise.allSettled instead of race with setTimeout
                const results = await Promise.allSettled(warmingPromises);
                const completed = results.filter(r => r.status === 'fulfilled').length;
                console.log(`[OllamaService] ${completed}/${results.length} warming operations completed`);
            } catch (error) {
                console.log('[OllamaService] Error waiting for warm-up completion, proceeding with shutdown');
            }
        }

        // Clean up all resources
        this._cleanup();
        this._clearWarmUpCache();
        
        return super.shutdown(force);
    }

    async shutdownMacOS(force) {
        try {
            // Try to quit Ollama.app gracefully
            await spawnAsync('osascript', ['-e', 'tell application "Ollama" to quit']);
            console.log('[OllamaService] Ollama.app quit successfully');
            
            // Wait a moment for graceful shutdown
            await new Promise(resolve => setTimeout(resolve, 2000));
            
            // Check if still running
            const stillRunning = await this.isServiceRunning();
            if (stillRunning) {
                console.log('[OllamaService] Ollama still running, forcing shutdown');
                // Force kill if necessary
                await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]);
            }
            
            return true;
        } catch (error) {
            console.log('[OllamaService] Graceful quit failed, trying force kill');
            try {
                await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]);
                return true;
            } catch (killError) {
                console.error('[OllamaService] Failed to force kill Ollama:', killError);
                return false;
            }
        }
    }

    async shutdownWindows(force) {
        try {
            // Try to stop the service gracefully
            await spawnAsync('taskkill', ['/IM', 'ollama.exe', '/T']);
            console.log('[OllamaService] Ollama process terminated on Windows');
            return true;
        } catch (error) {
            console.log('[OllamaService] Standard termination failed, trying force kill');
            try {
                await spawnAsync('taskkill', ['/IM', 'ollama.exe', '/F', '/T']);
                return true;
            } catch (killError) {
                console.error('[OllamaService] Failed to force kill Ollama on Windows:', killError);
                return false;
            }
        }
    }

    async shutdownLinux(force) {
        try {
            await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]);
            console.log('[OllamaService] Ollama process terminated on Linux');
            return true;
        } catch (error) {
            if (force) {
                await spawnAsync('pkill', ['-9', '-f', this.getOllamaCliPath()]).catch(() => {});
            }
            console.error('[OllamaService] Failed to shutdown Ollama on Linux:', error);
            return false;
        }
    }

    async getAllModelsWithStatus() {
        // Get all installed models directly from Ollama
        const installedModels = await this.getInstalledModels();
        
        const models = [];
        for (const model of installedModels) {
            models.push({
                name: model.name,
                displayName: model.name, // Use model name as display name
                size: model.size || 'Unknown',
                description: `Ollama model: ${model.name}`,
                installed: true,
                installing: this.installationProgress.has(model.name),
                progress: this.getInstallProgress(model.name)
            });
        }
        
        // Also add any models currently being installed
        for (const [modelName, progress] of this.installationProgress) {
            if (!models.find(m => m.name === modelName)) {
                models.push({
                    name: modelName,
                    displayName: modelName,
                    size: 'Unknown',
                    description: `Ollama model: ${modelName}`,
                    installed: false,
                    installing: true,
                    progress: progress
                });
            }
        }
        
        return models;
    }

    async handleGetStatus() {
        try {
            const installed = await this.isInstalled();
            if (!installed) {
                return { success: true, installed: false, running: false, models: [] };
            }

            const running = await this.isServiceRunning();
            if (!running) {
                return { success: true, installed: true, running: false, models: [] };
            }

            const models = await this.getAllModelsWithStatus();
            return { success: true, installed: true, running: true, models };
        } catch (error) {
            console.error('[OllamaService] Error getting status:', error);
            return { success: false, error: error.message, installed: false, running: false, models: [] };
        }
    }

    async handleInstall() {
        try {
            const onProgress = (data) => {
                this._broadcastToAllWindows('ollama:install-progress', data);
            };

            await this.autoInstall(onProgress);

            if (!await this.isServiceRunning()) {
                onProgress({ stage: 'starting', message: 'Starting Ollama service...', progress: 0 });
                await this.startService();
                onProgress({ stage: 'starting', message: 'Ollama service started.', progress: 100 });
            }
            this._broadcastToAllWindows('ollama:install-complete', { success: true });
            return { success: true };
        } catch (error) {
            console.error('[OllamaService] Failed to install:', error);
            this._broadcastToAllWindows('ollama:install-complete', { success: false, error: error.message });
            return { success: false, error: error.message };
        }
    }

    async handleStartService() {
        try {
            if (!await this.isServiceRunning()) {
                console.log('[OllamaService] Starting Ollama service...');
                await this.startService();
            }
            this.emit('install-complete', { success: true });
            return { success: true };
        } catch (error) {
            console.error('[OllamaService] Failed to start service:', error);
            this.emit('install-complete', { success: false, error: error.message });
            return { success: false, error: error.message };
        }
    }

    async handleEnsureReady() {
        try {
            if (await this.isInstalled() && !await this.isServiceRunning()) {
                console.log('[OllamaService] Ollama installed but not running, starting service...');
                await this.startService();
            }
            return { success: true };
        } catch (error) {
            console.error('[OllamaService] Failed to ensure ready:', error);
            return { success: false, error: error.message };
        }
    }

    async handleGetModels() {
        try {
            const models = await this.getAllModelsWithStatus();
            return { success: true, models };
        } catch (error) {
            console.error('[OllamaService] Failed to get models:', error);
            return { success: false, error: error.message };
        }
    }

    async handleGetModelSuggestions() {
        try {
            const suggestions = await this.getModelSuggestions();
            return { success: true, suggestions };
        } catch (error) {
            console.error('[OllamaService] Failed to get model suggestions:', error);
            return { success: false, error: error.message };
        }
    }

    async handlePullModel(modelName) {
        try {
            console.log(`[OllamaService] Starting model pull: ${modelName}`);

            await ollamaModelRepository.updateInstallStatus(modelName, false, true);

            await this.pullModel(modelName);

            await ollamaModelRepository.updateInstallStatus(modelName, true, false);

            console.log(`[OllamaService] Model ${modelName} pull successful`);
            return { success: true };
        } catch (error) {
            console.error('[OllamaService] Failed to pull model:', error);
            await ollamaModelRepository.updateInstallStatus(modelName, false, false);
            this._broadcastToAllWindows('ollama:pull-error', { model: modelName, error: error.message });
            return { success: false, error: error.message };
        }
    }

    async handleIsModelInstalled(modelName) {
        try {
            const installed = await this.isModelInstalled(modelName);
            return { success: true, installed };
        } catch (error) {
            console.error('[OllamaService] Failed to check model installation:', error);
            return { success: false, error: error.message };
        }
    }

    async handleWarmUpModel(modelName) {
        try {
            const success = await this.warmUpModel(modelName);
            return { success };
        } catch (error) {
            console.error('[OllamaService] Failed to warm up model:', error);
            return { success: false, error: error.message };
        }
    }

    async handleAutoWarmUp() {
        try {
            const success = await this.autoWarmUpSelectedModel();
            return { success };
        } catch (error) {
            console.error('[OllamaService] Failed to auto warm-up:', error);
            return { success: false, error: error.message };
        }
    }

    async handleGetWarmUpStatus() {
        try {
            const status = this.getWarmUpStatus();
            return { success: true, status };
        } catch (error) {
            console.error('[OllamaService] Failed to get warm-up status:', error);
            return { success: false, error: error.message };
        }
    }

    async handleShutdown(force = false) {
        try {
            console.log(`[OllamaService] Manual shutdown requested (force: ${force})`);
            const success = await this.shutdown(force);
            return { success };
        } catch (error) {
            console.error('[OllamaService] Failed to shutdown Ollama:', error);
            return { success: false, error: error.message };
        }
    }
}

// Export singleton instance
const ollamaService = new OllamaService();
module.exports = ollamaService;