/* eslint-disable consistent-return */
/* eslint global-require: off, no-console: off, promise/always-return: off */
/* eslint-disable no-bitwise */
/**
 * This module executes inside of electron's main process. You can start
 * electron renderer process from here and communicate with the other processes
 * through IPC.
 *
 * When running `npm run build` or `npm run build:main`, this file is compiled to
 * `./src/main.js` using webpack. This gives us some performance wins.
 */
import path from 'path';
import os from "os";
import {
  app,
  BrowserWindow,
  dialog,
  ipcMain,
  session,
  shell,
  Cookie,
} from 'electron';
import { autoUpdater } from 'electron-updater';
import { SerialPort } from 'serialport';
import { ReadlineParser } from '@serialport/parser-readline';
import express from 'express';
import AutoLaunch from 'auto-launch';
import cron from 'node-cron';
import fs from 'fs';
import getMAC from 'getmac';
import net from 'net';
import fetch from 'electron-fetch';
import log from 'electron-log';
import { AutoDetectTypes } from '@serialport/bindings-cpp';
import xml2js from 'xml2js';
import webpackPaths from '../../.erb/configs/webpack.paths';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import Store from './utils/storeData';
import 'regenerator-runtime/runtime';


const localDataPath = path.join(os.homedir(), "AppData", "Local", "came-access");
app.setPath("userData", localDataPath);
console.log(`UserData path changed: ${app.getPath("userData")}`);

const url = require('url');

const parserXml = new xml2js.Parser({ explicitArray: false });

const POS_PACKAGE_ID = `Display Came Access: 1.0.17`;

console.log('Using POSPackageID:', POS_PACKAGE_ID);

const scheduleDailyBatchClose = () => {
  // 23:55 every day (local time)
  cron.schedule('55 23 * * *', () => {
    if (mainWindow) {
      mainWindow.webContents.send('auto-batchClose');
      console.log('[CRON] auto-batchClose sent @ 23:55');
    }
  });
};
 
// --- Unique IDs for EMV transactions ---
let __emvSeq = 0;
function nextSeq() {
  __emvSeq = (__emvSeq + 1) % 100000; // 0..99999
  return __emvSeq.toString().padStart(5, '0');
}

function makeInvoiceNo(): string {
  // 12 numeric chars (YYMMDDHHmm + 2-digit seq)
  const d = new Date();
  const yy = d.getFullYear().toString().slice(-2);
  const MM = String(d.getMonth() + 1).padStart(2, '0');
  const DD = String(d.getDate()).padStart(2, '0');
  const hh = String(d.getHours()).padStart(2, '0');
  const mm = String(d.getMinutes()).padStart(2, '0');
  // 10 chars timestamp + last 2 of the seq = 12
  return `${yy}${MM}${DD}${hh}${mm}${nextSeq().slice(-2)}`;
}


// --- EMV Logging Helper (TStream + raw RStream + concise summary) ---
function logEmvBlock(
  label: string,
  tStream: string,
  rStreamRaw: string,
  summary: Record<string, any>
) {
  console.log(`\n---- ${label} ----`);
  console.log("[TStream]");
  console.log(tStream);

  console.log("\n[RStream]");
  console.log(rStreamRaw);

  console.log("\n[Summary]");
  console.log(summary);
}

// Normalize BatchSummary into a flat object we can echo back for BatchClose
function normalizeBatchSummary(RStream: any) {
  const S = RStream?.BatchSummary ?? RStream;
  const out: Record<string, string> = {};

  ['TerminalID','BatchNo','BatchItemCount','NetBatchTotal'].forEach(k => {
    if (S?.[k] != null) out[k] = String(S[k]);
  });

  Object.keys(S || {}).forEach(k => {
    if (/(Count|Amount)$/.test(k) && S[k] != null) out[k] = String(S[k]);
  });

  return out;
}


async function datacapEMVParamDownload(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string
) {
  try {
    const obj = {
      TStream: {
        Admin: {
          MerchantID: merchantId,
          OperatorID: operatorId,
          POSPackageID: POS_PACKAGE_ID,
          TranCode: 'EMVParamDownload',
          SecureDevice: 'CloudEMV2',
          UserTrace: 'CAME-Trace',
          SequenceNo: '0010010010',
          TranDeviceID: tranDeviceId,
        },
      },
    };

       const builder = new xml2js.Builder({ headless: false, renderOpts: { pretty: false } });
    const tstreamXml = builder.buildObject(obj);

    const response = await fetch(`http://${ipEmv}:8080/ProcessEMVTransaction/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/xml' },
      body: tstreamXml,
    });

    const rstreamRaw = await response.text();
    const parsed = await parserXml.parseStringPromise(rstreamRaw);
    const RStream = parsed?.RStream ?? parsed;

    // Build a small summary for readability
    const summary = {
      CmdStatus: RStream?.CmdStatus ?? 'Unknown',
      DSIXReturnCode: RStream?.DSIXReturnCode ?? '—',
      TextResponse: RStream?.TextResponse ?? '',
      SequenceNo: RStream?.SequenceNo ?? '',
      TranCode: RStream?.TranCode ?? 'EMVParamDownload',
    };

    // Unified logging block
    logEmvBlock("EMVParamDownload", tstreamXml, rstreamRaw, summary);

    const payload = {
      Timestamp: new Date().toISOString(),
      Url: `http://${ipEmv}:8080/ProcessEMVTransaction/`,
      TStream: tstreamXml,
      RStreamRaw: rstreamRaw,
      RStream,
    };

    return payload;
  } catch (e) {
    console.error('EMVParamDownload Error:', e);
    return 'ERROR';
  }
}

async function datacapBatchSummary(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string,
  terminalId?: string
) {
  try {
    const obj: any = {
      TStream: {
        Admin: {
          MerchantID: merchantId,
          POSPackageID: POS_PACKAGE_ID,
          SecureDevice: 'CloudEMV2',
          OperatorID: operatorId,
          UserTrace: 'CAME-Trace',
          TranType: 'Administrative',
          TranCode: 'BatchSummary',
          SequenceNo: '0010010010',
          TranDeviceID: tranDeviceId,
        },
      },
    };
    if (terminalId) obj.TStream.Admin.TerminalID = terminalId;

    const builder = new xml2js.Builder({ headless: false, renderOpts: { pretty: false } });
    const tstreamXml = builder.buildObject(obj);

    const response = await fetch(`http://${ipEmv}:8080/ProcessEMVTransaction/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/xml' },
      body: tstreamXml,
    });

    const rstreamRaw = await response.text();
    const parsed = await parserXml.parseStringPromise(rstreamRaw);
    const RStream = parsed?.RStream ?? parsed;

    logEmvBlock('BatchSummary', tstreamXml, rstreamRaw, {
      CmdStatus: RStream?.CmdStatus,
      DSIXReturnCode: RStream?.DSIXReturnCode,
      BatchNo: RStream?.BatchNo,
      TextResponse: RStream?.TextResponse,
    });

    return { 
      Timestamp: new Date().toISOString(), 
      Url: `http://${ipEmv}:8080/ProcessEMVTransaction/`, 
      TStream: tstreamXml, 
      RStreamRaw: rstreamRaw, 
      RStream,
      normalized: normalizeBatchSummary(RStream)
    };
  } catch (e) {
    console.error('BatchSummary Error:', e);
    return 'ERROR';
  }
}

async function datacapBatchCloseDirect(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string,
  summary?: Record<string, string> 
) {
  try {
    const Admin: any = {
      MerchantID: merchantId,
      POSPackageID: POS_PACKAGE_ID,
      SecureDevice: 'CloudEMV2',
      OperatorID: operatorId,
      UserTrace: 'CAME-Trace',
      TranType: 'Administrative',
      TranCode: 'BatchClose',
      SequenceNo: '0010010010',
      TranDeviceID: tranDeviceId,
    };

    if (summary) {
      // required core fields
      if (summary.TerminalID) Admin.TerminalID = summary.TerminalID;
      Admin.BatchNo = summary.BatchNo;
      Admin.BatchItemCount = summary.BatchItemCount;
      Admin.NetBatchTotal = summary.NetBatchTotal;

      // echo **everything else** we got back (e.g., Credit/Debit/Gift/EBT …Count/…Amount)
      Object.entries(summary).forEach(([k, v]) => {
        if (!['TerminalID', 'BatchNo', 'BatchItemCount', 'NetBatchTotal'].includes(k) && v !== undefined) {
          Admin[k] = v;
        }
      });
    }

    const builder = new xml2js.Builder({ headless: false, renderOpts: { pretty: false } });
    const tstreamXml = builder.buildObject({ TStream: { Admin } });

    const response = await fetch(`http://${ipEmv}:8080/ProcessEMVTransaction/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/xml' },
      body: tstreamXml,
    });

    const rstreamRaw = await response.text();
    const parsed = await parserXml.parseStringPromise(rstreamRaw);
    const RStream = parsed?.RStream ?? parsed;

    logEmvBlock('BatchClose', tstreamXml, rstreamRaw, {
      CmdStatus: RStream?.CmdStatus,
      DSIXReturnCode: RStream?.DSIXReturnCode,
      BatchNo: RStream?.BatchNo,
      TextResponse: RStream?.TextResponse,
    });

    return {
      Timestamp: new Date().toISOString(),
      Url: `http://${ipEmv}:8080/ProcessEMVTransaction/`,
      TStream: tstreamXml,
      RStreamRaw: rstreamRaw,
      RStream,
    };
  } catch (e) {
    console.error('BatchCloseDirect Error:', e);
    return 'ERROR';
  }
}

async function datacapBatchSummaryAndClose(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string,
  terminalId?: string
) {
  // 1) Summary
  const sumResp = await datacapBatchSummary(merchantId, operatorId, tranDeviceId, ipEmv, terminalId);
  if (sumResp === 'ERROR') return { stage: 'summary', error: 'ERROR' };

  const sumCmd = sumResp?.RStream?.CmdResponse ?? sumResp?.RStream;
  if (sumCmd?.CmdStatus !== 'Success') {
    return {
      stage: 'summary',
      error: 'FAILED',
      DSIXReturnCode: sumCmd?.DSIXReturnCode,
      TextResponse: sumCmd?.TextResponse,
      RStream: sumResp?.RStream,
    };
  }

  // Use normalized totals (includes zeros + all *Count/*Amount)
  const summary = sumResp?.normalized ?? normalizeBatchSummary(sumResp?.RStream);

  // 2) Close
  const closeResp = await datacapBatchCloseDirect(merchantId, operatorId, tranDeviceId, ipEmv, summary);
  if (closeResp === 'ERROR') return { stage: 'close', error: 'ERROR', summary };

  const closeCmd = closeResp?.RStream?.CmdResponse ?? closeResp?.RStream;
  const ok = closeCmd?.CmdStatus === 'Success';

  return {
    stage: 'done',
    ok,
    summary,
    summaryResp: sumResp,
    closeResp,
    DSIXReturnCode: closeCmd?.DSIXReturnCode,
    TextResponse: closeCmd?.TextResponse,
    BatchNo: closeResp?.RStream?.BatchNo ?? summary?.BatchNo ?? '',
  };
}

async function datacapProcessEMVTransaction(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string,
  amount: string,
  invoiceNo?: string,
  refNo?: string
) {
  try {
    const inv = invoiceNo ?? makeInvoiceNo(); 
    const ref = refNo ?? inv;  
    const obj = {
      TStream: {
        Admin: {
          MerchantID: merchantId,
          OperatorID: operatorId,
          POSPackageID: POS_PACKAGE_ID,
          TranCode: 'EMVSale',
          InvoiceNo: inv,
          RefNo: ref,
          SequenceNo: '0010010010',
          UserTrace: 'CAME-Trace',
          TranDeviceID: tranDeviceId,
          OKAmount: 'Disallow',
          Amount: { Purchase: amount },
        },
      },
    };

    const builder = new xml2js.Builder({ headless: false, renderOpts: { pretty: false } });
    const tstreamXml = builder.buildObject(obj);

    const response = await fetch(`http://${ipEmv}:8080/ProcessEMVTransaction/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/xml' },
      body: tstreamXml,
    });

    const rstreamRaw = await response.text();
    const parsed = await parserXml.parseStringPromise(rstreamRaw);
    const RStream = parsed?.RStream ?? parsed;

    // Concise summary (pull common fields safely)
    const summary = {
      CmdStatus: RStream?.CmdStatus,
      DSIXReturnCode: RStream?.DSIXReturnCode,
      AuthCode: RStream?.AuthCode,
      InvoiceNo: RStream?.InvoiceNo ?? inv,
      Purchase: RStream?.Purchase,
      Authorize: RStream?.Authorize,
      CardType: RStream?.CardType,
      EntryMethod: RStream?.EntryMethod,
      RefNo: RStream?.RefNo ?? ref,
      };

    // One clean log for the transaction
    logEmvBlock("EMVSale", tstreamXml, rstreamRaw, summary);

    const payload = {
      Timestamp: new Date().toISOString(),
      Url: `http://${ipEmv}:8080/ProcessEMVTransaction/`,
      TStream: tstreamXml,
      RStreamRaw: rstreamRaw,
      RStream,
    };

    return payload;
  } catch (e) {
    console.error('EMV Transaction Error:', e);
    return 'ERROR';
  }
}





async function datacapEMVPadReset(
  merchantId: string,
  operatorId: string,
  tranDeviceId: string,
  ipEmv: string
) {
  try {
    console.log('---- EMVPadReset ----');
    const obj = {
      TStream: {
        Admin: {
          MerchantID: merchantId,
          OperatorID: operatorId,
          POSPackageID: POS_PACKAGE_ID,
          SecureDevice: 'CloudEMV2',
          UserTrace: 'dev1',
          TranCode: 'EMVPadReset',
          SequenceNo: '0010010010',
          TranDeviceID: tranDeviceId,
        },
      },
    };

    const builder = new xml2js.Builder({ headless: false, renderOpts: { pretty: false } });
    const tstreamXml = builder.buildObject(obj);

    const response = await fetch(`http://${ipEmv}:8080/EMVPadReset/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/xml' },
      body: tstreamXml,
    });

    const rstreamRaw = await response.text();
    const parsed = await parserXml.parseStringPromise(rstreamRaw);

    const payload = {
      Timestamp: new Date().toISOString(),
      Url: `http://${ipEmv}:8080/EMVPadReset/`,
      TStream: tstreamXml,
      RStreamRaw: rstreamRaw,
      RStream: parsed?.RStream ?? parsed,
    };

    console.log('[EMVPadReset]', {
      CmdStatus: payload.RStream?.CmdStatus,
      DSIXReturnCode: payload.RStream?.DSIXReturnCode,
    });

    return payload;
  } catch (e) {
    console.log({ e });
    return 'ERROR';
  }
}



// getXMLfromURL();

const debug = false;
export default class AppUpdater {
  constructor() {
    log.transports.file.level = 'info';
    autoUpdater.logger = log;
    autoUpdater.checkForUpdatesAndNotify();
  }
}

let mainWindow: BrowserWindow | null = null;
let workerWindow: BrowserWindow | null = null;
let workerWindowNetwork: BrowserWindow | null = null;

const calculateXORChecksumBody = (array: Buffer) => {
  const total =
    array[0] ^
    array[1] ^
    array[2] ^
    array[3] ^
    array[4] ^
    array[5] ^
    array[6] ^
    array[7] ^
    array[8] ^
    array[9] ^
    array[10] ^
    array[11] ^
    array[12] ^
    array[13] ^
    array[14] ^
    array[15] ^
    array[16] ^
    array[17] ^
    array[18] ^
    array[19] ^
    array[20] ^
    array[21];
  return total.toString(16);
};

const getMessageToSendToPanel = (num: number) => {
  const header = 'aabb0000ffff001515';
  const prefix = '0001030003000000';
  const sufix = '0007e80300091f';
  let message = `204820482048`;
  if (num >= 100 && num <= 999) {
    message = `3${String(num)[0]}203${String(num)[1]}203${String(num)[2]}20`;
  } else if (num >= 10 && num <= 99) {
    message = `20203${String(num)[0]}203${String(num)[1]}20`;
  } else if (num >= 0 && num <= 9) {
    message = `202020203${String(num)[0]}20`;
  }
  const assambledMessageBeforeCheck = `${prefix}${message}${sufix}`;
  const hexValBuffer = Buffer.from(assambledMessageBeforeCheck, 'hex');
  const checkSumMessage = calculateXORChecksumBody(hexValBuffer);
  const finalMessage = `${header}${prefix}${message}${sufix}${checkSumMessage}`;
  const hexValBufferFinalMessage = Buffer.from(finalMessage, 'hex');
  return hexValBufferFinalMessage;
};

let schedulerJob: cron.ScheduledTask;

app.disableHardwareAcceleration();

// Below if needed: npm install --save cors
// expressApp.use(cors());
const expressApp = express();
const port = 3000; // 0 for a free port

try {
  // Configuring body parser middleware
  // expressApp.use(bodyParser.urlencoded({ extended: false }));
  expressApp.use(express.json({ limit: '100mb' }));

  // expressApp.use(express.json());

  expressApp.post('/setbackup', (req, res) => {
    mainWindow?.webContents.send('API-setBackup', req.body);
    console.log(req.body);
    res.send(req.body);
  });

  expressApp.post('/plate', (req, res) => {
    mainWindow?.webContents.send('API-new-plate', req.body);
    console.log('req');
    console.log(req.body);
    res.send(req.body);
  });

  expressApp.get('/getbackup', (req, res) => {
    console.log('*************** getbackup called');
    mainWindow?.webContents.send('getBackup', '');
    ipcMain.once('getBackup-response', (_event, args) => {
      console.log('Returning the BU', args);
      res.json(args);
    });
  });

  expressApp.listen(port, () =>
    console.log(`CAME access app listening on port ${port}!`)
  );
} catch (e) {
  console.log('Got an error when setting up express server, ', e);
}

// const nfc = new NFC(); // optionally you can pass logger

const store = new Store({
  configName: 'acs01config',
  defaults: {
    autoStart: false,
    autoUpdate: false,
    fullscreen: false,
    silentPrint: true,
    hideCursor: false,
    serialPath: 'No ports detected',
    schedulerEnabled: false,
    schedulerConfig: 0,
    pathImage: '',
    localUrl: '',
    cookie: '',
    defaultReaderId: '',
    defaultPrinterId: '',
    username: '',
    password: '',
  },
});

const serialPath = store.get('serialPath');
let autoStart = store.get('autoStart');
let autoUpdate = store.get('autoUpdate');
let fullscreen = store.get('fullscreen');
let silentPrint = store.get('silentPrint');
let hideCursor = store.get('hideCursor');
let schedulerEnabled = store.get('schedulerEnabled');
let schedulerConfig = store.get('schedulerConfig');
let updateReady = false;
let pathImage = store.get('pathImage');
let totalClose = false; // To know when we close the app or when we close the workers from JS

let serialPort: SerialPort<AutoDetectTypes>;

let parser;

let autoLauncherConfig = { name: 'acs01-electron-updater', isHidden: true };
if (process.env.APPIMAGE) {
  autoLauncherConfig = Object.assign(autoLauncherConfig, {
    path: process.env.APPIMAGE,
  });
}

const applicationAutoLauncher = new AutoLaunch(autoLauncherConfig);

if (autoStart) {
  // Check if autoStart is true, if true application wil start on startup OS
  applicationAutoLauncher
    .isEnabled()
    .then((isEnabled: any) => {
      if (isEnabled) {
        return;
      }
      applicationAutoLauncher.enable();
    })
    .catch((_err: any) => {
      // handle error
    });
} else {
  applicationAutoLauncher
    .isEnabled()
    .then((isEnabled: any) => {
      if (isEnabled) {
        return;
      }
      applicationAutoLauncher.disable();
    })
    .catch((_err: any) => {
      // handle error
    });
}
autoUpdater.setFeedURL({
  provider: 'github',
  repo: 'CAME-Access',
  owner: 'CAME-Benelux',
  private: true,
  token: '',
  // process.env.GH_TOKEN instead of actual token?
});

const updaterChecker = () => {
  log.info("Updater check disabled.");
};

if (process.env.NODE_ENV === 'production') {
  const sourceMapSupport = require('source-map-support');
  sourceMapSupport.install();
}

const isDevelopment =
  process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';

if (isDevelopment) {
  require('electron-debug')();
}

const installExtensions = async () => {
  const installer = require('electron-devtools-installer');
  const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
  const extensions = ['REACT_DEVELOPER_TOOLS'];

  return installer
    .default(
      extensions.map((name) => installer[name]),
      forceDownload
    )
    .catch(console.log);
};

const createWorkerWindowPrint = async () => {
  workerWindow = new BrowserWindow({
    show: false,
    backgroundColor: '#2e2c29',
    webPreferences: {
      nodeIntegration: true, // disable NodeJS functionality in the renderers
      backgroundThrottling: false,
      contextIsolation: false,
    },
  });

  workerWindow.loadURL(resolveHtmlPath('worker.html'));

  workerWindow.on('ready-to-show', () => {
    if (!workerWindow) {
      throw new Error('"workerWindow" is not defined');
    }
    if (process.env.START_MINIMIZED) {
      workerWindow.minimize();
    } else {
      workerWindow.webContents.send('setToPrintUI', true);
      if (debug) workerWindow.show();
    }
  });

  // workerWindow.webContents.toggleDevTools();

  workerWindow.on('closed', () => {
    if (!totalClose) {
      createWorkerWindowPrint();
    }
  });
};

const createWorkerWindowNetwork = async () => {
  workerWindowNetwork = new BrowserWindow({
    show: false,
    backgroundColor: '#2e2c29',
    webPreferences: {
      nodeIntegration: true, // disable NodeJS functionality in the renderers
      backgroundThrottling: false,
      contextIsolation: false,
    },
  });

  // Set correct path for the worker, its different when packaged: https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/3013
  const prodPath = path.join(__dirname, '../../../release/app/workers');
  const prodWorkerHtml = path.join(prodPath, 'workerNetwork.html');
  const workerHtml = app.isPackaged
    ? prodWorkerHtml // We are in production with a packaged app
    : path.join(webpackPaths.appPath, 'workers/workerNetwork.html'); // We are in dev

  workerWindowNetwork.loadFile(workerHtml);

  workerWindowNetwork.on('ready-to-show', () => {
    if (!workerWindowNetwork) {
      throw new Error('"workerWindowNetwork" is not defined');
    }
    if (process.env.START_MINIMIZED) {
      workerWindowNetwork.minimize();
    } else {
      if (debug) workerWindowNetwork.show();
      mainWindow?.webContents.send('workerNetworkId', workerWindowNetwork?.id);
      workerWindowNetwork.webContents.send('mainWindowId', mainWindow?.id);
    }
  });

  // workerWindowNetwork.webContents.toggleDevTools();

  workerWindowNetwork.on('closed', () => {
    if (!totalClose) {
      createWorkerWindowNetwork();
    }
  });
};

const createWindow = async () => {
  if (
    process.env.NODE_ENV === 'development' ||
    process.env.DEBUG_PROD === 'true'
  ) {
    await installExtensions();
  }

  const RESOURCES_PATH = app.isPackaged
    ? path.join(process.resourcesPath, 'assets')
    : path.join(__dirname, '../../assets');

  const getAssetPath = (...paths: string[]): string => {
    return path.join(RESOURCES_PATH, ...paths);
  };

  mainWindow = new BrowserWindow({
    show: false,
    width: 1280,
    height: 720,
    backgroundColor: '#95A5A6',
    frame: !fullscreen,
    icon: getAssetPath('icon.png'),
    webPreferences: {
      nodeIntegration: true, // disable NodeJS functionality in the renderers
      backgroundThrottling: false,
      contextIsolation: false,
    },
  });

  const loadFile = app.isPackaged
    ? mainWindow.loadURL(
        url.format({
          pathname: path.join(__dirname, '..', '/renderer/', 'index.html'),
          protocol: 'file:',
          slashes: true,
        })
      )
    : mainWindow.loadURL(resolveHtmlPath('index.html'));

  console.log(
    'THE PATH: ',
    url.format({
      pathname: path.join(__dirname, '..', '/renderer/', 'index.html'),
      protocol: 'file:',
      slashes: true,
    })
  );

  // mainWindow.loadURL(resolveHtmlPath('index.html'));

  mainWindow.on('ready-to-show', () => {
    if (!mainWindow) {
      throw new Error('"mainWindow" is not defined');
    }
    mainWindow.webContents.send('locale', app.getLocale());

    if (process.env.START_MINIMIZED) {
      mainWindow.minimize();
    } else {
      mainWindow.show();
      mainWindow.maximize();
      mainWindow.focus();
      if (fullscreen) {
        mainWindow.setFullScreen(true);
      }
      if (pathImage) mainWindow.webContents.send('thePath', pathImage);

      
    }
  });

  mainWindow.webContents.on('dom-ready', (_event) => {
    const css = '* { cursor: none !important;   -webkit-user-select: none;}';
    if (mainWindow && hideCursor === true) {
      mainWindow.webContents.insertCSS(css);
    }
  });

  if (workerWindow && workerWindowNetwork) {
    const menuBuilder = new MenuBuilder(mainWindow);
    menuBuilder.buildMenu();
  }

  // Open urls in the user's browser
  mainWindow.webContents.on('new-window', (event, url) => {
    event.preventDefault();
    shell.openExternal(url);
  });

  mainWindow.on('close', () => {
    totalClose = true;
    workerWindow = null;
    workerWindowNetwork = null;
    mainWindow = null;
    app.exit();
  });

  mainWindow.on('closed', () => {
    if (!totalClose) {
      createWindow();
    }
  });
  const menuBuilder = new MenuBuilder(mainWindow);
  menuBuilder.buildMenu();

  // Open urls in the user's browser
  mainWindow.webContents.setWindowOpenHandler((edata) => {
    shell.openExternal(edata.url);
    return { action: 'deny' };
  });

  // Remove this if your app does not use auto updates
  // eslint-disable-next-line
 // new AppUpdater();
};

/**
 * Helper flows for serialport, scheduler. Should be cleaned
 */

// Serial port
if (serialPath !== 'No ports detected' && serialPath) {
  serialPort = new SerialPort({ path: serialPath, baudRate: 115200 });
  parser = serialPort?.pipe(new ReadlineParser({ delimiter: '\r' }));
  // Serial port on runtime
  serialPort?.on('error', (err: { message: any }) => {
    console.log('Error Serialport: ', err.message);
  });
  // Send the code from serial to renderer
  parser.on('data', (event: string) => {
    // const arr = event.slice(event.indexOf("["));
    console.log(event);
    // console.log(arr);

    if (mainWindow)
      mainWindow.webContents.send('main->mainWindow-serialMessage', event);
  });
}

// Scheduler (for counter reset)
if (schedulerEnabled) {
  if (schedulerConfig === 0) {
    schedulerJob = cron.schedule('00 22 * * *', () => {
      console.log('SCHEDULER ***** a task every day at 22:00');
      if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
    });
  } else if (schedulerConfig === 1) {
    schedulerJob = cron.schedule('* * * * *', () => {
      console.log('SCHEDULER ***** a task every minute');
      if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
    });
  } else if (schedulerConfig === 2) {
    schedulerJob = cron.schedule('*/10 * * * *', () => {
      console.log('SCHEDULER ***** a task every 10 minute');
      if (mainWindow) mainWindow.webContents.send('schedulerTrigger', false);
    });
  }
}
/**
 * Helper functions for LED-panel interface and serialport access
 */

// Below is random number, so that we can buffer the message and never send same message to panel twice as this could trigger an error on the panel, we use an array because there can be more the one LED-panel (ip)
const bufferdObjArr = [{ ip: '0.0.0.0', message: -2313 }];

const sendToLedPanel = async (ipLedPanel: string, message: number) => {
  // We search the bufferd object in the array
  const bufferdObj = bufferdObjArr.find((x) => x.ip === ipLedPanel);

  const compObj = { ip: ipLedPanel, message };

  if (compObj === bufferdObj) {
    console.log('same message to same panel', compObj);
    return;
  }
  try {
    const client = new net.Socket();

    client.connect(
      {
        host: ipLedPanel,
        port: 9100,
      },
      function () {
        console.log(`connected to ${ipLedPanel}:${9100}`);
        client.write(
          getMessageToSendToPanel(message),
          function callBackWhenOk() {
            // Below is to get the position of the bufferd message
            const elementPos = bufferdObjArr
              .map(function getId(a: any) {
                return a.ip;
              })
              .indexOf(ipLedPanel);
            console.log('elementPos', elementPos);

            if (elementPos !== -1) {
              bufferdObjArr[elementPos].ip = ipLedPanel;
              bufferdObjArr[elementPos].message = message;
            } else {
              bufferdObjArr.push({ ip: ipLedPanel, message });
            }

            console.log('message send, buffer: ', bufferdObjArr);
            client.destroy();
          }
        );
      }
    );

    client.on('data', function (data) {
      const str = data.toString();
    });

    client.on('error', function (err) {
      console.log('Error : ', err);
    });

    client.on('close', function () {
      console.log('socket closed');
    });
  } catch (e) {
    console.log('catched error: ', e);
  }
};

// To send the path of the custom logo to the mainWindow
const readFile = (filepath: any) => {
  pathImage = filepath;
  if (mainWindow && filepath) {
    mainWindow.webContents.send('thePath', filepath);
    store.set('pathImage', filepath);
  }
};

// To relay update information to the mainWindow
const send = async (message: string, type: string) => {
  if (!mainWindow) {
    return;
  }
  const arrToSend = [message, type];
  mainWindow.webContents.send('messageUpdate', arrToSend);
};

ipcMain.handle('acs01-login', async (_event, { ip, username, password }) => {
  try {
    const ipOrHost = String(ip || '')
      .trim()
      .replace(/^https?:\/\//, '')
      .replace(/\/$/, '');

    if (!ipOrHost) return { ok: false, error: 'Missing IP address' };

    const res: any = await fetch(`http://${ipOrHost}/apiv1/auth/login`, {
      method: 'POST',
      headers: {
        accept: '*/*',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ username, password }),
    });

    if (res.status !== 200) {
      const text = await res.text().catch(() => '');
      return { ok: false, error: text || `HTTP ${res.status}` };
    }

    const json = await res.json().catch(() => null);
    if (!json || typeof json.role !== 'number') {
      return { ok: false, error: 'Unexpected response (missing role)' };
    }

    return { ok: true, role: json.role };
  } catch (e: any) {
    return { ok: false, error: String(e?.message || e) };
  }
});

/**
 * Serialport event listeners...
 */
app.on('ready', () => {
  ipcMain.handle('serialList', async (event, ...arg) => {
    const validSerialPorts: any[] | PromiseLike<any[]> = [];
    const result = await SerialPort?.list()
      .then((ports) => {
        for (let i = 0; i < ports.length; i += 1) {
          if (typeof ports[i].manufacturer !== 'undefined') {
            validSerialPorts.push(ports[i]);
          }
        }
        return validSerialPorts;
      })
      .catch((err) => console.log(err));
    return result;
  });

  ipcMain.on('mainWindow->main-arm/disArm-scanner', (_event, args) => {
    store.set('serialPath', args[0]);
    // Arm
    const arm = Buffer.from([
      0x16, 0x4d, 0x0d, 0x53, 0x43, 0x4e, 0x4c, 0x45, 0x44, 0x31, 0x2e,
    ]);
    // DisArm
    const disArm = Buffer.from([
      0x16, 0x4d, 0x0d, 0x53, 0x43, 0x4e, 0x4c, 0x45, 0x44, 0x30, 0x2e,
    ]);
    if (args[1] === true) {
      return serialPort?.write(arm, function (err, result) {
        if (err) {
          console.log(`Error while sending message : ${err}`);
        }
        if (result) {
          console.log(`Response received after sending message : ${result}`);
        }
      });
    }
    return serialPort?.write(disArm, function (err, result) {
      if (err) {
        console.log(`Error while sending message : ${err}`);
      }
      if (result) {
        console.log(`Response received after sending message : ${result}`);
      }
    });
  });

  /**
   * Other event listeners...
   */

  // Below seems necessary since we cannot send directly between renderers when the window has been closed once :-(
  ipcMain.on(
    'mainWindow->main->workerWindowNetwork-printTicket',
    (_event, args) => {
      workerWindowNetwork?.webContents.send(
        'mainWindow->workerWindowNetwork-printTicket',
        args
      );
    }
  );

  ipcMain.on(
    'mainWindow->main->workerWindowNetwork-ticketById',
    (_event, args) => {
      workerWindowNetwork?.webContents.send(
        'mainWindow->workerWindowNetwork-ticketById',
        args
      );
    }
  );

  // Get the MAC from default network adapter, this is used to create a unique id for the propogation of the application trough mDNS
  ipcMain.handle('workerWindow->main-getmac', async (_event, _someArgument) => {
    let mac = '';
    try {
      mac = getMAC().toString();
    } catch (err) {
      return console.log(err);
    }
    return mac;
  });

  // Download parameters
  ipcMain.handle(
    'datacap-emvParamDownload',
    async (_event, { merchantId, operatorId, tranDeviceId, ipEmv }) => {
      try {
        return await datacapEMVParamDownload(merchantId, operatorId, tranDeviceId, ipEmv);
      } catch {
        return 'ERROR';
      }
    }
  );
      ipcMain.handle(
        'datacap-batchSummary',
        async (_event, { merchantId, operatorId, tranDeviceId, ipEmv, terminalId }) => {
          try {
            return await datacapBatchSummary(merchantId, operatorId, tranDeviceId, ipEmv, terminalId);
          } catch {
            return 'ERROR';
          }
        }
      );

    // --- IPC: Batch Close (direct) ---
    ipcMain.handle(
      'datacap-batchCloseDirect',
      async (_event, { merchantId, operatorId, tranDeviceId, ipEmv, summary }) => {
        try {
          return await datacapBatchCloseDirect(merchantId, operatorId, tranDeviceId, ipEmv, summary);
        } catch (e) {
          console.error('IPC datacap-batchCloseDirect error:', e);
          return 'ERROR';
        }
      }
    );

    ipcMain.handle(
      'datacap-batchSummaryAndClose',
      async (_event, { merchantId, operatorId, tranDeviceId, ipEmv, terminalId }) => {
        try {
          return await datacapBatchSummaryAndClose(merchantId, operatorId, tranDeviceId, ipEmv, terminalId);
        } catch (e) {
          console.error('IPC datacap-batchSummaryAndClose error:', e);
          return 'ERROR';
        }
      }
    );
    

  // Send amount to datacap EMV
  ipcMain.handle(
    'datacap-sendAmount',
    async (_event, { merchantId, operatorId, tranDeviceId, ipEmv, amount, invoiceNo, refNo }) => {
      try {
        const resp = await datacapProcessEMVTransaction(
          merchantId,
          operatorId,
          tranDeviceId,
          ipEmv,
          amount,
          invoiceNo,
          refNo
        );
        return resp;
      } catch (err) {
        return 'ERROR';
      }
    }
  );

  ipcMain.handle(
    'datacap-resetEmv',
    async (_event, { merchantId, operatorId, tranDeviceId, ipEmv }) => {
      try {
        const respons = await datacapEMVPadReset(
          merchantId,
          operatorId,
          tranDeviceId,
          ipEmv
        );
        return respons;
      } catch (err) {
        return 'ERROR';
      }
    }
  );

  ipcMain.handle('getCounters', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/counter/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('✔ counters:', json);
        return json;
      })
      .catch((error) => {
        console.error('✖ error fetching counters:', error);
        return error;
      });
  
    return fetchData;
  });


  // Send counter data to a LED-panel, based on its IP.
  // Currently only implemented QLight MLN protocol for 3 digits and 3 times X
  ipcMain.handle(
    'mainWindow->main-sendCounterDataForPanel',
    async (_event, args1, args2) => {
      const counterGroups = args1;
      const wsMessage = args2;
      store.set('counterGroups', counterGroups);
      console.log('counterGroups ', counterGroups);
      // Run trough countersGroups
      for (let x = 0; x < counterGroups.length; x += 1) {
        // Run trough countersGroups, counters
        for (let y = 0; y < counterGroups[x].counters.length; y += 1) {
          console.log(
            `1. send Match for GROUP ${counterGroups[x].name}: name: ${wsMessage.name}, value: ${wsMessage.value}`
          );
          if (counterGroups[x].counters[y].counterId === wsMessage.id) {
            // Run trough it again to be sure to start from 0 to end
            let total = 0;
            let totalZones = counterGroups[x].maxSize;
            let notFirstCounter = false;

            for (let z = 0; z < counterGroups[x].counters.length; z += 1) {
              if (z === 0) {
                if (y === 0) {
                  total = wsMessage.value;
                  console.log('total init wsMessage.value ', total);
                } else {
                  notFirstCounter = true;
                  total = counterGroups[x].counters[0].value;
                  console.log(
                    'total init counterGroups[x].counters[0].value ',
                    total
                  );
                }
              } else if (z !== 0) {
                const prevZ = z - 1;
                if (counterGroups[x].counters[prevZ].operator === '+') {
                  console.log('now plus ', counterGroups[x].counters[z]?.value);
                  if (notFirstCounter) {
                    if (wsMessage.value) total += wsMessage.value;
                    console.log('total plus ', wsMessage.value);
                  } else {
                    if (counterGroups[x].counters[z].value)
                      total += counterGroups[x].counters[z]?.value;
                    console.log('total plus ', total);
                  }
                }
                if (counterGroups[x].counters[prevZ].operator === '-') {
                  console.log('now min ', counterGroups[x].counters[z]?.value);

                  if (notFirstCounter) {
                    if (wsMessage.value !== 0) {
                      console.log('NOW not 0', total);
                    } else if (wsMessage.value && wsMessage.value > 0) {
                      total -= wsMessage.value;
                    } else if (total < 0) {
                      total = 0;
                      console.log('total min corrected ', total);
                    }

                    if (total < 0) {
                      total = 0;
                      console.log('total min corrected ', total);
                    }
                    console.log('total min1 ', wsMessage.value);
                  } else {
                    if (counterGroups[x].counters[z].value) {
                      // total -= counterGroups[x].counters[z]?.value;
                      if (total < 0) {
                        total = 0;
                        console.log('total min corrected ', total);
                      }
                    }
                    console.log('total min2 ', total);
                  }
                }
              }
            }
            console.log(
              `2. send Match for GROUP ${counterGroups[x].counters[y].name}: name: ${wsMessage.name}, value: ${wsMessage.value}`
            );
            console.log('total send ', total);

            totalZones -= total;
            console.log('totalZones send ', totalZones);

            if (counterGroups[x].zeroBecomesXXX && totalZones === 0) {
              totalZones = -1;
            }

            sendToLedPanel(counterGroups[x].ip, totalZones);
          }
        }
      }
    }
  );

  ipcMain.handle('mainWindow->main-sendToLedPanel', async (_event, args) => {
    const ipLedPanel = args[0];
    const message: number = args[1];
    const zeroBecomesXXX: boolean = args[2];
    console.log('MESSAGE ', message);

    try {
      const client = new net.Socket();

      client.connect(
        {
          host: ipLedPanel,
          port: 9100,
        },
        function writeToPanel() {
          /*
          if (zeroBecomesXXX === true) {
            if (message === 0) {
              message = -1;
            }
          }
          */
          client.write(
            getMessageToSendToPanel(message),
            function callBackWhenOk() {
              console.log('move forward command sent ', message);
              client.destroy();
            }
          );
        }
      );

      client.on('data', function (data) {
        const str = data.toString();
      });

      client.on('error', function (err) {
        console.log('Error : ', err);
      });

      client.on('close', function () {
        console.log('socket closed');
      });
    } catch (e) {
      console.log('catched error: ', e);
    }
  });

  // Send an array of tickets to the workerwindow to print tickets in this window
  ipcMain.on('mainSendToPrint', (_event, args) => {
    console.log('Received receipt data for printing:', args);
    // Here we get the array of tickets to print
    const receivedArgs = args;
    if (workerWindow) {
      workerWindow.webContents.send('workerSendToPrint', receivedArgs);
    }
  });

  // When the worker receives the array it'll send a message to indicate it's done
  ipcMain.on('workerDoneLoading', (_event, _args) => {
    console.log('in workerDoneLoading');
    // eslint-disable-next-line
    if (!workerWindow) {
      return;
    }
    if (silentPrint !== true) {
      workerWindow.webContents.print(
        {
          silent: false,
          margins: {
            marginType: 'custom',
            top: 0,
            bottom: 10,
            left: 0,
            right: 0,
          },
          pagesPerSheet: 1,
        },
        (success, _failureReason) => {
          // Signal that the print is finished
          if (!workerWindow) {
            return;
          }
          if (success)
            workerWindow.webContents.send('workerDoneLoadingConfirm', 'ok');
          else workerWindow.webContents.send('workerDoneLoadingConfirm', 'nok');
        }
      );
    } else {
      workerWindow.webContents.print(
        {
          silent: true,
          margins: {
            marginType: 'custom',
            top: 0,
            bottom: 10,
            left: 0,
            right: 0,
          },
          pagesPerSheet: 1,
        },
        (success, _failureReason) => {
          // Signal that the print is finished
          if (!workerWindow) {
            return;
          }
          if (success)
            workerWindow.webContents.send('workerDoneLoadingConfirm', 'ok');
          else workerWindow.webContents.send('workerDoneLoadingConfirm', 'nok');
        }
      );
    }
  });

  ipcMain.on(
    'mainWindow->main->workerWindowNetwork-openPassage',
    (_event, args) => {
      workerWindowNetwork?.webContents.send(
        'mainWindow->main->workerWindowNetwork-openPassage',
        args
      );
    }
  );
  ipcMain.on(
    'mainWindow->main->workerWindowNetwork-controlPassage',
    (_event, args) => {
      workerWindowNetwork?.webContents.send(
        'mainWindow->main->workerWindowNetwork-controlPassage',
        args
      );
    }
  );
  ipcMain.on(
    'mainWindow->main->workerWindowNetwork-changeOnlineTicketState',
    (_event, args) => {
      workerWindowNetwork?.webContents.send(
        'mainWindow->main->workerWindowNetwork-changeOnlineTicketState',
        args
      );
    }
  );
  // Below is to save to desktop
  ipcMain.on('save-backup', (_event, args) => {
    const userDataPath = app.getPath('desktop');
    // We'll use the `configName` property to set the file name and path.join to bring it all together as a string 
    const pathName = path.join(userDataPath, `backup.json`, 'utf8');

    if (!mainWindow) return;
    try {
      fs.writeFileSync(pathName, args);
    } catch (e) {
      console.log(e);
    }
  });
  // Below is to save to desktop
  ipcMain.on('restore-backup', (_event, args) => {
    if (!mainWindow) return;

    dialog
      .showOpenDialog(mainWindow, {
        properties: ['openFile'],
      })
      .then((result: any) => {
        try {
          const resultRead = fs.readFileSync(result.filePaths[0], 'utf8');
          mainWindow?.webContents.send(
            'restore-backup-payload',
            JSON.parse(resultRead)
          );
        } catch (error) {
          // if there was some kind of error, return the passed in defaults instead.
          console.log('error restoring bu');
        }
      })
      .catch((_err: any) => {
        console.log('no file selected');
      });
  });
  // Below is workaroud because ipcRenderer.sendTo() doesnt work corectly yet (like a relay from worker to mainWindow)
  ipcMain.on('interWindowRelay', (_event, args) => {
    // in the first block we find the event name [0] then the data [1]
    mainWindow?.webContents.send(args[0], args[1]);
  });

  // Below is workaroud because ipcRenderer.sendTo() doesnt work corectly yet (like a relay from worker to mainWindow)
  ipcMain.on('closeWorkerNetwork', (_event, args) => {
    // in the first block we find the event name [0] then the data [1]
    return workerWindowNetwork?.close();
  });

  // Below is workaroud because ipcRenderer.sendTo() doesnt work corectly yet (like a relay from worker to mainWindow)
  ipcMain.on('closeWorkerQr', (_event, args) => {
    // in the first block we find the event name [0] then the data [1]
    return workerWindow?.close();
  });

  // Below we handle the IPC request for the initial data that gets send to the workerNetwork
  ipcMain.on('mainWindow->main-keystoneSync', (_event, args) => {
    // Save all the data persistent
    args?.map((keyValuePair: { key: string; value: string | undefined }) => {
      // console.log("°°°°°°°° Now syncing => ", keyValuePair);
      if (keyValuePair.key === 'localUrl') {
        if (keyValuePair.value !== store.get('localUrl')) {
          store.set(keyValuePair.key, keyValuePair.value);
          // console.log("°°°°°°°° Now closing the window ", keyValuePair);
          return workerWindowNetwork?.close();
        }
        return store.set(keyValuePair.key, keyValuePair.value);
      }
      return store.set(keyValuePair.key, keyValuePair.value);
    });
    // Now send it to the workerNetwork
    workerWindowNetwork?.webContents.send(
      'main->workerWindowNetwork-keystoneSync-updateInitData',
      [
        store.get('localUrl'),
        store.get('cookie'),
        store.get('username'),
        store.get('password'),
        store.get('defaultReaderId'),
        store.get('defaultPrinterId'),
        store.get('defaultScenarioConfigured'),
        store.get('defaultScenarioId'),
        store.get('applicationMode'),
        store.get('defaultPrinterBackupId'),
        store.get('defaultPrinterButtonId'),
      ]
    );
  });

  ipcMain.handle(
    'workerWindowNetwork->main-keystoneSync-getInitData',
    async (_event, _someArgument) => {
      return [
        store.get('localUrl'),
        store.get('cookie'),
        store.get('username'),
        store.get('password'),
        store.get('defaultReaderId'),
        store.get('defaultPrinterId'),
        store.get('defaultScenarioConfigured'),
        store.get('defaultScenarioId'),
        store.get('applicationMode'),
        store.get('defaultPrinterBackupId'),
        store.get('defaultPrinterButtonId'),
      ];
    }
  );

  // Below could be deleted if cheked
  ipcMain.handle('testLogin', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/auth/login`, {
      method: 'POST',
      body: JSON.stringify({
        username: store.get('username'),
        password: store.get('password'),
      }),
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('local', json);
        return json;
      })
      .catch((error) => {
        console.log('non local');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('loginLocal', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/auth/login`, {
      method: 'POST',
      body: JSON.stringify({
        username: store.get('username'),
        password: store.get('password'),
      }),
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('local', json);
        return json;
      })
      .catch((error) => {
        console.log('non local');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('passageById', async (_event, args) => {
    const url = args[0];
    const defaultPassageId = args[1];
    const fetchData: any = await fetch(
      `http://${url}/apiv1/passage/${defaultPassageId}`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('passage by id', json);
        return json;
      })
      .catch((error) => {
        console.log('passage by id');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('getReaders', async (_event, args) => {
    const url = args[0];
    const fetchData: any = await fetch(`http://${url}/apiv1/reader/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('readers', json);
        return json;
      })
      .catch((error) => {
        console.log('readers');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('readerById', async (_event, args) => {
    const url = args[0];
    const defaultReaderId = args[1];
    const fetchData: any = await fetch(
      `http://${url}/apiv1/reader/${defaultReaderId}`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('reader by id', json);
        return json;
      })
      .catch((error) => {
        console.log('reader by id');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('createHttpReader', async (_event, args) => {
    const url = args[0];
    const moduleid = args[1];
    const passage = args[2];
    const zone = args[3];
    console.log('** moduleid', moduleid);
    console.log('** passage', passage);
    console.log('** zone', zone);

    const fetchData: any = await fetch(`http://${url}/apiv1/reader/create`, {
      method: 'POST',
      useSessionCookies: true,
      body: JSON.stringify({
        address: '',
        arm_input: '00000000-0000-0000-0000-000000000000',
        baudrate: 115200,
        connection: 8,
        disarm_after_scan: false,
        moduleid,
        name: 'Generic https',
        passage,
        port: -1,
        scan_allowed_input: '00000000-0000-0000-0000-000000000000',
        type: 13,
        zone,
      }),
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('createHttpReader', json);
        return json;
      })
      .catch((error) => {
        console.log('createHttpReader');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('printerById', async (_event, args) => {
    const url = args[0];
    const defaultPrinterId = args[1];
    const fetchData: any = await fetch(
      `http://${url}/apiv1/printer/${defaultPrinterId}`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('printer by id', json);
        return json;
      })
      .catch((error) => {
        console.log('printer by id');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('scanCode', async (_event, args) => {
    const url = args[0];
    const codePin = args[1];
    const defaultReaderId = args[2];
    console.log('Now sending the url: ', url);
    console.log('Now sending the code: ', codePin);
    console.log('Now sending the defaultReaderId: ', defaultReaderId);

    const fetchData: any = await fetch(`http://${url}/apiv1/reader/scan`, {
      method: 'POST',
      useSessionCookies: true,
      body: JSON.stringify({
        code: codePin,
        id: defaultReaderId,
        type: 2,
      }),
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('scanCode', json);
        return json;
      })
      .catch((error) => {
        console.log('scanCode');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('auxById', async (_event, args) => {
    const url = args[0];
    const defaultLoopId = args[1];
    const fetchData: any = await fetch(
      `http://${url}/apiv1/io_connection/${defaultLoopId}`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('aux by id', json);
        return json;
      })
      .catch((error) => {
        console.log('aux by id');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('scenarioById', async (_event, args) => {
    const url = args[0];
    const defaultScenarioId = args[1];
    const fetchData: any = await fetch(
      `http://${url}/apiv1/scenario/${defaultScenarioId}`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('passage by id', json);
        return json;
      })
      .catch((error) => {
        console.log('passage by id');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('passages', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/passage/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('passages', json);
        return json;
      })
      .catch((error) => {
        console.log('passages');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('auxs', async (_event, url) => {
    const fetchData: any = await fetch(
      `http://${url}/apiv1/io_connection/all`,
      {
        method: 'GET',
        useSessionCookies: true,
      }
    )
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('auxs', json);
        return json;
      })
      .catch((error) => {
        console.log('auxs');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('printers', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/printer/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('printers', json);
        return json;
      })
      .catch((error) => {
        console.log('printers');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('modules', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/module/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('modules', json);
        return json;
      })
      .catch((error) => {
        console.log('modules');
        return error;
      });
    return fetchData;
  });

  ipcMain.handle('scenarios', async (_event, url) => {
    const fetchData: any = await fetch(`http://${url}/apiv1/scenario/all`, {
      method: 'GET',
      useSessionCookies: true,
    })
      .then((response: any) => {
        if (response.status !== 200) {
          return new Error(response.status);
        }
        return response.json();
      })
      .then((json: any) => {
        console.log('scenarios', json);
        return json;
      })
      .catch((error) => {
        console.log('scenarios');
        return error;
      });
    return fetchData;
  });

  ipcMain.on('setSchedulerEnabled', (_event, args) => {
    console.log('REC schedulerEnabled', args);
    store.set('schedulerEnabled', args);
    if (args) {
      if (schedulerConfig === 0) {
        schedulerJob = cron.schedule('00 22 * * *', () => {
          console.log('SCHEDULER ***** a task every day at 22:00');
          if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
        });
      } else if (schedulerConfig === 1) {
        schedulerJob = cron.schedule('* * * * *', () => {
          console.log('SCHEDULER ***** a task every minute');
          if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
        });
      } else if (schedulerConfig === 2) {
        schedulerJob = cron.schedule('*/10 * * * *', () => {
          console.log('SCHEDULER ***** a task every 10 minute');
          if (mainWindow)
            mainWindow.webContents.send('schedulerTrigger', false);
        });
      }
    }
    schedulerEnabled = args;
  });

  ipcMain.on('setSchedulerConfig', (_event, args) => {
    store.set('schedulerConfig', args);
    schedulerConfig = args;
    if (schedulerConfig === 0) {
      schedulerJob = cron.schedule('00 22 * * *', () => {
        console.log('SCHEDULER ***** a task every day at 22:00');
        if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
      });
    } else if (schedulerConfig === 1) {
      schedulerJob = cron.schedule('* * * * *', () => {
        console.log('SCHEDULER ***** a task every minute');
        if (mainWindow) mainWindow.webContents.send('schedulerTrigger', true);
      });
    } else if (schedulerConfig === 2) {
      schedulerJob = cron.schedule('*/10 * * * *', () => {
        console.log('SCHEDULER ***** a task every 10 minute');
        if (mainWindow) mainWindow.webContents.send('schedulerTrigger', false);
      });
    }
  });

  // Below could be deleted if cheked
  ipcMain.handle('getCookieForWorker', async (_event, _someArgument) => {
    let cookieGot: Cookie;
    const sessionWorker = workerWindowNetwork?.webContents.session;
    const result = sessionWorker?.cookies
      .get({ name: 'acs_token' })
      .then((cookies) => {
        // event.reply("getCookie", cookies[0]);
        [cookieGot] = cookies;

        store.set('cookie', cookieGot.value);
        cookieGot.sameSite = 'no_restriction';

        return cookieGot.value;
      })
      .catch((_error) => {
        return 'error';
      });
    return result;
  });

  // Below could be deleted if cheked
  ipcMain.handle('getCookie', async (_event, _someArgument) => {
    let cookieGot: Cookie;
    const result = session.defaultSession.cookies
      .get({ name: 'acs_token' })
      .then((cookies) => {
        // event.reply("getCookie", cookies[0]);
        [cookieGot] = cookies;
        store.set('cookie', cookieGot.value);
        return cookieGot.value;
      })
      .catch((_error) => {
        return 'error';
      });
    return result;
  });

  ipcMain.on('handleChangeLogo', (_event, arg) => {
    if (!mainWindow) return;
    if (arg === 'true') {
      dialog
        .showOpenDialog(mainWindow, {
          properties: ['openFile'],
        })
        .then((result: any) => {
          readFile(result.filePaths[0]);
        })
        .catch((_err: any) => {
          console.log('no file selected');
        });
    }
  });

  ipcMain.on('setSerialPath', (_event, args) => {
    store.set('serialPath', args);
  });

  ipcMain.on('restart', (_event, _args) => {
    app.relaunch();
    app.exit();
  });

  // Add a handler for the silent-print ipc message
  ipcMain.on('hideCursor', (_event, args) => {
    store.set('hideCursor', args);
    hideCursor = args;
    const css = '* { cursor: none !important; }';
    const cssTrue = '* { cursor: true; }';
    if (mainWindow && args === true) {
      mainWindow.webContents.insertCSS(css);
    } else if (mainWindow && args === false) {
      mainWindow.webContents.insertCSS(cssTrue);
    }
  });
  ipcMain.on('fullscreen', (_event, args) => {
    store.set('fullscreen', args);
    fullscreen = args;
    if (mainWindow) {
      mainWindow.setFullScreen(args);
    }
  });

  
  ipcMain.on('autoStart', (_event, args) => {
    store.set('autoStart', args);
    autoStart = args;
    applicationAutoLauncher.enable();
  });

  ipcMain.on('silentPrint', (_event, args) => {
    store.set('silentPrint', args);
    silentPrint = args;
  });
  
});
/**
 * App cleanup and maintenance by closing the workers and mainwindow once a day
 */
// We will restart all workers at 03:00 every day ( to ensure working app in low mem systems like rpi3+ )
// This will only give less then 1s down time
// At 6u20
schedulerJob = cron.schedule('20 06 * * *', () => {
  console.log('SCHEDULER ***** a task every day at 22:00');
  workerWindow?.destroy();
  workerWindowNetwork?.destroy();
});

// We will restart mainWindow at 03:05 every day ( to ensure working app in low mem systems like rpi3+ )
// This will only give 2s down time
schedulerJob = cron.schedule('21 06 * * *', () => {
  console.log('SCHEDULER ***** a task every day at 22:00');
  mainWindow?.destroy();
});



app.on('window-all-closed', () => {
  // Respect the OSX convention of having the application in memory even
  // after all windows have been closed
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app
  .whenReady()
  .then(() => {
    createWindow();
    createWorkerWindowNetwork();
    createWorkerWindowPrint();
    scheduleDailyBatchClose();
    app.on('activate', () => {
      // On macOS it's common to re-create a window in the app when the
      // dock icon is clicked and there are no other windows open.
      if (mainWindow === null) {
        createWindow();
        createWorkerWindowNetwork();
        createWorkerWindowPrint();
        // createWorkerWindowNetwork2();
      }
    });

    setTimeout(() => {
      const defaultLoopId = store.get('defaultLoopId');
      if (defaultLoopId && workerWindowNetwork) {
        console.log('[INIT] Requesting initial loop state...');
        workerWindowNetwork.webContents.send(
          'main->workerWindowNetwork-requestLoopInitState',
          [defaultLoopId]
        );
      }
    }, 1000); 
    
    
  })
  .catch(console.log);


  
