// open-or-focus-app.ts

import { shell } from 'electron';
import { execFile } from 'child_process';
import * as fs from 'fs';

export function openOrFocus(folder: string, isUrl: boolean): Promise<void> {
  if (isUrl) {
    return shell.openExternal(folder);
  }

  if (!fs.existsSync(folder)) {
    return Promise.reject(new Error(`Path does not exist: ${folder}`));
  }

  if (process.platform === 'darwin') {
    return openOrFocusMac(folder);
  }

  if (process.platform === 'win32') {
    return openOrFocusWindows(folder);
  }

  return shell.openPath(folder).then(errorMessage => {
    if (errorMessage) {
      throw new Error(errorMessage);
    }
  });
}

function openOrFocusMac(appPath: string): Promise<void> {
  return new Promise((resolve, reject) => {
    execFile('open', [getMacAppBundlePath(appPath)], error => {
      if (error) {
        reject(error);
        return;
      }

      resolve();
    });
  });
}

function getMacAppBundlePath(inputPath: string): string {
  const appIndex = inputPath.toLowerCase().indexOf('.app');
  return appIndex === -1 ? inputPath : inputPath.substring(0, appIndex + 4);
}

function openOrFocusWindows(appPath: string): Promise<void> {
  return new Promise((resolve, reject) => {
    const script = getWindowsScript(appPath);

    execFile(
      'powershell.exe',
      ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script],
      (error, stdout, stderr) => {
        if (stdout) {
          console.log('openOrFocus stdout:', stdout);
        }

        if (stderr) {
          console.error('openOrFocus stderr:', stderr);
        }

        if (error) {
          reject(error);
          return;
        }

        resolve();
      }
    );
  });
}

function getWindowsScript(appPath: string): string {
  const safeAppPath = escapePowerShellString(appPath);

  return `
$inputPath = '${safeAppPath}'
$appName = [System.IO.Path]::GetFileNameWithoutExtension($inputPath)

$proc = Get-Process -Name $appName -ErrorAction SilentlyContinue |
  Where-Object { $_.MainWindowTitle -ne "" -and $_.MainWindowHandle -ne 0 } |
  Select-Object -First 1

if ($proc) {
  Write-Output "FOCUSING: $($proc.ProcessName), PID: $($proc.Id)"

  Add-Type @"
using System;
using System.Runtime.InteropServices;

public class Win32 {
  [DllImport("user32.dll")]
  public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

  [DllImport("user32.dll")]
  public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"@

  # 9 = restore if minimized
  [Win32]::ShowWindowAsync($proc.MainWindowHandle, 9) | Out-Null
  Start-Sleep -Milliseconds 200

  # 5 = show and activate
  [Win32]::ShowWindowAsync($proc.MainWindowHandle, 5) | Out-Null
  Start-Sleep -Milliseconds 200

  $wshell = New-Object -ComObject WScript.Shell
  $wshell.AppActivate($proc.Id) | Out-Null
  Start-Sleep -Milliseconds 200

  [Win32]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null
} else {
  Write-Output "STARTING: $inputPath"
  Start-Process -FilePath $inputPath
}
`;
}

function escapePowerShellString(value: string): string {
  return value.replace(/'/g, "''");
}