/**
 * Desktop Automation Manager
 *
 * Unified interface for mouse, keyboard, window, and application control.
 * Supports multiple backends (robotjs, nut.js, etc.) with mock for testing.
 */
import { EventEmitter } from 'events';
import { logger } from '../utils/logger.js';
import { DEFAULT_AUTOMATION_CONFIG } from './types.js';
// ============================================================================
// Mock Provider (for testing and fallback)
// ============================================================================
export class MockAutomationProvider {
    name = 'mock';
    capabilities = {
        mouse: true,
        keyboard: true,
        windows: true,
        apps: true,
        screenshots: true,
        colorPicker: true,
        clipboard: true,
        ocr: false,
    };
    mousePos = { x: 500, y: 500 };
    mockWindows = [];
    mockApps = [];
    mockClipboard = { formats: [] };
    initialized = false;
    windowIdCounter = 1;
    appPidCounter = 1000;
    async initialize() {
        this.initialized = true;
        this.mockWindows = [
            {
                handle: 'window-1',
                title: 'Terminal',
                pid: 1001,
                processName: 'terminal',
                bounds: { x: 100, y: 100, width: 800, height: 600 },
                focused: true,
                visible: true,
                minimized: false,
                maximized: false,
                fullscreen: false,
            },
            {
                handle: 'window-2',
                title: 'Browser - Home',
                pid: 1002,
                processName: 'browser',
                bounds: { x: 200, y: 150, width: 1200, height: 800 },
                focused: false,
                visible: true,
                minimized: false,
                maximized: false,
                fullscreen: false,
            },
        ];
        this.mockApps = [
            {
                name: 'Terminal',
                path: '/usr/bin/terminal',
                pid: 1001,
                running: true,
            },
            {
                name: 'Browser',
                path: '/usr/bin/browser',
                pid: 1002,
                running: true,
            },
        ];
    }
    async shutdown() {
        this.initialized = false;
    }
    async isAvailable() {
        return true; // Mock is always available
    }
    // Mouse
    async getMousePosition() {
        return { ...this.mousePos };
    }
    async moveMouse(x, y, options) {
        if (options?.duration && options.duration > 0) {
            await this.simulateDelay(options.duration);
        }
        this.mousePos = { x, y };
    }
    async click(_options) {
        await this.simulateDelay(50);
    }
    async doubleClick(_button) {
        await this.simulateDelay(100);
    }
    async rightClick() {
        await this.simulateDelay(50);
    }
    async drag(fromX, fromY, toX, toY, options) {
        this.mousePos = { x: fromX, y: fromY };
        await this.simulateDelay(options?.duration || 200);
        this.mousePos = { x: toX, y: toY };
    }
    async scroll(_options) {
        await this.simulateDelay(50);
    }
    // Keyboard
    async keyPress(_key, _options) {
        await this.simulateDelay(30);
    }
    async keyDown(_key) {
        await this.simulateDelay(10);
    }
    async keyUp(_key) {
        await this.simulateDelay(10);
    }
    async type(text, options) {
        const delay = options?.delay || 30;
        await this.simulateDelay(text.length * delay);
    }
    async hotkey(_sequence) {
        await this.simulateDelay(50);
    }
    // Windows
    async getActiveWindow() {
        return this.mockWindows.find(w => w.focused) || null;
    }
    async getWindows(options) {
        let windows = [...this.mockWindows];
        if (options?.title) {
            const pattern = options.title instanceof RegExp
                ? options.title
                : new RegExp(options.title, 'i');
            windows = windows.filter(w => pattern.test(w.title));
        }
        if (options?.processName) {
            windows = windows.filter(w => w.processName === options.processName);
        }
        if (options?.pid) {
            windows = windows.filter(w => w.pid === options.pid);
        }
        if (!options?.includeHidden) {
            windows = windows.filter(w => w.visible);
        }
        if (!options?.includeMinimized) {
            windows = windows.filter(w => !w.minimized);
        }
        return windows;
    }
    async getWindow(handle) {
        return this.mockWindows.find(w => w.handle === handle) || null;
    }
    async focusWindow(handle) {
        for (const window of this.mockWindows) {
            window.focused = window.handle === handle;
        }
    }
    async minimizeWindow(handle) {
        const window = this.mockWindows.find(w => w.handle === handle);
        if (window) {
            window.minimized = true;
            window.focused = false;
        }
    }
    async maximizeWindow(handle) {
        const window = this.mockWindows.find(w => w.handle === handle);
        if (window) {
            window.maximized = true;
            window.minimized = false;
        }
    }
    async restoreWindow(handle) {
        const window = this.mockWindows.find(w => w.handle === handle);
        if (window) {
            window.minimized = false;
            window.maximized = false;
        }
    }
    async closeWindow(handle) {
        const index = this.mockWindows.findIndex(w => w.handle === handle);
        if (index >= 0) {
            this.mockWindows.splice(index, 1);
        }
    }
    async setWindow(handle, options) {
        const window = this.mockWindows.find(w => w.handle === handle);
        if (window) {
            if (options.position) {
                window.bounds.x = options.position.x;
                window.bounds.y = options.position.y;
            }
            if (options.size) {
                window.bounds.width = options.size.width;
                window.bounds.height = options.size.height;
            }
            if (options.focus) {
                await this.focusWindow(handle);
            }
        }
    }
    // Applications
    async getRunningApps() {
        return [...this.mockApps];
    }
    async launchApp(appPath, _options) {
        const pid = this.appPidCounter++;
        const name = appPath.split('/').pop() || 'App';
        const app = {
            name,
            path: appPath,
            pid,
            running: true,
        };
        this.mockApps.push(app);
        // Create a window for the app
        const window = {
            handle: `window-${this.windowIdCounter++}`,
            title: name,
            pid,
            processName: name.toLowerCase(),
            bounds: { x: 100 + this.windowIdCounter * 50, y: 100 + this.windowIdCounter * 30, width: 800, height: 600 },
            focused: true,
            visible: true,
            minimized: false,
            maximized: false,
            fullscreen: false,
        };
        this.mockWindows.push(window);
        return app;
    }
    async closeApp(pid) {
        const index = this.mockApps.findIndex(a => a.pid === pid);
        if (index >= 0) {
            this.mockApps.splice(index, 1);
            this.mockWindows = this.mockWindows.filter(w => w.pid !== pid);
        }
    }
    // Screen
    async getScreens() {
        return [
            {
                id: 0,
                name: 'Primary Display',
                bounds: { x: 0, y: 0, width: 1920, height: 1080 },
                workArea: { x: 0, y: 0, width: 1920, height: 1040 },
                scaleFactor: 1,
                primary: true,
                refreshRate: 60,
            },
        ];
    }
    async getPixelColor(x, y) {
        // Return a mock color based on position
        const r = x % 256;
        const g = y % 256;
        const b = (x + y) % 256;
        const hex = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
        return { r, g, b, a: 255, hex };
    }
    // Clipboard
    async getClipboard() {
        return { ...this.mockClipboard };
    }
    async setClipboard(content) {
        this.mockClipboard = {
            ...this.mockClipboard,
            ...content,
            formats: Object.keys(content).filter(k => k !== 'formats' && content[k]),
        };
    }
    async clearClipboard() {
        this.mockClipboard = { formats: [] };
    }
    // Helper
    async simulateDelay(ms) {
        await new Promise(resolve => setTimeout(resolve, Math.min(ms, 100)));
    }
    // Test helpers
    addMockWindow(window) {
        this.mockWindows.push(window);
    }
    removeMockWindow(handle) {
        this.mockWindows = this.mockWindows.filter(w => w.handle !== handle);
    }
}
// ============================================================================
// Automation Manager
// ============================================================================
export class DesktopAutomationManager extends EventEmitter {
    config;
    provider = null;
    providers = new Map();
    failSafeActive = false;
    lastActionTime = 0;
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_AUTOMATION_CONFIG, ...config };
    }
    // ============================================================================
    // Initialization
    // ============================================================================
    /**
     * Register a provider
     */
    registerProvider(provider) {
        this.providers.set(provider.name, provider);
    }
    /**
     * Initialize with the best available provider
     */
    async initialize() {
        // Register mock provider by default
        if (!this.providers.has('mock')) {
            this.registerProvider(new MockAutomationProvider());
        }
        // Try preferred provider first
        const preferred = this.providers.get(this.config.provider);
        if (preferred && await preferred.isAvailable()) {
            await preferred.initialize();
            this.provider = preferred;
            return;
        }
        // Try fallback providers
        for (const fallbackName of this.config.fallbackProviders || []) {
            const fallback = this.providers.get(fallbackName);
            if (fallback && await fallback.isAvailable()) {
                await fallback.initialize();
                this.provider = fallback;
                return;
            }
        }
        // Fall back to mock
        const mock = this.providers.get('mock');
        if (mock) {
            await mock.initialize();
            this.provider = mock;
        }
        else {
            throw new Error('No automation provider available');
        }
    }
    /**
     * Shutdown
     */
    async shutdown() {
        if (this.provider) {
            await this.provider.shutdown();
            this.provider = null;
        }
    }
    /**
     * Get current provider status
     */
    async getProviderStatus() {
        if (!this.provider)
            return null;
        return {
            name: this.provider.name,
            available: await this.provider.isAvailable(),
            capabilities: this.provider.capabilities,
        };
    }
    /**
     * Get all provider statuses
     */
    async getAllProviderStatuses() {
        const statuses = [];
        for (const [_, provider] of this.providers) {
            statuses.push({
                name: provider.name,
                available: await provider.isAvailable(),
                capabilities: provider.capabilities,
            });
        }
        return statuses;
    }
    // ============================================================================
    // Safety
    // ============================================================================
    async checkSafety() {
        if (!this.config.safety.failSafe)
            return;
        const pos = await this.getMousePosition();
        const screens = await this.getScreens();
        const screen = screens[0];
        if (!screen)
            return;
        let isInCorner = false;
        const threshold = 5;
        switch (this.config.safety.failSafeCorner) {
            case 'topLeft':
                isInCorner = pos.x < threshold && pos.y < threshold;
                break;
            case 'topRight':
                isInCorner = pos.x > screen.bounds.width - threshold && pos.y < threshold;
                break;
            case 'bottomLeft':
                isInCorner = pos.x < threshold && pos.y > screen.bounds.height - threshold;
                break;
            case 'bottomRight':
                isInCorner = pos.x > screen.bounds.width - threshold && pos.y > screen.bounds.height - threshold;
                break;
        }
        if (isInCorner && !this.failSafeActive) {
            this.failSafeActive = true;
            this.emit('fail-safe');
            throw new Error('Fail-safe triggered: automation aborted');
        }
    }
    async enforceDelay() {
        const now = Date.now();
        const elapsed = now - this.lastActionTime;
        const minDelay = this.config.safety.minActionDelay;
        if (elapsed < minDelay) {
            await new Promise(resolve => setTimeout(resolve, minDelay - elapsed));
        }
        this.lastActionTime = Date.now();
    }
    ensureProvider() {
        if (!this.provider) {
            throw new Error('Automation not initialized. Call initialize() first.');
        }
        return this.provider;
    }
    // ============================================================================
    // Mouse Operations
    // ============================================================================
    async getMousePosition() {
        return this.ensureProvider().getMousePosition();
    }
    async moveMouse(x, y, options) {
        await this.checkSafety();
        await this.enforceDelay();
        await this.ensureProvider().moveMouse(x, y, options);
        this.emit('mouse-move', { x, y });
    }
    async click(x, y, options) {
        await this.checkSafety();
        await this.enforceDelay();
        if (x !== undefined && y !== undefined) {
            await this.moveMouse(x, y);
        }
        const pos = await this.getMousePosition();
        await this.ensureProvider().click(options);
        this.emit('mouse-click', pos, options?.button || 'left');
    }
    async doubleClick(x, y, button) {
        await this.checkSafety();
        await this.enforceDelay();
        if (x !== undefined && y !== undefined) {
            await this.moveMouse(x, y);
        }
        await this.ensureProvider().doubleClick(button);
    }
    async rightClick(x, y) {
        await this.checkSafety();
        await this.enforceDelay();
        if (x !== undefined && y !== undefined) {
            await this.moveMouse(x, y);
        }
        await this.ensureProvider().rightClick();
    }
    async drag(fromX, fromY, toX, toY, options) {
        await this.checkSafety();
        await this.enforceDelay();
        await this.ensureProvider().drag(fromX, fromY, toX, toY, options);
        this.emit('mouse-drag', { x: fromX, y: fromY }, { x: toX, y: toY });
    }
    async scroll(options) {
        await this.checkSafety();
        await this.enforceDelay();
        await this.ensureProvider().scroll(options);
    }
    // ============================================================================
    // Keyboard Operations
    // ============================================================================
    async keyPress(key, options) {
        await this.checkSafety();
        await this.enforceDelay();
        await this.ensureProvider().keyPress(key, options);
        this.emit('key-press', key, options?.modifiers || []);
    }
    async keyDown(key) {
        await this.enforceDelay();
        await this.ensureProvider().keyDown(key);
    }
    async keyUp(key) {
        await this.enforceDelay();
        await this.ensureProvider().keyUp(key);
    }
    async type(text, options) {
        await this.checkSafety();
        await this.enforceDelay();
        await this.ensureProvider().type(text, options);
        this.emit('key-type', text);
    }
    async hotkey(...keys) {
        await this.checkSafety();
        await this.enforceDelay();
        // Determine modifiers
        const modifiers = [];
        const regularKeys = [];
        for (const key of keys) {
            if (['ctrl', 'alt', 'shift', 'meta', 'command', 'win'].includes(key)) {
                modifiers.push(key);
            }
            else {
                regularKeys.push(key);
            }
        }
        await this.ensureProvider().hotkey({ keys: regularKeys, modifiers });
    }
    // ============================================================================
    // Window Operations
    // ============================================================================
    async getActiveWindow() {
        return this.ensureProvider().getActiveWindow();
    }
    async getWindows(options) {
        return this.ensureProvider().getWindows(options);
    }
    async getWindow(handle) {
        return this.ensureProvider().getWindow(handle);
    }
    async findWindow(titleOrProcess) {
        const windows = await this.getWindows({
            title: titleOrProcess,
            includeHidden: false,
        });
        return windows[0] || null;
    }
    async focusWindow(handle) {
        await this.ensureProvider().focusWindow(handle);
        const window = await this.getWindow(handle);
        if (window) {
            this.emit('window-focus', window);
        }
    }
    async minimizeWindow(handle) {
        await this.ensureProvider().minimizeWindow(handle);
    }
    async maximizeWindow(handle) {
        await this.ensureProvider().maximizeWindow(handle);
    }
    async restoreWindow(handle) {
        await this.ensureProvider().restoreWindow(handle);
    }
    async closeWindow(handle) {
        await this.ensureProvider().closeWindow(handle);
    }
    async setWindow(handle, options) {
        await this.ensureProvider().setWindow(handle, options);
        const window = await this.getWindow(handle);
        if (window) {
            this.emit('window-change', window, options);
        }
    }
    async moveWindow(handle, x, y) {
        await this.setWindow(handle, { position: { x, y } });
    }
    async resizeWindow(handle, width, height) {
        await this.setWindow(handle, { size: { width, height } });
    }
    // ============================================================================
    // Application Operations
    // ============================================================================
    async getRunningApps() {
        return this.ensureProvider().getRunningApps();
    }
    async launchApp(appPath, options) {
        const app = await this.ensureProvider().launchApp(appPath, options);
        this.emit('app-launch', app);
        return app;
    }
    async closeApp(pid) {
        const apps = await this.getRunningApps();
        const app = apps.find(a => a.pid === pid);
        await this.ensureProvider().closeApp(pid);
        if (app) {
            this.emit('app-close', app);
        }
    }
    async findApp(name) {
        const apps = await this.getRunningApps();
        return apps.find(a => a.name.toLowerCase().includes(name.toLowerCase()) ||
            a.path.toLowerCase().includes(name.toLowerCase())) || null;
    }
    // ============================================================================
    // Screen Operations
    // ============================================================================
    async getScreens() {
        return this.ensureProvider().getScreens();
    }
    async getPrimaryScreen() {
        const screens = await this.getScreens();
        return screens.find(s => s.primary) || screens[0] || null;
    }
    async getPixelColor(x, y) {
        return this.ensureProvider().getPixelColor(x, y);
    }
    // ============================================================================
    // Clipboard Operations
    // ============================================================================
    async getClipboard() {
        return this.ensureProvider().getClipboard();
    }
    async setClipboard(content) {
        await this.ensureProvider().setClipboard(content);
    }
    async copyText(text) {
        await this.setClipboard({ text });
    }
    async getClipboardText() {
        const content = await this.getClipboard();
        return content.text;
    }
    async clearClipboard() {
        await this.ensureProvider().clearClipboard();
    }
    // ============================================================================
    // Configuration
    // ============================================================================
    getConfig() {
        return { ...this.config };
    }
    updateConfig(config) {
        this.config = { ...this.config, ...config };
    }
    resetFailSafe() {
        this.failSafeActive = false;
    }
    /**
     * Get current provider
     */
    getProvider() {
        return this.provider;
    }
}
// ============================================================================
// Singleton
// ============================================================================
let automationInstance = null;
export function getDesktopAutomation(config) {
    if (!automationInstance) {
        automationInstance = new DesktopAutomationManager(config);
    }
    return automationInstance;
}
export function resetDesktopAutomation() {
    if (automationInstance) {
        automationInstance.shutdown().catch((err) => {
            logger.debug('Desktop automation shutdown error (ignored)', { error: err instanceof Error ? err.message : String(err) });
        });
        automationInstance = null;
    }
}
//# sourceMappingURL=automation-manager.js.map