#!/usr/bin/env node

const fs = require('node:fs')
const path = require('node:path')
const { spawn, spawnSync } = require('node:child_process')

const appRoot = path.resolve(__dirname, '..')
const launchCwd = process.cwd()

function electronExecutablePath() {
  const electronPackageJson = require.resolve('electron/package.json')
  const electronRoot = path.dirname(electronPackageJson)
  const pathFile = path.join(electronRoot, 'path.txt')

  if (!fs.existsSync(pathFile)) {
    restoreElectronPathFile(electronRoot, pathFile)
  }

  let relativeExecutable = fs.existsSync(pathFile) ? fs.readFileSync(pathFile, 'utf8').trim() : ''
  let executable = relativeExecutable ? path.join(electronRoot, 'dist', relativeExecutable) : ''

  if (!executable || !fs.existsSync(executable)) {
    installElectron(electronRoot)
    if (!fs.existsSync(pathFile)) {
      restoreElectronPathFile(electronRoot, pathFile)
    }
    relativeExecutable = fs.existsSync(pathFile) ? fs.readFileSync(pathFile, 'utf8').trim() : ''
    executable = relativeExecutable ? path.join(electronRoot, 'dist', relativeExecutable) : ''
  }

  if (!executable || !fs.existsSync(executable)) {
    rebuildElectronPackage()
    if (!fs.existsSync(pathFile)) {
      restoreElectronPathFile(electronRoot, pathFile)
    }
    relativeExecutable = fs.existsSync(pathFile) ? fs.readFileSync(pathFile, 'utf8').trim() : ''
    executable = relativeExecutable ? path.join(electronRoot, 'dist', relativeExecutable) : ''
  }

  if (!executable || !fs.existsSync(executable)) {
    const expected = path.join(electronRoot, 'dist', defaultElectronExecutable())
    throw new Error(
      `Electron executable was not found after install. Expected: ${expected}
` +
      `Try repairing the install with: npm rebuild -g hyperdesk --foreground-scripts`
    )
  }

  return executable
}

function restoreElectronPathFile(electronRoot, pathFile) {
  const relativeExecutable = defaultElectronExecutable()
  const executable = path.join(electronRoot, 'dist', relativeExecutable)
  if (fs.existsSync(executable)) {
    fs.writeFileSync(pathFile, relativeExecutable)
  }
}

function defaultElectronExecutable() {
  switch (process.platform) {
    case 'darwin':
      return 'Electron.app/Contents/MacOS/Electron'
    case 'win32':
      return 'electron.exe'
    default:
      return 'electron'
  }
}

function rebuildElectronPackage() {
  console.log('Electron runtime is still missing; rebuilding npm package...')
  const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
  const result = spawnSync(npm, ['rebuild', 'electron', '--foreground-scripts'], {
    cwd: appRoot,
    stdio: 'inherit',
    env: process.env,
  })

  if (result.status !== 0) {
    console.error('npm rebuild electron failed; continuing with final executable check...')
  }
}

function cleanPartialElectronInstall(electronRoot) {
  for (const target of ['dist', 'path.txt']) {
    try {
      fs.rmSync(path.join(electronRoot, target), { recursive: true, force: true })
    } catch (_error) {
      // Best effort cleanup; install.js will report the real failure if cleanup was insufficient.
    }
  }
}

function installElectron(electronRoot) {
  console.log('Electron runtime is missing; downloading it now...')
  cleanPartialElectronInstall(electronRoot)
  const result = spawnSync(process.execPath, [path.join(electronRoot, 'install.js')], {
    stdio: 'inherit',
    env: { ...process.env, force_no_cache: 'true' },
  })

  if (result.status !== 0) {
    throw new Error('Electron runtime installation failed. Try reinstalling with: npm install -g hyperdesk')
  }
}

let electronPath
try {
  electronPath = electronExecutablePath()
} catch (error) {
  console.error(error?.message || String(error))
  process.exit(1)
}

const child = spawn(electronPath, [appRoot], {
  stdio: 'inherit',
  env: {
    ...process.env,
    HYPER_DESK_WORKSPACE_CWD: launchCwd,
  },
})

child.on('exit', code => {
  process.exit(code ?? 0)
})
