const { app, BrowserWindow, dialog, ipcMain, powerSaveBlocker, clipboard, safeStorage, shell } = require('electron');
const path = require('node:path');
const { pathToFileURL } = require('node:url');
const { Sinks } = require('./sinks.cjs');
const { diskSpace } = require('./disk.cjs');
const { DesktopLicense } = require('./license.cjs');
const { readBrand, brandPaths } = require('./brand.cjs');
const brand = readBrand();
const directories = brandPaths(app.getPath('appData'), brand);

// The package name remains relio-local; TransferWing preserves its original activation path.
// Relyo keeps a separate activation because macOS encryption keys differ between applications.
app.setName(brand.name);
app.setPath('userData', directories.userData);
if (process.platform === 'win32') app.setAppUserModelId(brand.appId);

const sinks = new Sinks();
let window, server, origin, blocker, destinationDirectory, license, quitting = false;

function authorize(event) {
  if (!window || event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame)
    throw new Error('Accès refusé.');
  const url = new URL(event.senderFrame.url);
  if (url.origin !== origin) throw new Error('Origine refusée.');
}
function handle(name, callback) {
  ipcMain.handle(name, (event, ...args) => { authorize(event); return callback(...args); });
}
function stopAwake() {
  if (blocker !== undefined && powerSaveBlocker.isStarted(blocker)) powerSaveBlocker.stop(blocker);
  blocker = undefined;
}

function registerHandlers() {
  handle('license:account', () => shell.openExternal(`${brand.serviceOrigin}/account`));
  handle('license:status', () => license.status());
  handle('license:activate', link => license.activate(link));
  handle('license:connect', () => license.publisherToken());
  handle('license:deactivate', () => license.deactivate());
  handle('disk:space', async () => {
    const systemRoot = process.platform === 'win32' ? path.parse(process.env.SystemRoot || 'C:\\Windows').root : '/';
    const locations = [{ id: 'system', label: 'Disque système', directory: systemRoot }];
    if (destinationDirectory) locations.push({ id: 'destination', label: 'Disque de destination du dernier téléchargement choisi', directory: destinationDirectory });
    return Promise.all(locations.map(async ({ id, label, directory }) => {
      try { return { id, label, ...await diskSpace(directory) }; }
      catch { return { id, label, unavailable: true }; }
    }));
  });
  handle('clipboard:link', value => {
    if (typeof value !== 'string' || value.length > 4096) throw new Error('Lien invalide.');
    const url = new URL(value);
    if (![origin, 'https://t.mailtingpot.com', 'https://transferwing.com', 'https://relyo.online'].includes(url.origin) || url.username || url.password || !['/', '/app'].includes(url.pathname) ||
        !/^\?share=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(url.search) ||
        !/^#[A-Za-z0-9_-]{43}$/.test(url.hash)) throw new Error('Lien de partage invalide.');
    clipboard.writeText(url.href);
    return true;
  });
  handle('sink:open', async metadata => {
    if (!metadata || typeof metadata.name !== 'string' || !Number.isSafeInteger(metadata.size) || metadata.size < 0)
      throw new Error('Fichier invalide.');
    if (sinks.items.size >= 32) throw new Error('Trop de téléchargements simultanés.');
    const name = metadata.name.replace(/[\\/\x00-\x1f<>:"|?*]/g, '_').slice(0, 200) || 'fichier';
    const selection = await dialog.showSaveDialog(window, { title: 'Enregistrer le fichier reçu', defaultPath: name,
      properties: ['showOverwriteConfirmation', 'createDirectory'] });
    if (selection.canceled || !selection.filePath) return null;
    destinationDirectory = path.dirname(selection.filePath);
    return sinks.open(selection.filePath, metadata.size);
  });
  handle('sink:write', (id, bytes) => sinks.write(id, bytes));
  handle('sink:close', id => sinks.close(id));
  handle('sink:abort', id => sinks.cleanup(id));
  handle('power:awake', enabled => {
    if (typeof enabled !== 'boolean') throw new Error('Valeur invalide.');
    if (enabled && blocker === undefined) blocker = powerSaveBlocker.start('prevent-app-suspension');
    if (!enabled) stopAwake();
    return enabled;
  });
}

function createWindow() {
  window = new BrowserWindow({ width: 1180, height: 840, minWidth: 760, minHeight: 600,
    title: `${brand.name} — Partage direct`, webPreferences: { preload: path.join(__dirname, 'preload.cjs'),
      additionalArguments: [`--relio-version=${app.getVersion()}`],
      sandbox: true, contextIsolation: true, nodeIntegration: false, webSecurity: true } });
  window.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
  window.webContents.session.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));
  window.webContents.session.setPermissionCheckHandler(() => false);
  window.webContents.on('will-navigate', (event, url) => {
    if (new URL(url).origin !== origin) event.preventDefault();
  });
  window.webContents.on('will-attach-webview', event => event.preventDefault());
  window.webContents.on('will-prevent-unload', event => {
    if (quitting) event.preventDefault();
  });
  window.webContents.on('render-process-gone', () => { stopAwake(); sinks.cleanupAll().catch(console.error); });
  window.on('close', event => {
    if (quitting) return;
    event.preventDefault();
    app.quit();
  });
  window.on('closed', () => { window = undefined; });
  window.loadURL(origin);
}

app.on('before-quit', event => {
  if (quitting) return;
  event.preventDefault();
  if (window) {
    const answer = dialog.showMessageBoxSync(window, { type: 'question', title: `Quitter ${brand.name} ?`,
      message: 'Quitter arrête les partages et les téléchargements en cours.',
      detail: 'Laissez cette fenêtre ouverte pour que les fichiers restent disponibles.',
      buttons: ['Rester ouvert', 'Quitter'], defaultId: 0, cancelId: 0 });
    if (answer === 0) return;
  }
  quitting = true;
  stopAwake();
  Promise.resolve().then(() => sinks.cleanupAll()).then(() => server?.close()).catch(console.error)
    .finally(() => app.quit());
});
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
app.on('activate', () => { if (!window && origin && !quitting) createWindow(); });

app.whenReady().then(async () => {
  license = new DesktopLicense({ directory: directories.licenseDirectory, safeStorage, serviceOrigin: brand.serviceOrigin });
  const { startServer } = await import(pathToFileURL(path.join(__dirname, '../server/index.js')).href);
  server = await startServer({ port: 0, host: '127.0.0.1', localBrand: brand.id });
  const port = server.port ?? server.address?.().port;
  if (!Number.isInteger(port) || port <= 0) throw new Error('Port du serveur local invalide.');
  origin = `http://127.0.0.1:${port}`;
  registerHandlers();
  createWindow();
}).catch(error => {
  dialog.showErrorBox(`${brand.name} — Démarrage impossible`, String(error.message || error));
  app.quit();
});
