#!/bin/sh
# SecuBox HAProxy Controller
# Copyright (C) 2025 CyberMind.fr

# Source OpenWrt functions for UCI iteration
. /lib/functions.sh

CONFIG="haproxy"
LXC_NAME="haproxy"

# Paths
LXC_PATH="/srv/lxc"
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"
DATA_PATH="/srv/haproxy"
SHARE_PATH="/usr/share/haproxy"
CERTS_PATH="$DATA_PATH/certs"
CONFIG_PATH="$DATA_PATH/config"

# Logging
log_info() { echo "[INFO] $*"; logger -t haproxy "$*"; }
log_error() { echo "[ERROR] $*" >&2; logger -t haproxy -p err "$*"; }
log_debug() { [ "$DEBUG" = "1" ] && echo "[DEBUG] $*"; }

# Helpers
require_root() {
	[ "$(id -u)" -eq 0 ] || { log_error "Root required"; exit 1; }
}

has_lxc() { command -v lxc-start >/dev/null 2>&1; }
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }

# Load configuration
load_config() {
	http_port="$(uci_get main.http_port)" || http_port="80"
	https_port="$(uci_get main.https_port)" || https_port="443"
	stats_port="$(uci_get main.stats_port)" || stats_port="8404"
	stats_enabled="$(uci_get main.stats_enabled)" || stats_enabled="1"
	stats_user="$(uci_get main.stats_user)" || stats_user="admin"
	stats_password="$(uci_get main.stats_password)" || stats_password="secubox"
	data_path="$(uci_get main.data_path)" || data_path="$DATA_PATH"
	memory_limit="$(uci_get main.memory_limit)" || memory_limit="256M"
	maxconn="$(uci_get main.maxconn)" || maxconn="4096"
	log_level="$(uci_get main.log_level)" || log_level="warning"

	CERTS_PATH="$data_path/certs"
	CONFIG_PATH="$data_path/config"

	ensure_dir "$data_path"
	ensure_dir "$CERTS_PATH"
	ensure_dir "$CONFIG_PATH"
}

# Usage
usage() {
	cat <<EOF
SecuBox HAProxy Controller

Usage: $(basename $0) <command> [options]

Container Commands:
  install             Setup HAProxy LXC container
  uninstall           Remove container (keeps config)
  update              Update HAProxy in container
  status              Show service status

Configuration:
  generate            Generate haproxy.cfg from UCI
  validate            Validate configuration
  reload              Reload HAProxy config (no downtime)

Virtual Hosts:
  vhost list          List all virtual hosts
  vhost add <domain>  Add virtual host
  vhost remove <domain> Remove virtual host
  vhost sync          Sync vhosts to config

Backends:
  backend list        List all backends
  backend add <name>  Add backend
  backend remove <name> Remove backend

Servers:
  server list <backend>           List servers in backend
  server add <backend> <addr:port> Add server to backend
  server remove <backend> <name>  Remove server

Certificates:
  cert list           List certificates
  cert add <domain>   Request ACME certificate
  cert import <domain> <cert> <key> Import certificate
  cert renew [domain] Renew certificate(s)
  cert remove <domain> Remove certificate

Service Commands:
  service-run         Run in foreground (for init)
  service-stop        Stop service

Stats:
  stats               Show HAProxy stats
  connections         Show active connections

EOF
}

# ===========================================
# LXC Container Management
# ===========================================

lxc_running() {
	lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"
}

lxc_exists() {
	[ -f "$LXC_CONFIG" ] && [ -d "$LXC_ROOTFS" ]
}

lxc_stop() {
	if lxc_running; then
		log_info "Stopping HAProxy container..."
		lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true
		sleep 2
	fi
}

lxc_create_rootfs() {
	log_info "Creating Alpine rootfs for HAProxy..."

	ensure_dir "$LXC_PATH/$LXC_NAME"

	local arch="x86_64"
	case "$(uname -m)" in
		aarch64) arch="aarch64" ;;
		armv7l)  arch="armv7" ;;
	esac

	local alpine_url="https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/$arch/alpine-minirootfs-3.21.2-$arch.tar.gz"
	local rootfs_tar="/tmp/alpine-haproxy.tar.gz"

	log_info "Downloading Alpine rootfs..."
	wget -q -O "$rootfs_tar" "$alpine_url" || {
		log_error "Failed to download Alpine rootfs"
		return 1
	}

	log_info "Extracting rootfs..."
	ensure_dir "$LXC_ROOTFS"
	tar -xzf "$rootfs_tar" -C "$LXC_ROOTFS" || {
		log_error "Failed to extract rootfs"
		return 1
	}
	rm -f "$rootfs_tar"

	# Configure Alpine
	cat > "$LXC_ROOTFS/etc/resolv.conf" << 'EOF'
nameserver 1.1.1.1
nameserver 8.8.8.8
EOF

	cat > "$LXC_ROOTFS/etc/apk/repositories" << 'EOF'
https://dl-cdn.alpinelinux.org/alpine/v3.21/main
https://dl-cdn.alpinelinux.org/alpine/v3.21/community
EOF

	# Install HAProxy
	log_info "Installing HAProxy..."
	chroot "$LXC_ROOTFS" /bin/sh -c "
		apk update
		apk add --no-cache haproxy openssl curl socat lua5.4 lua5.4-socket
	" || {
		log_error "Failed to install HAProxy"
		return 1
	}

	log_info "Rootfs created successfully"
}

lxc_create_config() {
	load_config

	local arch="x86_64"
	case "$(uname -m)" in
		aarch64) arch="aarch64" ;;
		armv7l)  arch="armhf" ;;
	esac

	local mem_bytes=$(echo "$memory_limit" | sed 's/M/000000/;s/G/000000000/')

	cat > "$LXC_CONFIG" << EOF
# HAProxy LXC Configuration
lxc.uts.name = $LXC_NAME
lxc.rootfs.path = dir:$LXC_ROOTFS
lxc.arch = $arch

# Network: use host network for binding ports
lxc.net.0.type = none

# Mount points
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.mount.entry = $data_path opt/haproxy none bind,create=dir 0 0

# Environment
lxc.environment = HTTP_PORT=$http_port
lxc.environment = HTTPS_PORT=$https_port
lxc.environment = STATS_PORT=$stats_port

# Security
lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time

# Resource limits (cgroup2)
lxc.cgroup2.memory.max = $mem_bytes

# Init command
lxc.init.cmd = /opt/start-haproxy.sh
EOF

	log_info "LXC config created"
}

lxc_run() {
	load_config
	lxc_stop

	if ! lxc_exists; then
		log_error "Container not installed. Run: haproxyctl install"
		return 1
	fi

	lxc_create_config

	# Ensure start script exists
	local start_script="$LXC_ROOTFS/opt/start-haproxy.sh"
	cat > "$start_script" << 'STARTEOF'
#!/bin/sh
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

CONFIG_FILE="/opt/haproxy/config/haproxy.cfg"
LOG_FILE="/opt/haproxy/startup.log"

# Log all output
exec >>"$LOG_FILE" 2>&1
echo "=== HAProxy startup: $(date) ==="
echo "Config: $CONFIG_FILE"
ls -la /opt/haproxy/
ls -la /opt/haproxy/certs/ 2>/dev/null || echo "No certs dir"

# Clean up legacy certificate files - only .pem files should exist
# HAProxy loads all files from certs directory, and extra files cause errors
if [ -d "/opt/haproxy/certs" ]; then
    for pem in /opt/haproxy/certs/*.pem; do
        [ -f "$pem" ] || continue
        base="${pem%.pem}"
        # Remove any associated .crt, .key, .fullchain.pem, .crt.key files
        rm -f "${base}.crt" "${base}.key" "${base}.crt.key" "${base}.fullchain.pem" 2>/dev/null
    done
fi

# Wait for config
if [ ! -f "$CONFIG_FILE" ]; then
    echo "[haproxy] Config not found, generating default..."
    mkdir -p /opt/haproxy/config
    cat > "$CONFIG_FILE" << 'CFGEOF'
global
    log stdout format raw local0
    maxconn 4096
    stats socket /var/run/haproxy.sock mode 660 level admin expose-fd listeners
    stats timeout 30s

defaults
    mode http
    log global
    option httplog
    option dontlognull
    timeout connect 5s
    timeout client 30s
    timeout server 30s

frontend stats
    bind *:8404
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if TRUE

frontend http-in
    bind *:80
    mode http
    default_backend fallback

backend fallback
    mode http
    server local 127.0.0.1:8080 check
CFGEOF
fi

# Validate config first
echo "[haproxy] Validating config..."
haproxy -c -f "$CONFIG_FILE"
RC=$?
echo "[haproxy] Validation exit code: $RC"
if [ $RC -ne 0 ]; then
    echo "[haproxy] Config validation failed!"
    exit 1
fi

echo "[haproxy] Starting HAProxy..."
exec haproxy -f "$CONFIG_FILE" -W -db
STARTEOF
	chmod +x "$start_script"

	# Generate config before starting
	generate_config

	log_info "Starting HAProxy container..."
	exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG"
}

lxc_exec() {
	if ! lxc_running; then
		log_error "Container not running"
		return 1
	fi
	lxc-attach -n "$LXC_NAME" -- "$@"
}

# ===========================================
# Configuration Generation
# ===========================================

generate_config() {
	load_config

	local cfg_file="$CONFIG_PATH/haproxy.cfg"

	log_info "Generating HAProxy configuration..."

	# Global section
	cat > "$cfg_file" << EOF
# HAProxy Configuration - Generated by SecuBox
# DO NOT EDIT - Use UCI configuration

global
    log stdout format raw local0 $log_level
    maxconn $maxconn
    stats socket /var/run/haproxy.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 2048

EOF

	# Defaults section
	local mode=$(uci_get defaults.mode) || mode="http"
	local timeout_connect=$(uci_get defaults.timeout_connect) || timeout_connect="5s"
	local timeout_client=$(uci_get defaults.timeout_client) || timeout_client="30s"
	local timeout_server=$(uci_get defaults.timeout_server) || timeout_server="30s"

	cat >> "$cfg_file" << EOF
defaults
    mode $mode
    log global
    option httplog
    option dontlognull
    option forwardfor
    timeout connect $timeout_connect
    timeout client $timeout_client
    timeout server $timeout_server
    timeout http-request 10s
    timeout http-keep-alive 10s
    retries 3

EOF

	# Stats frontend
	if [ "$stats_enabled" = "1" ]; then
		cat >> "$cfg_file" << EOF
frontend stats
    bind *:$stats_port
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth $stats_user:$stats_password
    stats admin if TRUE

EOF
	fi

	# Generate frontends from UCI
	_generate_frontends >> "$cfg_file"

	# Generate backends from UCI
	_generate_backends >> "$cfg_file"

	log_info "Configuration generated: $cfg_file"
}

_generate_frontends() {
	# HTTP Frontend
	cat << EOF
frontend http-in
    bind *:$http_port
    mode http
EOF

	# Add HTTPS redirect rules for vhosts with ssl_redirect
	config_load haproxy
	config_foreach _add_ssl_redirect vhost

	# Add vhost ACLs for HTTP
	config_foreach _add_vhost_acl vhost "http"

	echo "    default_backend fallback"
	echo ""

	# HTTPS Frontend (if certificates exist)
	# Use container path /opt/haproxy/certs/ (not host path)
	local CONTAINER_CERTS_PATH="/opt/haproxy/certs"
	if [ -d "$CERTS_PATH" ] && ls "$CERTS_PATH"/*.pem >/dev/null 2>&1; then
		cat << EOF
frontend https-in
    bind *:$https_port ssl crt $CONTAINER_CERTS_PATH/ alpn h2,http/1.1
    mode http
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Real-IP %[src]
EOF
		# Add vhost ACLs for HTTPS
		config_foreach _add_vhost_acl vhost "https"

		echo "    default_backend fallback"
		echo ""
	fi
}

_add_ssl_redirect() {
	local section="$1"
	local enabled domain ssl_redirect

	config_get enabled "$section" enabled "0"
	[ "$enabled" = "1" ] || return

	config_get domain "$section" domain
	config_get ssl_redirect "$section" ssl_redirect "0"

	[ -n "$domain" ] || return
	[ "$ssl_redirect" = "1" ] || return

	local acl_name=$(echo "$domain" | tr '.' '_' | tr '-' '_')
	echo "    acl host_${acl_name} hdr(host) -i $domain"
	echo "    http-request redirect scheme https code 301 if host_${acl_name} !{ ssl_fc }"
}

_add_vhost_acl() {
	local section="$1"
	local proto="$2"
	local enabled domain backend ssl

	config_get enabled "$section" enabled "0"
	[ "$enabled" = "1" ] || return

	config_get domain "$section" domain
	config_get backend "$section" backend
	config_get ssl "$section" ssl "0"

	[ -n "$domain" ] || return
	[ -n "$backend" ] || return

	# For HTTP frontend, skip SSL-only vhosts
	[ "$proto" = "http" ] && [ "$ssl" = "1" ] && return

	local acl_name=$(echo "$domain" | tr '.' '_' | tr '-' '_')
	echo "    acl host_${acl_name} hdr(host) -i $domain"
	echo "    use_backend $backend if host_${acl_name}"
}

_generate_backends() {
	config_load haproxy

	# Generate each backend from UCI
	config_foreach _generate_backend backend

	# Only add default fallback if no "fallback" backend exists in UCI
	if ! uci -q get haproxy.fallback >/dev/null 2>&1; then
		cat << EOF

backend fallback
    mode http
    http-request deny deny_status 503
EOF
	fi
}

_generate_backend() {
	local section="$1"
	local enabled name mode balance health_check

	config_get enabled "$section" enabled "0"
	[ "$enabled" = "1" ] || return

	config_get name "$section" name "$section"
	config_get mode "$section" mode "http"
	config_get balance "$section" balance "roundrobin"
	config_get health_check "$section" health_check ""

	echo ""
	echo "backend $name"
	echo "    mode $mode"
	echo "    balance $balance"

	[ -n "$health_check" ] && echo "    option $health_check"

	# Check if there are separate server sections for this backend
	local has_server_sections=0
	_check_server_sections() {
		local srv_section="$1"
		local srv_backend
		config_get srv_backend "$srv_section" backend
		config_get srv_enabled "$srv_section" enabled "0"
		if [ "$srv_backend" = "$name" ] && [ "$srv_enabled" = "1" ]; then
			has_server_sections=1
		fi
	}
	config_foreach _check_server_sections server

	# Add inline server ONLY if no separate server sections exist
	# This prevents duplicate server names
	if [ "$has_server_sections" = "0" ]; then
		local server_line
		config_get server_line "$section" server ""
		[ -n "$server_line" ] && echo "    server $server_line"
	fi

	# Add servers from separate server UCI sections
	config_foreach _add_server_to_backend server "$name"
}

_add_server_to_backend() {
	local section="$1"
	local target_backend="$2"
	local backend server_name address port weight check enabled

	config_get backend "$section" backend
	[ "$backend" = "$target_backend" ] || return

	config_get enabled "$section" enabled "0"
	[ "$enabled" = "1" ] || return

	config_get server_name "$section" name "$section"
	config_get address "$section" address
	config_get port "$section" port "80"
	config_get weight "$section" weight "100"
	config_get check "$section" check "1"

	[ -n "$address" ] || return

	local check_opt=""
	[ "$check" = "1" ] && check_opt="check"

	echo "    server $server_name $address:$port weight $weight $check_opt"
}

# ===========================================
# Certificate Management
# ===========================================

# Check if certificate is from Let's Encrypt Production (not Staging)
cert_is_production() {
	local cert_file="$1"
	[ -f "$cert_file" ] || return 1

	# Check the issuer - staging certs have "(STAGING)" in the issuer
	local issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null)
	if echo "$issuer" | grep -qi "staging\|test\|fake"; then
		return 1  # Staging certificate
	fi

	# Check for Let's Encrypt production issuers
	if echo "$issuer" | grep -qiE "Let's Encrypt|R3|R10|R11|E1|E2|ISRG"; then
		return 0  # Production certificate
	fi

	# Check if it's a self-signed or other CA
	return 0  # Assume production for other CAs
}

# Validate certificate publicly using external service
cert_validate_public() {
	local domain="$1"
	local timeout=10

	# Try to connect and verify the certificate
	if command -v curl >/dev/null 2>&1; then
		if curl -sS --max-time "$timeout" -o /dev/null "https://$domain" 2>/dev/null; then
			return 0
		fi
	fi

	# Fallback: use openssl s_client
	if command -v openssl >/dev/null 2>&1; then
		local result=$(echo | timeout "$timeout" openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
		if [ -n "$result" ]; then
			return 0
		fi
	fi

	return 1
}

# Get certificate info
cert_info() {
	local cert_file="$1"
	[ -f "$cert_file" ] || return 1

	local subject=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | sed 's/subject=//')
	local issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | sed 's/issuer=//')
	local not_after=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d= -f2)
	local not_before=$(openssl x509 -in "$cert_file" -noout -startdate 2>/dev/null | cut -d= -f2)

	echo "Subject: $subject"
	echo "Issuer: $issuer"
	echo "Valid From: $not_before"
	echo "Valid Until: $not_after"

	if cert_is_production "$cert_file"; then
		echo "Type: PRODUCTION (publicly trusted)"
	else
		echo "Type: STAGING/TEST (NOT publicly trusted!)"
	fi
}

# Verify and report certificate status
cmd_cert_verify() {
	load_config

	local domain="$1"
	if [ -z "$domain" ]; then
		echo "Usage: haproxyctl cert verify <domain>"
		return 1
	fi

	local cert_file="$CERTS_PATH/$domain.pem"
	if [ ! -f "$cert_file" ]; then
		log_error "Certificate not found: $cert_file"
		return 1
	fi

	echo "Certificate Information for $domain:"
	echo "======================================"
	cert_info "$cert_file"
	echo ""

	# Check if it's production
	if ! cert_is_production "$cert_file"; then
		log_warn "This is a STAGING certificate - NOT trusted by browsers!"
		log_warn "To get a production certificate, ensure staging='0' in config and re-issue"
		return 1
	fi

	# Try public validation
	echo "Public Validation:"
	if cert_validate_public "$domain"; then
		log_info "Certificate is publicly valid and accessible"
		return 0
	else
		log_warn "Could not verify certificate publicly"
		log_warn "Ensure DNS points to this server and port 443 is accessible"
		return 1
	fi
}

cmd_cert_list() {
	load_config

	echo "Certificates in $CERTS_PATH:"
	echo "----------------------------"

	if [ -d "$CERTS_PATH" ]; then
		for cert in "$CERTS_PATH"/*.pem; do
			[ -f "$cert" ] || continue
			local name=$(basename "$cert" .pem)
			local expiry=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
			local type_icon="✅"
			if ! cert_is_production "$cert"; then
				type_icon="⚠️ STAGING"
			fi
			echo "  $name - Expires: ${expiry:-Unknown} $type_icon"
		done
	else
		echo "  No certificates found"
	fi

	# Show current mode
	local staging=$(uci -q get haproxy.acme.staging)
	echo ""
	if [ "$staging" = "1" ]; then
		echo "⚠️  ACME Mode: STAGING (certificates will NOT be trusted by browsers)"
		echo "   To use production: uci set haproxy.acme.staging='0' && uci commit haproxy"
	else
		echo "✅ ACME Mode: PRODUCTION (certificates will be publicly trusted)"
	fi
}

cmd_cert_add() {
	require_root
	load_config

	local domain="$1"
	[ -z "$domain" ] && { log_error "Domain required"; return 1; }

	local email=$(uci_get acme.email)
	local staging=$(uci_get acme.staging)
	local key_type_raw=$(uci_get acme.key_type) || key_type_raw="ec-256"

	# Convert key type for acme.sh (rsa-4096 → 4096, ec-256 stays ec-256)
	local key_type="$key_type_raw"
	case "$key_type_raw" in
		rsa-*) key_type="${key_type_raw#rsa-}" ;;  # rsa-4096 → 4096
		RSA-*) key_type="${key_type_raw#RSA-}" ;;
	esac

	[ -z "$email" ] && { log_error "ACME email not configured. Set in LuCI > Services > HAProxy > Settings"; return 1; }

	# Warn about staging mode
	if [ "$staging" = "1" ]; then
		log_warn "=========================================="
		log_warn "STAGING MODE ENABLED!"
		log_warn "Certificate will NOT be trusted by browsers"
		log_warn "To use production: uci set haproxy.acme.staging='0' && uci commit haproxy"
		log_warn "=========================================="
		sleep 2
	else
		log_info "Using Let's Encrypt PRODUCTION (certificates will be publicly trusted)"
	fi

	log_info "Requesting certificate for $domain..."

	local staging_flag=""
	[ "$staging" = "1" ] && staging_flag="--staging"

	# Find acme.sh - check OpenWrt location first, then PATH
	local ACME_SH=""
	if [ -x "/usr/lib/acme/client/acme.sh" ]; then
		ACME_SH="/usr/lib/acme/client/acme.sh"
	elif command -v acme.sh >/dev/null 2>&1; then
		ACME_SH="acme.sh"
	fi

	if [ -n "$ACME_SH" ]; then
		# Set acme.sh home directory
		export LE_WORKING_DIR="/etc/acme"
		export LE_CONFIG_HOME="/etc/acme"
		ensure_dir "$LE_WORKING_DIR"

		# Register account if needed
		if [ ! -f "$LE_WORKING_DIR/account.conf" ]; then
			log_info "Registering ACME account..."
			"$ACME_SH" --register-account -m "$email" $staging_flag --home "$LE_WORKING_DIR" || true
		fi

		# Check if HAProxy is using the port
		local haproxy_was_running=0
		if lxc_running; then
			log_info "Temporarily stopping HAProxy for certificate issuance..."
			haproxy_was_running=1
			/etc/init.d/haproxy stop 2>/dev/null || true
			sleep 2
		fi

		# Issue certificate using standalone mode
		log_info "Issuing certificate (standalone mode on port $http_port)..."
		local acme_result=0
		"$ACME_SH" --issue -d "$domain" \
			--standalone --httpport "$http_port" \
			--keylength "$key_type" \
			$staging_flag \
			--home "$LE_WORKING_DIR" || acme_result=$?

		# acme.sh returns 0 on success, 2 on "skip/already valid" - both are OK
		# Install the certificate to our certs path
		if [ "$acme_result" -eq 0 ] || [ "$acme_result" -eq 2 ]; then
			log_info "Installing certificate..."
			"$ACME_SH" --install-cert -d "$domain" \
				--home "$LE_WORKING_DIR" \
				--cert-file "$CERTS_PATH/$domain.crt" \
				--key-file "$CERTS_PATH/$domain.key" \
				--fullchain-file "$CERTS_PATH/$domain.fullchain.pem" \
				--reloadcmd "/etc/init.d/haproxy reload" 2>/dev/null || true

			# HAProxy needs combined file: fullchain + private key
			log_info "Creating combined PEM for HAProxy..."
			cat "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.key" > "$CERTS_PATH/$domain.pem"
			chmod 600 "$CERTS_PATH/$domain.pem"

			# Clean up intermediate files - HAProxy only needs the .pem file
			# Keeping these causes issues when HAProxy loads certs from directory
			rm -f "$CERTS_PATH/$domain.crt" "$CERTS_PATH/$domain.key" "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.crt.key" 2>/dev/null
		fi

		# Restart HAProxy if it was running
		if [ "$haproxy_was_running" = "1" ]; then
			log_info "Restarting HAProxy..."
			/etc/init.d/haproxy start 2>/dev/null || true
		fi

		# Check if certificate was created
		if [ ! -f "$CERTS_PATH/$domain.pem" ]; then
			log_error "Certificate issuance failed. Ensure port $http_port is accessible from internet and domain points to this IP."
			return 1
		fi
		log_info "Certificate ready: $CERTS_PATH/$domain.pem"
	elif command -v certbot >/dev/null 2>&1; then
		certbot certonly --standalone -d "$domain" \
			--email "$email" --agree-tos -n \
			--http-01-port "$http_port" $staging_flag || {
			log_error "Certbot failed"
			return 1
		}

		# Copy to HAProxy certs dir
		local le_path="/etc/letsencrypt/live/$domain"
		cat "$le_path/fullchain.pem" "$le_path/privkey.pem" > "$CERTS_PATH/$domain.pem"
	else
		log_error "No ACME client found. Install: opkg install acme acme-acmesh"
		return 1
	fi

	chmod 600 "$CERTS_PATH/$domain.pem"

	# Verify certificate type (production vs staging)
	echo ""
	if cert_is_production "$CERTS_PATH/$domain.pem"; then
		log_info "✅ Certificate is from PRODUCTION CA (publicly trusted)"
	else
		log_warn "⚠️  Certificate is from STAGING CA (NOT publicly trusted!)"
		log_warn "   Browsers will show security warnings for this certificate"
		log_warn "   To get a production certificate:"
		log_warn "   1. uci set haproxy.acme.staging='0'"
		log_warn "   2. uci commit haproxy"
		log_warn "   3. haproxyctl cert remove $domain"
		log_warn "   4. haproxyctl cert add $domain"
	fi

	# Show certificate info
	echo ""
	cert_info "$CERTS_PATH/$domain.pem"

	# Add to UCI
	local section="cert_$(echo "$domain" | tr '.-' '__')"
	uci set haproxy.$section=certificate
	uci set haproxy.$section.domain="$domain"
	uci set haproxy.$section.type="acme"
	uci set haproxy.$section.enabled="1"
	uci commit haproxy

	log_info "Certificate installed for $domain"

	# Offer to verify publicly if production
	if cert_is_production "$CERTS_PATH/$domain.pem"; then
		echo ""
		log_info "To verify the certificate is working publicly, run:"
		log_info "  haproxyctl cert verify $domain"
	fi
}

cmd_cert_import() {
	require_root
	load_config

	local domain="$1"
	local cert_file="$2"
	local key_file="$3"

	[ -z "$domain" ] && { log_error "Domain required"; return 1; }
	[ -z "$cert_file" ] && { log_error "Certificate file required"; return 1; }
	[ -z "$key_file" ] && { log_error "Key file required"; return 1; }

	[ -f "$cert_file" ] || { log_error "Certificate file not found"; return 1; }
	[ -f "$key_file" ] || { log_error "Key file not found"; return 1; }

	# Combine cert and key for HAProxy
	cat "$cert_file" "$key_file" > "$CERTS_PATH/$domain.pem"
	chmod 600 "$CERTS_PATH/$domain.pem"

	# Add to UCI
	uci set haproxy.cert_${domain//[.-]/_}=certificate
	uci set haproxy.cert_${domain//[.-]/_}.domain="$domain"
	uci set haproxy.cert_${domain//[.-]/_}.type="manual"
	uci set haproxy.cert_${domain//[.-]/_}.enabled="1"
	uci commit haproxy

	log_info "Certificate imported for $domain"
}

# ===========================================
# Virtual Host Management
# ===========================================

cmd_vhost_list() {
	load_config

	echo "Virtual Hosts:"
	echo "--------------"

	config_load haproxy
	config_foreach _print_vhost vhost
}

_print_vhost() {
	local section="$1"
	local enabled domain backend ssl ssl_redirect acme

	config_get domain "$section" domain
	config_get backend "$section" backend
	config_get enabled "$section" enabled "0"
	config_get ssl "$section" ssl "0"
	config_get ssl_redirect "$section" ssl_redirect "0"
	config_get acme "$section" acme "0"

	local status="disabled"
	[ "$enabled" = "1" ] && status="enabled"

	local flags=""
	[ "$ssl" = "1" ] && flags="${flags}SSL "
	[ "$ssl_redirect" = "1" ] && flags="${flags}REDIRECT "
	[ "$acme" = "1" ] && flags="${flags}ACME "

	printf "  %-30s -> %-20s [%s] %s\n" "$domain" "$backend" "$status" "$flags"
}

cmd_vhost_add() {
	require_root
	load_config

	local domain="$1"
	local backend="$2"

	[ -z "$domain" ] && { log_error "Domain required"; return 1; }
	[ -z "$backend" ] && backend="fallback"

	local section="vhost_${domain//[.-]/_}"

	uci set haproxy.$section=vhost
	uci set haproxy.$section.domain="$domain"
	uci set haproxy.$section.backend="$backend"
	uci set haproxy.$section.ssl="1"
	uci set haproxy.$section.ssl_redirect="1"
	uci set haproxy.$section.acme="1"
	uci set haproxy.$section.enabled="1"
	uci commit haproxy

	log_info "Virtual host added: $domain -> $backend"
}

cmd_vhost_remove() {
	require_root

	local domain="$1"
	[ -z "$domain" ] && { log_error "Domain required"; return 1; }

	local section="vhost_${domain//[.-]/_}"
	uci delete haproxy.$section 2>/dev/null
	uci commit haproxy

	log_info "Virtual host removed: $domain"
}

# ===========================================
# Backend Management
# ===========================================

cmd_backend_list() {
	load_config

	echo "Backends:"
	echo "---------"

	config_load haproxy
	config_foreach _print_backend backend
}

_print_backend() {
	local section="$1"
	local enabled name mode balance

	config_get name "$section" name "$section"
	config_get enabled "$section" enabled "0"
	config_get mode "$section" mode "http"
	config_get balance "$section" balance "roundrobin"

	local status="disabled"
	[ "$enabled" = "1" ] && status="enabled"

	printf "  %-20s mode=%-6s balance=%-12s [%s]\n" "$name" "$mode" "$balance" "$status"
}

cmd_backend_add() {
	require_root

	local name="$1"
	[ -z "$name" ] && { log_error "Backend name required"; return 1; }

	local section="backend_${name//[.-]/_}"

	uci set haproxy.$section=backend
	uci set haproxy.$section.name="$name"
	uci set haproxy.$section.mode="http"
	uci set haproxy.$section.balance="roundrobin"
	uci set haproxy.$section.enabled="1"
	uci commit haproxy

	log_info "Backend added: $name"
}

cmd_server_add() {
	require_root

	local backend="$1"
	local addr_port="$2"
	local server_name="$3"

	[ -z "$backend" ] && { log_error "Backend name required"; return 1; }
	[ -z "$addr_port" ] && { log_error "Address:port required"; return 1; }

	local address=$(echo "$addr_port" | cut -d: -f1)
	local port=$(echo "$addr_port" | cut -d: -f2)
	[ -z "$port" ] && port="80"
	[ -z "$server_name" ] && server_name="srv_$(echo $address | tr '.' '_')_$port"

	local section="server_${server_name//[.-]/_}"

	uci set haproxy.$section=server
	uci set haproxy.$section.backend="$backend"
	uci set haproxy.$section.name="$server_name"
	uci set haproxy.$section.address="$address"
	uci set haproxy.$section.port="$port"
	uci set haproxy.$section.weight="100"
	uci set haproxy.$section.check="1"
	uci set haproxy.$section.enabled="1"
	uci commit haproxy

	log_info "Server added: $server_name ($address:$port) to backend $backend"
}

# ===========================================
# Commands
# ===========================================

cmd_install() {
	require_root
	load_config

	log_info "Installing HAProxy..."

	has_lxc || { log_error "LXC not installed"; exit 1; }

	if ! lxc_exists; then
		lxc_create_rootfs || exit 1
	fi

	lxc_create_config || exit 1

	log_info "Installation complete!"
	log_info ""
	log_info "Next steps:"
	log_info "  1. Enable: uci set haproxy.main.enabled=1 && uci commit haproxy"
	log_info "  2. Add vhost: haproxyctl vhost add example.com backend_name"
	log_info "  3. Start: /etc/init.d/haproxy start"
}

cmd_status() {
	load_config

	local enabled=$(uci_get main.enabled)
	local running="no"
	lxc_running && running="yes"

	cat << EOF
HAProxy Status
==============
Enabled:     $([ "$enabled" = "1" ] && echo "yes" || echo "no")
Running:     $running
HTTP Port:   $http_port
HTTPS Port:  $https_port
Stats Port:  $stats_port
Stats URL:   http://localhost:$stats_port/stats

Container:   $LXC_NAME
Rootfs:      $LXC_ROOTFS
Config:      $CONFIG_PATH/haproxy.cfg
Certs:       $CERTS_PATH
EOF
}

cmd_reload() {
	require_root

	if ! lxc_running; then
		log_error "Container not running"
		return 1
	fi

	generate_config

	log_info "Reloading HAProxy configuration..."
	# HAProxy in master-worker mode (-W) reloads gracefully on SIGUSR2
	# Fallback to SIGHUP if USR2 fails
	lxc_exec killall -USR2 haproxy 2>/dev/null || \
		lxc_exec killall -HUP haproxy 2>/dev/null || \
		log_error "Could not signal HAProxy for reload"

	log_info "Reload complete"
}

cmd_validate() {
	load_config
	generate_config

	log_info "Validating configuration..."

	if lxc_running; then
		lxc_exec haproxy -c -f /opt/haproxy/config/haproxy.cfg
	else
		# Validate locally if possible
		if [ -f "$CONFIG_PATH/haproxy.cfg" ]; then
			log_info "Config file: $CONFIG_PATH/haproxy.cfg"
			head -50 "$CONFIG_PATH/haproxy.cfg"
		fi
	fi
}

cmd_stats() {
	if ! lxc_running; then
		log_error "Container not running"
		return 1
	fi

	lxc_exec sh -c "echo 'show stat' | socat stdio /var/run/haproxy.sock" 2>/dev/null || \
		curl -s "http://localhost:$stats_port/stats;csv"
}

cmd_service_run() {
	require_root
	load_config

	has_lxc || { log_error "LXC not installed"; exit 1; }
	lxc_run
}

cmd_service_stop() {
	require_root
	lxc_stop
}

# ===========================================
# Main
# ===========================================

case "${1:-}" in
	install)    shift; cmd_install "$@" ;;
	uninstall)  shift; lxc_stop; log_info "Uninstall: rm -rf $LXC_PATH/$LXC_NAME" ;;
	update)     shift; lxc_exec apk update && lxc_exec apk upgrade haproxy ;;
	status)     shift; cmd_status "$@" ;;

	generate)   shift; generate_config "$@" ;;
	validate)   shift; cmd_validate "$@" ;;
	reload)     shift; cmd_reload "$@" ;;

	vhost)
		shift
		case "${1:-}" in
			list)   shift; cmd_vhost_list "$@" ;;
			add)    shift; cmd_vhost_add "$@" ;;
			remove) shift; cmd_vhost_remove "$@" ;;
			sync)   shift; generate_config && cmd_reload ;;
			*)      echo "Usage: haproxyctl vhost {list|add|remove|sync}" ;;
		esac
		;;

	backend)
		shift
		case "${1:-}" in
			list)   shift; cmd_backend_list "$@" ;;
			add)    shift; cmd_backend_add "$@" ;;
			remove) shift; uci delete haproxy.backend_${2//[.-]/_} 2>/dev/null; uci commit haproxy ;;
			*)      echo "Usage: haproxyctl backend {list|add|remove}" ;;
		esac
		;;

	server)
		shift
		case "${1:-}" in
			list)   shift; config_load haproxy; config_foreach _print_server server "$1" ;;
			add)    shift; cmd_server_add "$@" ;;
			remove) shift; uci delete haproxy.server_${3//[.-]/_} 2>/dev/null; uci commit haproxy ;;
			*)      echo "Usage: haproxyctl server {list|add|remove} <backend> [addr:port]" ;;
		esac
		;;

	cert)
		shift
		case "${1:-}" in
			list)   shift; cmd_cert_list "$@" ;;
			add)    shift; cmd_cert_add "$@" ;;
			import) shift; cmd_cert_import "$@" ;;
			renew)  shift; cmd_cert_add "$@" ;;
			verify) shift; cmd_cert_verify "$@" ;;
			remove) shift; rm -f "$CERTS_PATH/$1.pem"; uci delete haproxy.cert_${1//[.-]/_} 2>/dev/null ;;
			*)      echo "Usage: haproxyctl cert {list|add|import|renew|verify|remove}" ;;
		esac
		;;

	stats)       shift; cmd_stats "$@" ;;
	connections) shift; lxc_exec sh -c "echo 'show sess' | socat stdio /var/run/haproxy.sock" ;;

	service-run)  shift; cmd_service_run "$@" ;;
	service-stop) shift; cmd_service_stop "$@" ;;

	shell)       shift; lxc_exec /bin/sh ;;
	exec)        shift; lxc_exec "$@" ;;

	*)           usage ;;
esac
