const {app, BrowserWindow, screen, ipcMain, dialog, shell} = require('electron');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const {fork} = require('child_process');

const {encryptFileInPlace, decryptFileInPlace} = require('./aes_encrypt');
const {lockFile, unlockFile} = require("../../helpers/lock_mechanism");
const exec = require("nodemon/lib/config/exec");

const isMac = process.platform === 'darwin';
let html_page = undefined;
let mainWindow = undefined;
let alertWindow = undefined;

let fetcherProcess = null;
let backupProcess = null;
let externalEndpointsProcess = null;
let sendFileProcess = null;

const createInitialKeys = () => {
    const SECRET_KEY = crypto.randomBytes(32);
    const IV = crypto.randomBytes(16);

    const secretKeyPath = path.join(__dirname, '..', '..', 'secret.key');
    const ivPath = path.join(__dirname, '..', '..', 'iv.key');

    fs.writeFileSync(secretKeyPath, SECRET_KEY);
    console.log(`Secret Key saved to ${secretKeyPath}`);

    fs.writeFileSync(ivPath, IV);
    console.log(`IV saved to ${ivPath}`);
}

const checkForServerConnection = async () => {
    const pathToIpConfig = path.join(__dirname, '..', '..', 'ipConfig.json');

    try {
        console.log('Checking for server connection');
        await fs.promises.access(pathToIpConfig);

        await lockFile(pathToIpConfig);
        await decryptFileInPlace(pathToIpConfig);
        const ipConfig = await fs.promises.readFile(pathToIpConfig, 'utf-8');
        const { ip } = JSON.parse(ipConfig);

        const response = await fetch(`http://${ip}/heartbeat`);

        await encryptFileInPlace(pathToIpConfig)
        await unlockFile(pathToIpConfig);
        return response.ok;
    } catch (error) {
        console.error("Error:", error);
        try{
            fs.unlinkSync(pathToIpConfig);
        }
        catch{

        }
        return false;
    }
};

async function runStartupChecks() {
    try {
        await fs.promises.access(path.join(__dirname, '..', '..', 'secret.key'), fs.constants.F_OK);
        await fs.promises.access(path.join(__dirname, '..', '..', 'iv.key'), fs.constants.F_OK);
    } catch (err) {
        console.error("Startup error detected:", err);

        // Run the npm clean script
        await exec('npm run clean', { cwd: path.join(__dirname, '..', '..') }, (error, stdout, stderr) => {
            if (error) {
                console.error('Error occurred while running npm run clean:', stderr);
                return;
            }
            console.log('npm run clean output:', stdout);
            createInitialKeys();
        });
    }
}

const createMainWindow = (async (title, width, height) => {
    mainWindow = new BrowserWindow({
        title: title,
        width: width,
        height: height,
        resizable: false,
        icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'),
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    });

    await runStartupChecks();
    html_page = await checkForServerConnection() ? 'login.html' : 'ip_submit.html';

    //mainWindow.setMenu(null);
    mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', html_page))
        .then(() => {
            console.log('Main window loaded!')
        })
        .catch(err => console.error('Failed to load main window:', err));
});

const createAlertWindow = (title, width, height) => {
    alertWindow = new BrowserWindow({
        width: width,
        height: height,
        title: title,
        icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'),
        resizable: false,
        webPreferences: {
            nodeIntegration: false,
            preload: path.join(__dirname, 'preload.js')
        }
    });

    //alertWindow.setMenu(null);

    alertWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', 'alert_modal.html')).then(() => {
        console.log('Alert window loaded!')
    })
        .catch(err => console.error('Failed to load alert window:', err));

    alertWindow.on('closed', () => {
        alertWindow = undefined;
    });
}

function showAlert(message) {
    if (alertWindow === undefined) {
        const title = 'Alert';
        const mainScreen = screen.getPrimaryDisplay();
        const {width, height} = mainScreen.size;
        createAlertWindow(title, width / 4, height / 4);
    }

    alertWindow.webContents.once('dom-ready', () => {
        alertWindow.webContents.executeJavaScript(`showAlert("${message}")`);
    });
}

app.whenReady().then(() => {
    const title = "Application";
    const mainScreen = screen.getPrimaryDisplay();
    const {width, height} = mainScreen.size;
    createMainWindow(title, width / 1.5, height / 1.5);

    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createMainWindow(title, width, height);
        }
    });
});

app.on('before-quit', () => {
    if (backupProcess !== null) {
        backupProcess.kill();
    }
    if (externalEndpointsProcess !== null) {
        externalEndpointsProcess.kill();
    }
    if (fetcherProcess !== null) {
        fetcherProcess.kill();
    }

    if (sendFileProcess !== null) {
        sendFileProcess.kill();
    }
});

app.on('window-all-closed', () => {
    if (!isMac) {
        app.quit();
    }
});

ipcMain.handle('write-file', async (event, fileName, content) => {
    try {
        let filePath = path.join(__dirname, '..', '..', fileName);
        await lockFile(filePath);
        await fs.promises.writeFile(filePath, content);
        await encryptFileInPlace(filePath);
        await unlockFile(filePath)
        console.log(`File successfully written to ${filePath}`);
        return {success: true};
    } catch (error) {
        console.error('Failed to write file:', error);
        return {success: false, error: error.message};
    }
});

ipcMain.handle('delete-file', async (event, fileName) => {
    try {
        let filePath = path.join(__dirname, '..', '..', fileName);

        await fs.promises.unlink(filePath);

        console.log(`File ${filePath} successfully deleted`);
        return {success: true};
    } catch (error) {
        console.error('Failed to delete file:', error);
        return {success: false, error: error.message};
    }
});

ipcMain.handle('read-file', async (event, fileName) => {
    try {
        let filePath = path.join(__dirname, '..', '..', fileName);
        await lockFile(filePath);
        await decryptFileInPlace(filePath);
        const content = await fs.promises.readFile(filePath, 'utf-8');

        await encryptFileInPlace(filePath);
        await unlockFile(filePath);
        return {success: true, content};
    } catch (error) {
        console.error('Error reading file:', error);
        return {success: false, error: error.message};
    }
});

ipcMain.handle('change-content', async (event, nextPage) => {
    try {
        html_page = nextPage;
        await mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', nextPage));
        return true;
    } catch (error) {
        console.error('Error changing content:', error);
        return false;
    }
});

ipcMain.handle('open-dir-dialog', async (event) => {
    try {
        const result = await dialog.showOpenDialog({
            properties: ['openDirectory'],
        });

        if (result.canceled || result.filePaths.length === 0) {
            return {canceled: true}
        }

        return result.filePaths[0];
    } catch (error) {
        console.error('Error opening file dialog:', error);
        return null;
    }
})

ipcMain.handle('open-json-dir-config', async (event, fileName) => {
    try {
        const result = await dialog.showOpenDialog({
            properties: ['openDirectory'],
        });

        if (result.canceled || result.filePaths.length === 0) {
            return {canceled: true}
        }

        const dirPath = result.filePaths[0];
        await fs.promises.writeFile(
            path.join(__dirname, '..', '..', fileName),
            JSON.stringify({
                path: dirPath
            }, null, 2));

        return true;
    } catch (error) {
        console.error('Error opening file dialog:', error);
        return {error: error.message};
    }
});

ipcMain.handle('open-file-dialog', async (event) => {
    const result = await dialog.showOpenDialog({
        properties: ['openFile']
    });

    return result.filePaths[0] || '';
});

ipcMain.handle('check-file-exists', async (event, fileName) => {
    try {
        const filePath = path.join(__dirname, '..', '..', fileName);
        return await fs.promises.access(filePath)
            .then(() => true)
            .catch(() => false);
    } catch (error) {
        console.error('Error checking file existence:', error);
        throw error; // Propagate the error to the renderer process
    }
});

ipcMain.handle('show-alert', async (event, message) => {
    showAlert(message);
});

ipcMain.on('close-alert-window', () => {
    if (alertWindow) {
        alertWindow.close();
        alertWindow = undefined;
    }
});

//External processes
ipcMain.handle('start-main-processes', async (event, args) => {
    if (!backupProcess) {
        backupProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'backup.js'), args, { silent: false });
        backupProcess.on('exit', () => {
            backupProcess = null;
        });
        backupProcess.on('error', (err) => {
            console.log('Backup process error:', err);
        });
    }

    if (!fetcherProcess) {
        fetcherProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'fetcher.js'), args, { silent: false });
        fetcherProcess.on('exit', () => {
            fetcherProcess = null;
        });
        fetcherProcess.on('error', (err) => {
            console.log('Fetcher process error:', err);
        });

        fetcherProcess.on('message', (message) => {
            if (message.type === 'startBackup') {
                backupProcess.send({
                    type: 'startBackup'
                });
            }
        });
    }

    if (!externalEndpointsProcess) {
        externalEndpointsProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'external_endpoints.js'), args, { silent: false });
        externalEndpointsProcess.on('exit', () => {
            externalEndpointsProcess = null;
        });
        externalEndpointsProcess.on('error', (err) => {
            console.log('External endpoints process error:', err);
        });
    }

    return true; // Indicate that the operation has started
});

ipcMain.handle('start-send-file-process', async (event, args) => {
    if (sendFileProcess === null) {
        sendFileProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'send_file.js'), args, {silent: false});
        sendFileProcess.on('exit', () => {
            sendFileProcess = null;
        });
    }
    return true; // Indicate that the operation has started
});

ipcMain.handle('kill-before-logout', async(event) =>{
    if (backupProcess !== null) {
        backupProcess.kill('SIGINT');
    }
    if (externalEndpointsProcess !== null) {
        externalEndpointsProcess.kill('SIGINT');
    }
    if (fetcherProcess !== null) {
        fetcherProcess.kill('SIGINT');
    }

    if (sendFileProcess !== null) {
        sendFileProcess.kill('SIGINT');
    }
})

ipcMain.handle('decrypt-backup-files', async(event, destPath) => {
    console.log('ai intrat in handle')
    if(backupProcess != null){
        console.log('esti in process');
        backupProcess.send({
            type: 'decryptBackup',
            decryptDestPath: destPath
        });
    }
})

// Handle showing a file in the system file explorer
ipcMain.handle('show-file-in-explorer', async (event, filePath) => {
    try {
        // Ensure the file exists before attempting to show it
        await fs.promises.access(filePath, fs.constants.F_OK);
        shell.showItemInFolder(filePath); // Opens the file explorer and highlights the file
        return true;
    } catch (error) {
        console.error('File does not exist:', error);
        return false
    }
});

// Handle removing a path from 'filesReceived.json'
ipcMain.handle('remove-path-from-received-files', async (event, filePath) => {
    try {
        const jsonFilePath = path.join(__dirname, '..', '..', 'filesReceived.json');
        await lockFile(jsonFilePath);
        await decryptFileInPlace(jsonFilePath);
        const data = await fs.promises.readFile(jsonFilePath, 'utf8');

        const jsonData = JSON.parse(data);

        // Filter out the specified file path
        jsonData.receivedFiles = jsonData.receivedFiles.filter(file => file !== filePath);

        await fs.promises.writeFile(jsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8');
        await encryptFileInPlace(jsonFilePath);
        await unlockFile(jsonFilePath);

        return true;
    } catch (error) {
        console.error('Error updating filesReceived.json:', error);
        throw new Error('Failed to update received files list.');
    }
});
