import { promises as fs } from 'fs'
import { createRequire } from 'module'
import path from 'path'

const require = createRequire(import.meta.url)
const sudo = require('@vscode/sudo-prompt')

const quoteShellArg = (value) => `'${String(value).replaceAll("'", "'\\''")}'`

const parseMacInstallerProgress = (output) => {
  const lines = String(output || '').split('\n')
  let percent = null
  let message = 'Installing update...'

  for (const line of lines) {
    if (line.startsWith('installer:PHASE:')) {
      message = line.slice('installer:PHASE:'.length).trim() || message
    } else if (line.startsWith('installer:STATUS:')) {
      const status = line.slice('installer:STATUS:'.length).trim()
      if (status) message = status
    } else if (line.startsWith('installer:%')) {
      const value = Number.parseFloat(line.slice('installer:%'.length))
      if (Number.isFinite(value)) {
        percent = Math.min(100, Math.round(value <= 1 ? value * 100 : value))
      }
    } else if (
      line.startsWith('installer: ') &&
      !line.startsWith('installer:PHASE:') &&
      !line.startsWith('installer:STATUS:') &&
      !line.startsWith('installer:%')
    ) {
      const text = line.slice('installer: '.length).trim()
      if (text) message = text
    }
  }

  return { percent, message }
}

const isMacInstallSuccessful = (output) =>
  /installer: The (install|upgrade) was successful\./i.test(output)

const isMacInstallFailed = (output) =>
  /installer: The install failed/i.test(output)

const buildMacInstallScript = (installerPath, logPath) =>
  `sleep 2 && /usr/sbin/installer -pkg ${quoteShellArg(
    installerPath
  )} -target / -verboseR 2>&1 | /usr/bin/tee ${quoteShellArg(logPath)}`

const startMacInstallerProgressWatch = (logPath, webContents, sendProgress) => {
  let installerOutput = ''
  let offset = 0
  let lastPercent = null
  let lastMessage = null

  const poll = async () => {
    try {
      const stat = await fs.stat(logPath)
      if (stat.size <= offset) return

      const handle = await fs.open(logPath, 'r')
      try {
        const buffer = Buffer.alloc(stat.size - offset)
        await handle.read(buffer, 0, buffer.length, offset)
        offset = stat.size
        installerOutput += buffer.toString('utf8')

        const { percent, message } = parseMacInstallerProgress(installerOutput)
        const resolvedMessage = message || 'Installing update...'

        if (percent !== lastPercent || resolvedMessage !== lastMessage) {
          lastPercent = percent
          lastMessage = resolvedMessage
          sendProgress(webContents, {
            phase: 'installing',
            percent,
            message: resolvedMessage
          })
        }
      } finally {
        await handle.close()
      }
    } catch (error) {
      if (error?.code !== 'ENOENT') {
        console.error('[app-update] installer log poll error:', error)
      }
    }
  }

  const intervalId = setInterval(() => {
    poll().catch((error) => {
      console.error('[app-update] installer log poll error:', error)
    })
  }, 300)

  return async () => {
    clearInterval(intervalId)
    await poll()
    return installerOutput
  }
}

export const launchMacInstaller = (
  app,
  installerPath,
  webContents,
  { sendProgress, getInstallErrorMessage }
) => {
  const logPath = path.join(path.dirname(installerPath), 'install.log')
  const installScript = buildMacInstallScript(installerPath, logPath)
  const promptName = 'farmcontrol'

  sendProgress(webContents, {
    phase: 'installing',
    percent: 0,
    message: 'Enter your Mac password when prompted.'
  })

  app.focus({ steal: true })
  app.dock?.show()

  const stopProgressWatch = startMacInstallerProgressWatch(
    logPath,
    webContents,
    sendProgress
  )

  return new Promise((resolve, reject) => {
    sudo.exec(installScript, { name: promptName }, async (error, stdout, stderr) => {
      const watchedOutput = await stopProgressWatch()
      const output = `${stdout || ''}${stderr || ''}` || watchedOutput

      await fs.unlink(logPath).catch(() => {})

      if (stderr) console.error('[app-update] installer stderr:', stderr)

      if (error) {
        console.error('[app-update] installer error:', error)
        const message = getInstallErrorMessage(error, output)
        sendProgress(webContents, {
          phase: 'error',
          percent: null,
          message
        })
        reject(new Error(message))
        return
      }

      if (isMacInstallFailed(output) || !isMacInstallSuccessful(output)) {
        const message = getInstallErrorMessage(null, output)
        sendProgress(webContents, {
          phase: 'error',
          percent: null,
          message
        })
        reject(new Error(message))
        return
      }

      const { percent, message } = parseMacInstallerProgress(output)

      sendProgress(webContents, {
        phase: 'installing',
        percent: percent ?? 100,
        message: message || 'Installation complete. Restarting Farm Control...'
      })

      resolve()
    })
  })
}
