#!/usr/bin/env node

import { execFileSync, spawnSync } from 'node:child_process'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import {
  buildRemoteStagingManifest,
  buildRemoteStagingProofScript,
  buildSshArgs,
  buildStageArchiveRemoteCommand,
  extractStagingProofSummary,
  loadStagingProofManifest,
  parseStagingProofArgs,
  resolveStagingProofOptions,
  sanitizeStagingProofManifest,
} from './run-vps-same-origin-staging-proof-lib.mjs'

const currentFile = fileURLToPath(import.meta.url)
const websiteRoot = path.resolve(path.dirname(currentFile), '..')
const repoRoot = path.resolve(websiteRoot, '..')

function runCommand(command, args, options = {}) {
  const result = spawnSync(command, args, {
    encoding: 'utf8',
    maxBuffer: 64 * 1024 * 1024,
    ...options,
  })

  if (result.error) {
    throw result.error
  }

  return result
}

function buildArchiveBuffer(repoRoot, archiveSource) {
  if (archiveSource === 'worktree') {
    return execFileSync('tar', [
      '--exclude=website/node_modules',
      '--exclude=website/dist',
      '--exclude=website/coverage',
      '--exclude=website/server/node_modules',
      '--exclude=website/server/dist',
      '-cf',
      '-',
      'website',
    ], {
      cwd: repoRoot,
      encoding: null,
      maxBuffer: 64 * 1024 * 1024,
    })
  }

  return execFileSync('git', ['archive', '--format=tar', 'HEAD', 'website'], {
    cwd: repoRoot,
    encoding: null,
    maxBuffer: 64 * 1024 * 1024,
  })
}

function main() {
  const rawArgs = parseStagingProofArgs(process.argv.slice(2))
  const gitRef = execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
    cwd: repoRoot,
    encoding: 'utf8',
  }).trim()

  const options = resolveStagingProofOptions({
    manifestPath: rawArgs.manifest,
    identityFile: rawArgs.identityFile,
    vpsHost: rawArgs.vpsHost,
    vpsUser: rawArgs.vpsUser,
    archiveSource: rawArgs.archiveSource,
    remoteDir: rawArgs.remoteDir,
    port: rawArgs.port,
    waitSeconds: rawArgs.waitSeconds,
    keepRemoteDir: rawArgs.keepRemoteDir,
    json: rawArgs.json,
    dryRun: rawArgs.dryRun,
    gitRef,
  })

  const { path: manifestPath, manifest } = loadStagingProofManifest(options.manifestPath)
  const remoteManifest = buildRemoteStagingManifest(manifest, {
    remoteDir: options.remoteDir,
    port: options.port,
  })

  const remoteScript = buildRemoteStagingProofScript({
    remoteDir: options.remoteDir,
    waitSeconds: options.waitSeconds,
    keepRemoteDir: options.keepRemoteDir,
    manifest: remoteManifest,
  })

  const archiveCommand = buildStageArchiveRemoteCommand(options.remoteDir)
  const sanitizedManifest = sanitizeStagingProofManifest(remoteManifest)

  if (options.dryRun) {
    const payload = {
      repoRoot,
      websiteRoot,
      manifestPath,
      vpsHost: options.vpsHost,
      vpsUser: options.vpsUser,
      archiveSource: options.archiveSource,
      remoteDir: options.remoteDir,
      port: options.port,
      waitSeconds: options.waitSeconds,
      keepRemoteDir: options.keepRemoteDir,
      archiveCommand,
      remoteManifest: sanitizedManifest,
      remoteScript,
    }

    if (options.json) {
      process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`)
    } else {
      process.stdout.write(`[staging-proof] manifest: ${manifestPath}\n`)
      process.stdout.write(`[staging-proof] vps: ${options.vpsUser}@${options.vpsHost}\n`)
      process.stdout.write(`[staging-proof] remote dir: ${options.remoteDir}\n`)
      process.stdout.write(`[staging-proof] port: ${options.port}\n`)
      process.stdout.write(`[staging-proof] archive command: ${archiveCommand}\n`)
    }
    return
  }

  const archiveBuffer = buildArchiveBuffer(repoRoot, options.archiveSource)

  const stageResult = runCommand('ssh', buildSshArgs({
    identityFile: options.identityFile,
    vpsUser: options.vpsUser,
    vpsHost: options.vpsHost,
    remoteCommand: archiveCommand,
  }), {
    input: archiveBuffer,
    encoding: 'utf8',
  })

  if (stageResult.status !== 0) {
    process.stderr.write(stageResult.stderr || '')
    process.exit(stageResult.status || 1)
  }

  const proofResult = runCommand('ssh', [
    ...buildSshArgs({
      identityFile: options.identityFile,
      vpsUser: options.vpsUser,
      vpsHost: options.vpsHost,
    }),
    'bash',
    '-s',
  ], {
    input: remoteScript,
  })

  if (!options.json) {
    process.stdout.write(proofResult.stdout || '')
  }
  process.stderr.write(proofResult.stderr || '')

  const summary = extractStagingProofSummary(proofResult.stdout || '')

  if (options.json) {
    process.stdout.write(`${JSON.stringify({
      ok: proofResult.status === 0,
      manifestPath,
      vpsHost: options.vpsHost,
      vpsUser: options.vpsUser,
      remoteDir: options.remoteDir,
      port: options.port,
      keepRemoteDir: options.keepRemoteDir,
      summary,
    }, null, 2)}\n`)
  }

  if (proofResult.status !== 0) {
    process.exit(proofResult.status || 1)
  }
}

main()
