#!/usr/bin/env node
'use strict'
/**
 * Orchestrates the self-built FFmpeg binary.
 *
 * Reads the version pointer in ffmpeg-build.config.json, compares it against the
 * per-binary manifest, and (re)builds only when something changed — so it's a
 * no-op on a normal `npm install` and rebuilds when you bump the pointer.
 *
 * Mirrors scripts/download-node.js, which does the same for the bundled Node binary.
 * Output: resources/bin/<platform>/<arch>/ffmpeg[.exe] + .ffmpeg-build-manifest.json
 *
 * Targets per host: mac -> arm64, win -> x64. Any other host (incl. Linux, not a
 * shipping target) is skipped. The heavy compile lives in the platform scripts:
 * build-ffmpeg-mac.sh / build-ffmpeg-win.ps1 (both call build-ffmpeg-core.sh).
 *
 * Flags / env:
 *   --strict | VIZLOOM_FFMPEG_STRICT=1   a failed build exits non-zero (used by build and test:e2e)
 *   --force  | VIZLOOM_FFMPEG_FORCE=1    rebuild even if the manifest already matches
 *   VIZLOOM_SKIP_FFMPEG_BUILD=1          skip entirely (contributors not touching encoding)
 */
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const { execFileSync } = require('child_process')

const REPO_ROOT = path.resolve(__dirname, '..')
const CONFIG_PATH = path.join(REPO_ROOT, 'ffmpeg-build.config.json')

const FORCE = process.argv.includes('--force') || process.env.VIZLOOM_FFMPEG_FORCE === '1'
const STRICT = process.argv.includes('--strict') || process.env.VIZLOOM_FFMPEG_STRICT === '1'
const SKIP = process.env.VIZLOOM_SKIP_FFMPEG_BUILD === '1'

const TARGETS = { mac: ['arm64'], win: ['x64'] }

function log(msg) {
  console.log(`[build-ffmpeg] ${msg}`)
}

function platformName() {
  if (process.platform === 'darwin') return 'mac'
  if (process.platform === 'win32') return 'win'
  return process.platform
}

function binDir(platform, arch) {
  return path.join(REPO_ROOT, 'resources', 'bin', platform, arch)
}

function binName(platform) {
  return platform === 'win' ? 'ffmpeg.exe' : 'ffmpeg'
}

function sha256(file) {
  return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
}

/** A target is up to date iff the binary + manifest exist and the manifest matches
 *  the pinned config AND the on-disk binary matches the manifest's recorded SHA. */
function isUpToDate(cfg, platform, arch) {
  const dir = binDir(platform, arch)
  const bin = path.join(dir, binName(platform))
  const manifestPath = path.join(dir, '.ffmpeg-build-manifest.json')
  if (!fs.existsSync(bin) || !fs.existsSync(manifestPath)) return false
  let m
  try {
    m = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
  } catch {
    return false
  }
  if (m.ffmpegVersion !== cfg.ffmpeg.version) return false
  if (m.x264Commit !== cfg.x264.commit) return false
  if (m.lameVersion !== cfg.lame.version) return false
  if (JSON.stringify(m.configureFlags) !== JSON.stringify(cfg.configureFlags)) return false
  try {
    if (m.binarySha256 !== sha256(bin)) return false
  } catch {
    return false
  }
  return true
}

function runPlatformBuild(platform) {
  if (platform === 'win') {
    execFileSync(
      'powershell',
      ['-ExecutionPolicy', 'Bypass', '-File', path.join(__dirname, 'build-ffmpeg-win.ps1')],
      { stdio: 'inherit', cwd: REPO_ROOT }
    )
  } else {
    execFileSync('bash', [path.join(__dirname, 'build-ffmpeg-mac.sh')], {
      stdio: 'inherit',
      cwd: REPO_ROOT
    })
  }
}

function main() {
  if (SKIP) {
    log('VIZLOOM_SKIP_FFMPEG_BUILD=1 — skipping ffmpeg build')
    return
  }

  const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'))
  const platform = platformName()
  const arches = TARGETS[platform]
  if (!arches) {
    log(`platform "${platform}" is not a shipping target — skipping ffmpeg build`)
    return
  }

  if (!FORCE && arches.every((a) => isUpToDate(cfg, platform, a))) {
    log(`ffmpeg ${cfg.ffmpeg.version} already built for ${platform}/${arches.join(',')} — skipping`)
    return
  }

  if (FORCE) {
    log('--force — removing existing binary + manifest before rebuild')
    for (const a of arches) {
      const dir = binDir(platform, a)
      for (const f of ['ffmpeg', 'ffmpeg.exe', '.ffmpeg-build-manifest.json']) {
        fs.rmSync(path.join(dir, f), { force: true })
      }
    }
  }

  log(`building ffmpeg ${cfg.ffmpeg.version} for ${platform}/${arches.join(',')} ...`)
  try {
    runPlatformBuild(platform)
  } catch {
    const help =
      'FFmpeg build failed. Encoding/export will not work until it succeeds.\n' +
      '  Fix the toolchain and re-run `npm run build:ffmpeg`.\n' +
      (platform === 'mac'
        ? '  macOS needs: brew install nasm pkg-config (and Xcode Command Line Tools).\n'
        : '  Windows needs MSYS2/MinGW — see scripts/build-ffmpeg-win.ps1.\n') +
      '  To skip intentionally, set VIZLOOM_SKIP_FFMPEG_BUILD=1.'
    if (STRICT) {
      console.error(`[build-ffmpeg] ${help}`)
      process.exit(1)
    }
    console.warn(`[build-ffmpeg] WARNING: ${help}`)
  }
}

main()
