import { app } from 'electron';
import AutoLaunch from 'auto-launch';
import * as fsp from 'fs/promises';
import * as path from 'path';
import { readDesktopSettings } from '../desktop-settings';

let autoLauncher: AutoLaunch | null = null;
let autoLaunchPath = '';

const LINUX_AUTO_START_ARGUMENTS = ['--no-sandbox', '%U'];

function resolveLaunchPath(): string {
  // AppImage runs from a temporary mount; APPIMAGE points to the real file path.
  const appImagePath = process.platform === 'linux'
    ? String(process.env['APPIMAGE'] || '').trim()
    : '';

  return appImagePath || process.execPath;
}

function escapeDesktopEntryExecArgument(argument: string): string {
  const escapedArgument = argument.replace(/(["\\$`])/g, '\\$1');

  return /[\s"]/u.test(argument)
    ? `"${escapedArgument}"`
    : escapedArgument;
}

function getLinuxAutoStartDesktopEntryPath(launchPath: string): string {
  return path.join(app.getPath('home'), '.config', 'autostart', `${path.basename(launchPath)}.desktop`);
}

function buildLinuxAutoStartExecLine(launchPath: string): string {
  return `Exec=${[escapeDesktopEntryExecArgument(launchPath), ...LINUX_AUTO_START_ARGUMENTS].join(' ')}`;
}

function buildLinuxAutoStartDesktopEntry(launchPath: string): string {
  const appName = path.basename(launchPath);

  return [
    '[Desktop Entry]',
    'Type=Application',
    'Version=1.0',
    `Name=${appName}`,
    `Comment=${appName}startup script`,
    buildLinuxAutoStartExecLine(launchPath),
    'StartupNotify=false',
    'Terminal=false'
  ].join('\n');
}

async function synchronizeLinuxAutoStartDesktopEntry(launchPath: string): Promise<void> {
  if (process.platform !== 'linux') {
    return;
  }

  const desktopEntryPath = getLinuxAutoStartDesktopEntryPath(launchPath);
  const execLine = buildLinuxAutoStartExecLine(launchPath);

  let currentDesktopEntry = '';

  try {
    currentDesktopEntry = await fsp.readFile(desktopEntryPath, 'utf8');
  } catch {
    // Create the desktop entry if auto-launch did not leave one behind.
  }

  const nextDesktopEntry = currentDesktopEntry
    ? /^Exec=.*$/m.test(currentDesktopEntry)
      ? currentDesktopEntry.replace(/^Exec=.*$/m, execLine)
      : `${currentDesktopEntry.trimEnd()}\n${execLine}\n`
    : buildLinuxAutoStartDesktopEntry(launchPath);

  if (nextDesktopEntry === currentDesktopEntry) {
    return;
  }

  await fsp.mkdir(path.dirname(desktopEntryPath), { recursive: true });
  await fsp.writeFile(desktopEntryPath, nextDesktopEntry, 'utf8');
}

function getAutoLauncher(): AutoLaunch | null {
  if (!app.isPackaged) {
    return null;
  }

  if (!autoLauncher) {
    autoLaunchPath = resolveLaunchPath();
    autoLauncher = new AutoLaunch({
      name: app.getName(),
      path: autoLaunchPath
    });
  }

  return autoLauncher;
}

async function setAutoStartEnabled(enabled: boolean): Promise<void> {
  const launcher = getAutoLauncher();

  if (!launcher) {
    return;
  }

  const currentlyEnabled = await launcher.isEnabled();

  if (!enabled && currentlyEnabled === enabled) {
    return;
  }

  if (enabled) {
    if (!currentlyEnabled) {
      await launcher.enable();
    }

    await synchronizeLinuxAutoStartDesktopEntry(autoLaunchPath);
    return;
  }

  await launcher.disable();
}

export async function synchronizeAutoStartSetting(enabled = readDesktopSettings().autoStart): Promise<void> {
  try {
    await setAutoStartEnabled(enabled);
  } catch {
    // Auto-launch integration should never block app startup or settings saves.
  }
}
