import fs from 'node:fs'
import path from 'node:path'

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

function normalizeServerNames(serverNames) {
  const names = Array.isArray(serverNames)
    ? serverNames
    : String(serverNames || '').split(',')

  const normalized = names
    .map((item) => normalizeTrimmed(item))
    .filter(Boolean)

  if (normalized.length === 0) {
    throw new Error('At least one server name is required.')
  }

  return normalized
}

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

export function resolveDeploymentRenderOptions(options = {}) {
  const checkoutRoot = normalizeTrimmed(options.checkoutRoot)
  if (!checkoutRoot) {
    throw new Error('checkoutRoot is required.')
  }

  const serviceUser = normalizeTrimmed(options.serviceUser || 'hypertwist')
  const serviceGroup = normalizeTrimmed(options.serviceGroup || serviceUser)
  const serverNames = normalizeServerNames(options.serverNames || ['hypertwist.app', 'www.hypertwist.app'])
  const certificateName = normalizeTrimmed(options.certificateName || serverNames[0])
  const upstreamPort = normalizePositiveInteger(options.upstreamPort || 3011, 'upstreamPort')
  const upstreamName = normalizeTrimmed(options.upstreamName || 'hypertwist_website_auth_server')

  const websiteRoot = normalizeTrimmed(options.websiteRoot || path.join(checkoutRoot, 'website'))
  const serverRoot = normalizeTrimmed(options.serverRoot || path.join(websiteRoot, 'server'))
  const environmentFile = normalizeTrimmed(options.environmentFile || path.join(serverRoot, '.env'))
  const nodeEnv = normalizeTrimmed(options.nodeEnv || 'production')

  return {
    checkoutRoot,
    websiteRoot,
    serverRoot,
    environmentFile,
    serviceUser,
    serviceGroup,
    nodeEnv,
    serverNames,
    certificateName,
    upstreamPort,
    upstreamName,
  }
}

export function renderSystemdService(options = {}) {
  const resolved = resolveDeploymentRenderOptions(options)

  return `[Unit]
Description=HyperTwist same-origin website and auth server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${resolved.serviceUser}
Group=${resolved.serviceGroup}
WorkingDirectory=${resolved.serverRoot}
Environment=NODE_ENV=${resolved.nodeEnv}
EnvironmentFile=${resolved.environmentFile}
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=5
TimeoutStopSec=20
KillSignal=SIGTERM
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
`
}

export function renderNginxConfig(options = {}) {
  const resolved = resolveDeploymentRenderOptions(options)
  const joinedServerNames = resolved.serverNames.join(' ')

  return `map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream ${resolved.upstreamName} {
    server 127.0.0.1:${resolved.upstreamPort};
    keepalive 32;
}

server {
    listen 80;
    listen [::]:80;
    server_name ${joinedServerNames};

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name ${joinedServerNames};

    ssl_certificate /etc/letsencrypt/live/${resolved.certificateName}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${resolved.certificateName}/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 10m;

    location / {
        proxy_pass http://${resolved.upstreamName};
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}
`
}

export function writeRenderedDeploymentFiles({
  systemdOut,
  nginxOut,
  ...options
}) {
  const systemdContent = renderSystemdService(options)
  const nginxContent = renderNginxConfig(options)

  if (systemdOut) {
    fs.mkdirSync(path.dirname(systemdOut), { recursive: true })
    fs.writeFileSync(systemdOut, systemdContent, 'utf8')
  }

  if (nginxOut) {
    fs.mkdirSync(path.dirname(nginxOut), { recursive: true })
    fs.writeFileSync(nginxOut, nginxContent, 'utf8')
  }

  return {
    systemdContent,
    nginxContent,
  }
}
