import fs from 'node:fs'
import path from 'node:path'
import posixPath from 'node:path/posix'

import { buildSshArgs, sanitizeStagingProofManifest } from './run-vps-same-origin-staging-proof-lib.mjs'

function normalizeTrimmed(value) {
  return String(value || '').trim()
}

function normalizeBoolean(value, fallback = false) {
  if (typeof value === 'boolean') {
    return value
  }

  const normalized = normalizeTrimmed(value).toLowerCase()
  if (!normalized) {
    return fallback
  }

  return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'
}

function requireNonEmpty(value, label) {
  const normalized = normalizeTrimmed(value)
  if (!normalized) {
    throw new Error(`${label} is required.`)
  }
  return normalized
}

function requirePositiveInteger(value, label) {
  const parsed = Number.parseInt(String(value || ''), 10)
  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error(`${label} must be a positive integer.`)
  }
  return parsed
}

function normalizeArchiveSource(value, fallback = 'git-head') {
  const normalized = normalizeTrimmed(value).toLowerCase()
  if (normalized === 'worktree') {
    return 'worktree'
  }
  if (normalized === 'git' || normalized === 'head' || normalized === 'git-head') {
    return 'git-head'
  }
  return fallback
}

export function parseLiveDeployArgs(argv) {
  const options = {
    manifest: '',
    identityFile: '',
    vpsHost: '212.227.13.220',
    vpsUser: 'root',
    archiveSource: 'git-head',
    bundleDir: '',
    healthUrl: '',
    waitSeconds: '30',
    keepBundleDir: false,
    skipLiveValidation: false,
    json: false,
    dryRun: false,
  }

  for (let index = 0; index < argv.length; index += 1) {
    const argument = argv[index]
    const next = argv[index + 1] || ''

    switch (argument) {
      case '--manifest':
        options.manifest = next
        index += 1
        break
      case '--identity-file':
        options.identityFile = next
        index += 1
        break
      case '--vps-host':
        options.vpsHost = next
        index += 1
        break
      case '--vps-user':
        options.vpsUser = next
        index += 1
        break
      case '--archive-source':
        options.archiveSource = next
        index += 1
        break
      case '--bundle-dir':
        options.bundleDir = next
        index += 1
        break
      case '--health-url':
        options.healthUrl = next
        index += 1
        break
      case '--wait-seconds':
        options.waitSeconds = next
        index += 1
        break
      case '--keep-bundle-dir':
        options.keepBundleDir = true
        break
      case '--skip-live-validation':
        options.skipLiveValidation = true
        break
      case '--json':
        options.json = true
        break
      case '--dry-run':
        options.dryRun = true
        break
      default:
        break
    }
  }

  return options
}

export function loadLiveDeployManifest(filePath) {
  const absolutePath = path.resolve(filePath)
  const raw = fs.readFileSync(absolutePath, 'utf8')
  return {
    path: absolutePath,
    manifest: JSON.parse(raw),
  }
}

export function buildDefaultLiveDeployBundleDir(gitRef = 'head') {
  const normalized = normalizeTrimmed(gitRef).replace(/[^a-zA-Z0-9._-]/g, '-')
  return `/tmp/hypertwist-live-deploy-${normalized || 'head'}`
}

export function resolveLiveDeployOptions({
  manifestPath,
  identityFile,
  vpsHost,
  vpsUser,
  archiveSource,
  bundleDir,
  healthUrl,
  waitSeconds,
  keepBundleDir,
  skipLiveValidation,
  json,
  dryRun,
  manifest,
  gitRef,
}) {
  const checkoutRoot = requireNonEmpty(manifest?.checkoutRoot || '/srv/hypertwist/current', 'manifest.checkoutRoot')
  const publicOrigin = requireNonEmpty(manifest?.publicOrigin || '', 'manifest.publicOrigin')

  return {
    manifestPath: path.resolve(requireNonEmpty(manifestPath, 'manifestPath')),
    identityFile: path.resolve(requireNonEmpty(identityFile, 'identityFile')),
    vpsHost: requireNonEmpty(vpsHost || '212.227.13.220', 'vpsHost'),
    vpsUser: requireNonEmpty(vpsUser || 'root', 'vpsUser'),
    archiveSource: normalizeArchiveSource(archiveSource, 'git-head'),
    bundleDir: normalizeTrimmed(bundleDir) || buildDefaultLiveDeployBundleDir(gitRef),
    checkoutRoot,
    healthUrl: normalizeTrimmed(healthUrl) || publicOrigin,
    waitSeconds: requirePositiveInteger(waitSeconds || '30', 'waitSeconds'),
    keepBundleDir: normalizeBoolean(keepBundleDir, false),
    skipLiveValidation: normalizeBoolean(skipLiveValidation, false),
    json: normalizeBoolean(json, false),
    dryRun: normalizeBoolean(dryRun, false),
  }
}

export function buildStageCheckoutRemoteCommand(checkoutRoot) {
  const parentDir = posixPath.dirname(checkoutRoot)
  return `rm -rf ${JSON.stringify(checkoutRoot)} && mkdir -p ${JSON.stringify(parentDir)} ${JSON.stringify(checkoutRoot)} && tar -xf - -C ${JSON.stringify(checkoutRoot)}`
}

export function buildStageBundleRemoteCommand(bundleDir) {
  return `rm -rf ${JSON.stringify(bundleDir)} && mkdir -p ${JSON.stringify(bundleDir)} && tar -xf - -C ${JSON.stringify(bundleDir)}`
}

export function sanitizeLiveDeployManifest(manifest) {
  return sanitizeStagingProofManifest(manifest)
}

export function buildRemoteLiveDeployScript({
  checkoutRoot,
  bundleDir,
  keepBundleDir,
  manifest,
}) {
  const serviceUser = normalizeTrimmed(manifest?.serviceUser || 'hypertwist')
  const serviceGroup = normalizeTrimmed(manifest?.serviceGroup || serviceUser)
  const billingStatePath = normalizeTrimmed(manifest?.server?.billingStatePath || '/var/lib/hypertwist/auth/hypertwist-billing-state.json')
  const billingStateDir = posixPath.dirname(billingStatePath)

  return `set -euo pipefail
CHECKOUT_ROOT=${JSON.stringify(checkoutRoot)}
BUNDLE_DIR=${JSON.stringify(bundleDir)}
SERVICE_USER=${JSON.stringify(serviceUser)}
SERVICE_GROUP=${JSON.stringify(serviceGroup)}
BILLING_STATE_DIR=${JSON.stringify(billingStateDir)}
KEEP_BUNDLE_DIR=${keepBundleDir ? '1' : '0'}
SERVICE_NAME="hypertwist-website-auth-server.service"
NGINX_CONF_NAME="hypertwist-app.conf"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
CHECKOUT_PARENT=$(dirname "$CHECKOUT_ROOT")
WEBSITE_DIR="$CHECKOUT_ROOT/website"
SERVER_DIR="$WEBSITE_DIR/server"
NGINX_BACKUP=""
PLACEHOLDER_BACKUP=""
cleanup() {
  if [ "$KEEP_BUNDLE_DIR" = "1" ]; then
    return
  fi
  rm -rf "$BUNDLE_DIR"
}
trap cleanup EXIT

if ! getent group "$SERVICE_GROUP" >/dev/null 2>&1; then
  groupadd --system "$SERVICE_GROUP"
fi

if ! id "$SERVICE_USER" >/dev/null 2>&1; then
  useradd --system --create-home --home-dir "$CHECKOUT_PARENT" --shell /usr/sbin/nologin --gid "$SERVICE_GROUP" "$SERVICE_USER"
fi

install -d -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$CHECKOUT_PARENT" "$CHECKOUT_ROOT" "$(dirname "$BILLING_STATE_DIR")" "$BILLING_STATE_DIR"

cp "$BUNDLE_DIR/website.env" "$WEBSITE_DIR/.env"
cp "$BUNDLE_DIR/server.env" "$SERVER_DIR/.env"
chown -R "$SERVICE_USER:$SERVICE_GROUP" "$CHECKOUT_ROOT" "$(dirname "$BILLING_STATE_DIR")"

runuser -u "$SERVICE_USER" -- bash -lc "cd $(printf '%q' "$WEBSITE_DIR") && npm ci && npm --prefix server ci && npm run build"

systemd-analyze verify "$BUNDLE_DIR/hypertwist-website-auth-server.service"

if [ -f "/etc/nginx/sites-available/$NGINX_CONF_NAME" ]; then
  NGINX_BACKUP="/etc/nginx/sites-available/$NGINX_CONF_NAME.bak-$STAMP"
  cp "/etc/nginx/sites-available/$NGINX_CONF_NAME" "$NGINX_BACKUP"
fi

if [ -f "/var/www/hypertwist/index.html" ]; then
  PLACEHOLDER_BACKUP="/var/www/hypertwist/index.html.pre-same-origin-$STAMP"
  cp "/var/www/hypertwist/index.html" "$PLACEHOLDER_BACKUP"
fi

cp "$BUNDLE_DIR/hypertwist-website-auth-server.service" "/etc/systemd/system/$SERVICE_NAME"
cp "$BUNDLE_DIR/hypertwist.app.conf" "/etc/nginx/sites-available/$NGINX_CONF_NAME"
ln -sf "/etc/nginx/sites-available/$NGINX_CONF_NAME" "/etc/nginx/sites-enabled/$NGINX_CONF_NAME"

systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl restart "$SERVICE_NAME"
nginx -t
systemctl reload nginx

SERVICE_STATUS=$(systemctl is-active "$SERVICE_NAME")
NGINX_STATUS=$(systemctl is-active nginx)

node - "$CHECKOUT_ROOT" "$BUNDLE_DIR" "$SERVICE_STATUS" "$NGINX_STATUS" "$NGINX_BACKUP" "$PLACEHOLDER_BACKUP" <<'__HYPERTWIST_LIVE_DEPLOY_SUMMARY__'
const checkoutRoot = process.argv[2]
const bundleDir = process.argv[3]
const serviceStatus = process.argv[4]
const nginxStatus = process.argv[5]
const nginxBackup = process.argv[6]
const placeholderBackup = process.argv[7]

const summary = {
  ok: serviceStatus === 'active' && nginxStatus === 'active',
  checkoutRoot,
  bundleDir,
  serviceStatus,
  nginxStatus,
  nginxBackup: nginxBackup || null,
  placeholderBackup: placeholderBackup || null,
}

process.stdout.write(\`__HYPERTWIST_LIVE_DEPLOY_SUMMARY__\${JSON.stringify(summary)}\\n\`)
if (!summary.ok) {
  process.exit(1)
}
__HYPERTWIST_LIVE_DEPLOY_SUMMARY__
`
}

export function extractLiveDeploySummary(output) {
  const marker = '__HYPERTWIST_LIVE_DEPLOY_SUMMARY__'
  const lines = String(output || '').split('\n')
  const summaryLine = [...lines].reverse().find((line) => line.startsWith(marker))
  if (!summaryLine) {
    return null
  }

  return JSON.parse(summaryLine.slice(marker.length))
}

export { buildSshArgs }
