const fs = require('node:fs/promises');
const path = require('node:path');
const { app, BrowserWindow, dialog, ipcMain } = require('electron');

const rendererUrl = process.env.ELECTRON_RENDERER_URL;
const isDev = Boolean(rendererUrl);
let mainWindow = null;

const waitForPrintDocument = () => `
  new Promise((resolve) => {
    const pendingImages = Array.from(document.images).filter((image) => !image.complete);
    const waitForImages = Promise.all(
      pendingImages.map(
        (image) =>
          new Promise((done) => {
            image.addEventListener('load', () => done(), { once: true });
            image.addEventListener('error', () => done(), { once: true });
          })
      )
    );
    const waitForFonts = document.fonts ? document.fonts.ready.catch(() => undefined) : Promise.resolve();

    Promise.all([waitForImages, waitForFonts]).then(() => {
      requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
    });
  });
`;

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 1300,
    height: 800,
    minWidth: 800,
    minHeight: 600,
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.cjs'),
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: false,
    },
  });

  mainWindow.once('ready-to-show', () => {
    mainWindow.show();
  });

  if (isDev) {
    void mainWindow.loadURL(rendererUrl);
    mainWindow.webContents.openDevTools({ mode: 'detach' });
  } else {
    void mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'index.html'));
  }
}

app.whenReady().then(() => {
  createMainWindow();

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

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

ipcMain.handle('desktop:show-message', async (event, options) => {
  const ownerWindow = BrowserWindow.fromWebContents(event.sender) ?? mainWindow;
  const typeMap = {
    info: 'info',
    warning: 'warning',
    error: 'error',
  };

  await dialog.showMessageBox(ownerWindow, {
    type: typeMap[options.kind] || 'info',
    title: options.title || app.name,
    message: options.message,
    buttons: ['OK'],
    defaultId: 0,
  });
});

ipcMain.handle('desktop:http-request', async (_event, payload) => {
  const body =
    payload.bodyBase64 != null
      ? Buffer.from(payload.bodyBase64, 'base64')
      : payload.bodyText;

  const response = await fetch(payload.url, {
    method: payload.method || 'GET',
    headers: payload.headers,
    body,
  });

  const buffer = Buffer.from(await response.arrayBuffer());

  return {
    ok: response.ok,
    status: response.status,
    statusText: response.statusText,
    headers: Object.fromEntries(response.headers.entries()),
    bodyBase64: buffer.toString('base64'),
  };
});

ipcMain.handle('desktop:export-pdf', async (event, payload) => {
  const ownerWindow = BrowserWindow.fromWebContents(event.sender) ?? mainWindow;
  const saveResult = await dialog.showSaveDialog(ownerWindow, {
    title: 'Export PDF',
    defaultPath: payload.fileName,
    filters: [{ name: 'PDF Document', extensions: ['pdf'] }],
  });

  if (saveResult.canceled || !saveResult.filePath) {
    return { canceled: true };
  }

  const tempHtmlPath = path.join(
    app.getPath('temp'),
    `nextcloud-notes-export-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.html`
  );

  const pdfWindow = new BrowserWindow({
    width: 960,
    height: 1100,
    show: false,
    parent: ownerWindow ?? undefined,
    webPreferences: {
      sandbox: false,
      contextIsolation: true,
      nodeIntegration: false,
      spellcheck: false,
    },
  });

  try {
    await fs.writeFile(tempHtmlPath, payload.documentHtml, 'utf8');
    await pdfWindow.loadFile(tempHtmlPath);
    await pdfWindow.webContents.executeJavaScript(waitForPrintDocument(), true);

    const pdfData = await pdfWindow.webContents.printToPDF({
      printBackground: true,
      preferCSSPageSize: true,
    });

    await fs.writeFile(saveResult.filePath, pdfData);

    return {
      canceled: false,
      filePath: saveResult.filePath,
    };
  } finally {
    await fs.unlink(tempHtmlPath).catch(() => undefined);
    if (!pdfWindow.isDestroyed()) {
      pdfWindow.destroy();
    }
  }
});
