const { io } = require('socket.io-client');
const { mouse, keyboard, screen, clipboard, Button, Key, Point } = require('@nut-tree-fork/nut-js');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { execSync, exec } = require('child_process');

// Configure nut-js
mouse.config.autoDelayMs = 0;
keyboard.config.autoDelayMs = 0;

const SERVER = process.env.FASTDESK_SERVER || 'https://fastdesk.makesoft.it';
const TEMP_DIR = path.join(os.tmpdir(), 'fastdesk-files');
fs.mkdirSync(TEMP_DIR, { recursive: true });

console.log(`Connessione a ${SERVER}...`);
const socket = io(SERVER);

socket.on('connect', () => {
    console.log('Connesso al server! Socket ID:', socket.id);
    socket.emit('register-agent', { agentId: socket.id });
    console.log('');
    console.log('  In attesa che l\'host condivida lo schermo...');
    console.log('');
});

socket.on('disconnect', () => console.log('Disconnesso dal server. Riconnessione...'));
socket.on('connect_error', (err) => console.log('Errore connessione:', err.message));

socket.on('host-session-available', (data) => {
    console.log('Sessione host disponibile:', data.sessionId);
    socket.emit('agent-join-session', { sessionId: data.sessionId });
    console.log('Agent collegato alla sessione', data.sessionId);
});

// ===================== FILE RECEIVE (Viewer → Remote) =====================
// File arrives in chunks, saved to temp, then placed in system clipboard as file drop
const fileBuffers = {};

socket.on('file-transfer-start', (data) => {
    const { fileId, name, size, totalChunks } = data;
    console.log(`Ricezione file: ${name} (${(size / 1024 / 1024).toFixed(1)} MB)`);
    fileBuffers[fileId] = { name, chunks: [], totalChunks, received: 0 };
});

socket.on('file-transfer-chunk', (data) => {
    const { fileId, index, chunk } = data;
    const fb = fileBuffers[fileId];
    if (!fb) return;
    fb.chunks[index] = Buffer.from(chunk, 'base64');
    fb.received++;
});

socket.on('file-transfer-end', (data) => {
    const { fileId } = data;
    const fb = fileBuffers[fileId];
    if (!fb) return;

    try {
        const filePath = path.join(TEMP_DIR, fb.name);
        const fullBuffer = Buffer.concat(fb.chunks);
        fs.writeFileSync(filePath, fullBuffer);
        console.log(`File ricevuto: ${filePath}`);

        // Put the file in the system clipboard so user can Ctrl+V anywhere
        setFileInClipboard(filePath);
        console.log(`File pronto per incolla (Ctrl+V)`);

        socket.emit('file-transfer-complete', { fileId, name: fb.name, path: filePath });
    } catch (e) {
        console.log('Errore salvataggio file:', e.message);
    }
    delete fileBuffers[fileId];
});

// ===================== CLIPBOARD FILE DROP (Platform-specific) =====================

function setFileInClipboard(filePath) {
    if (process.platform === 'win32') {
        // PowerShell: put file in clipboard as file drop list
        const psCmd = `
Add-Type -AssemblyName System.Windows.Forms
$files = New-Object System.Collections.Specialized.StringCollection
$files.Add('${filePath.replace(/'/g, "''")}')
[System.Windows.Forms.Clipboard]::SetFileDropList($files)
`;
        try {
            execSync(`powershell -NoProfile -Command "${psCmd.replace(/\n/g, '; ')}"`, { stdio: 'pipe' });
        } catch (e) {
            console.log('Errore clipboard file:', e.message);
        }
    } else if (process.platform === 'darwin') {
        // macOS: use osascript to set clipboard
        try {
            execSync(`osascript -e 'set the clipboard to POSIX file "${filePath}"'`, { stdio: 'pipe' });
        } catch (e) {
            console.log('Errore clipboard file:', e.message);
        }
    } else {
        // Linux: use xclip if available
        try {
            execSync(`echo "copy\nfile://${filePath}" | xclip -selection clipboard -t x-special/gnome-copied-files`, { stdio: 'pipe' });
        } catch (e) {
            console.log('Errore clipboard file:', e.message);
        }
    }
}

// ===================== FILE SEND (Remote → Viewer) =====================
// When user copies a file on remote (Ctrl+C on a file), detect it and stream to viewer

function getFilesFromClipboard() {
    if (process.platform === 'win32') {
        try {
            const result = execSync(`powershell -NoProfile -Command "
Add-Type -AssemblyName System.Windows.Forms
$files = [System.Windows.Forms.Clipboard]::GetFileDropList()
if ($files.Count -gt 0) { $files | ForEach-Object { Write-Output $_ } }
"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
            if (result) return result.split('\n').map(f => f.trim()).filter(f => f && fs.existsSync(f));
        } catch (e) {}
    }
    return [];
}

function streamFileToViewer(filePath) {
    const name = path.basename(filePath);
    const stat = fs.statSync(filePath);
    if (stat.size > 100 * 1024 * 1024) {
        console.log('File troppo grande per il trasferimento (max 100MB)');
        return;
    }

    const CHUNK_SIZE = 64 * 1024;
    const buffer = fs.readFileSync(filePath);
    const totalChunks = Math.ceil(buffer.length / CHUNK_SIZE);
    const fileId = Date.now().toString(36) + Math.random().toString(36).slice(2);

    console.log(`Invio file al viewer: ${name} (${(stat.size / 1024 / 1024).toFixed(1)} MB)`);

    socket.emit('file-to-viewer-start', { fileId, name, size: stat.size, totalChunks });

    for (let i = 0; i < totalChunks; i++) {
        const start = i * CHUNK_SIZE;
        const end = Math.min(start + CHUNK_SIZE, buffer.length);
        const chunk = buffer.slice(start, end).toString('base64');
        socket.emit('file-to-viewer-chunk', { fileId, index: i, chunk });
    }

    socket.emit('file-to-viewer-end', { fileId });
    console.log(`File inviato: ${name}`);
}

// ===================== INPUT COMMANDS =====================
socket.on('input-command', async (cmd) => {
    try {
        switch (cmd.type) {
            case 'mouseMove': {
                const w = await screen.width();
                const h = await screen.height();
                await mouse.setPosition(new Point(Math.round(cmd.x * w), Math.round(cmd.y * h)));
                break;
            }
            case 'mouseDown': {
                if (cmd.x !== undefined && cmd.y !== undefined && cmd.x !== 0) {
                    const w = await screen.width();
                    const h = await screen.height();
                    await mouse.setPosition(new Point(Math.round(cmd.x * w), Math.round(cmd.y * h)));
                }
                await mouse.pressButton(mapButton(cmd.button));
                break;
            }
            case 'mouseUp': {
                await mouse.releaseButton(mapButton(cmd.button));
                break;
            }
            case 'mouseClick': {
                if (cmd.x !== undefined && cmd.y !== undefined) {
                    const w = await screen.width();
                    const h = await screen.height();
                    await mouse.setPosition(new Point(Math.round(cmd.x * w), Math.round(cmd.y * h)));
                }
                await mouse.click(mapButton(cmd.button));
                break;
            }
            case 'scroll': {
                const amount = cmd.deltaY > 0 ? 3 : -3;
                if (amount > 0) await mouse.scrollDown(amount);
                else await mouse.scrollUp(Math.abs(amount));
                break;
            }
            case 'keyDown': {
                const key = mapKey(cmd.key);
                if (key !== null) await keyboard.pressKey(key);
                break;
            }
            case 'keyUp': {
                const key = mapKey(cmd.key);
                if (key !== null) await keyboard.releaseKey(key);
                break;
            }

            // --- TEXT CLIPBOARD ---
            case 'clipboardPaste': {
                if (cmd.text) {
                    await clipboard.copy(cmd.text);
                    await keyboard.pressKey(Key.LeftControl);
                    await keyboard.pressKey(Key.V);
                    await keyboard.releaseKey(Key.V);
                    await keyboard.releaseKey(Key.LeftControl);
                }
                break;
            }
            case 'clipboardCopy': {
                // Simulate Ctrl+C on remote
                await keyboard.pressKey(Key.LeftControl);
                await keyboard.pressKey(Key.C);
                await keyboard.releaseKey(Key.C);
                await keyboard.releaseKey(Key.LeftControl);
                await new Promise(r => setTimeout(r, 300));

                // Check if clipboard has files
                const files = getFilesFromClipboard();
                if (files.length > 0) {
                    // Stream files back to viewer
                    for (const f of files) {
                        try {
                            if (fs.statSync(f).isFile()) {
                                streamFileToViewer(f);
                            }
                        } catch (e) {}
                    }
                } else {
                    // Text clipboard
                    try {
                        const text = await clipboard.paste();
                        if (text) socket.emit('clipboard-sync', { text });
                    } catch (e) {}
                }
                break;
            }

            // --- FILE PASTE (viewer sent a file, user wants to paste it) ---
            case 'filePaste': {
                // The file was already received and put in clipboard by file-transfer-end handler
                // Just simulate Ctrl+V
                await keyboard.pressKey(Key.LeftControl);
                await keyboard.pressKey(Key.V);
                await keyboard.releaseKey(Key.V);
                await keyboard.releaseKey(Key.LeftControl);
                break;
            }
        }
    } catch (e) {}
});

// ===================== KEY MAPPING =====================
function mapKey(keyName) {
    if (keyName.length === 1) {
        const upper = keyName.toUpperCase();
        if (Key[upper] !== undefined) return Key[upper];
        if (keyName >= '0' && keyName <= '9') return Key[`Num${keyName}`] || Key[keyName];
        return null;
    }
    const keyMap = {
        'Enter': Key.Enter, 'Backspace': Key.Backspace, 'Tab': Key.Tab,
        'Escape': Key.Escape, 'Delete': Key.Delete, 'Home': Key.Home,
        'End': Key.End, 'PageUp': Key.PageUp, 'PageDown': Key.PageDown,
        'ArrowUp': Key.Up, 'ArrowDown': Key.Down, 'ArrowLeft': Key.Left, 'ArrowRight': Key.Right,
        'Control': Key.LeftControl, 'Shift': Key.LeftShift, 'Alt': Key.LeftAlt, 'Meta': Key.LeftSuper,
        ' ': Key.Space, 'Space': Key.Space,
        'F1': Key.F1, 'F2': Key.F2, 'F3': Key.F3, 'F4': Key.F4,
        'F5': Key.F5, 'F6': Key.F6, 'F7': Key.F7, 'F8': Key.F8,
        'F9': Key.F9, 'F10': Key.F10, 'F11': Key.F11, 'F12': Key.F12,
        'CapsLock': Key.CapsLock, 'Insert': Key.Insert,
    };
    return keyMap[keyName] || null;
}

function mapButton(btnName) {
    if (btnName === 'left') return Button.LEFT;
    if (btnName === 'right') return Button.RIGHT;
    if (btnName === 'middle') return Button.MIDDLE;
    return Button.LEFT;
}
