#!/bin/sh
# SecuBox Hexo CMS Controller
# Copyright (C) 2025 CyberMind.fr
#
# Manages Hexo static site generator in LXC container
# Supports multiple instances on different ports

CONFIG="hexojs"
LXC_NAME="hexojs"

# Paths
LXC_PATH="/srv/lxc"
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"
SHARE_PATH="/usr/share/hexojs"

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

# Helpers
require_root() {
	[ "$(id -u)" -eq 0 ] || {
		log_error "This command requires root privileges"
		exit 1
	}
}

has_lxc() { command -v lxc-start >/dev/null 2>&1; }
has_git() { command -v git >/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 main configuration
load_config() {
	data_path="$(uci_get main.data_path)" || data_path="/srv/hexojs"
	memory_limit="$(uci_get main.memory_limit)" || memory_limit="512M"

	# Legacy support: active_site for default instance
	active_site="$(uci_get main.active_site)" || active_site="default"
	http_port="$(uci_get main.http_port)" || http_port="4000"

	# Gitea config (shared)
	gitea_enabled="$(uci_get gitea.enabled)" || gitea_enabled="0"
	gitea_url="$(uci_get gitea.url)" || gitea_url="http://192.168.255.1:3000"
	gitea_user="$(uci_get gitea.user)" || gitea_user="admin"
	gitea_token="$(uci_get gitea.token)" || gitea_token=""
	gitea_content_repo="$(uci_get gitea.content_repo)" || gitea_content_repo="blog-content"
	gitea_content_branch="$(uci_get gitea.content_branch)" || gitea_content_branch="main"

	ensure_dir "$data_path"
	ensure_dir "$data_path/instances"
	ensure_dir "$data_path/themes"
}

# Load instance configuration
load_instance_config() {
	local instance="$1"
	[ -z "$instance" ] && instance="default"

	current_instance="$instance"

	# Check if instance section exists
	local instance_type=$(uci_get "${instance}")
	if [ "$instance_type" != "instance" ]; then
		# Legacy: check if it's old-style site section or doesn't exist
		if [ -z "$instance_type" ] && [ "$instance" = "default" ]; then
			# Use legacy main config for default
			instance_port="$http_port"
			instance_title="$(uci_get default.title)" || instance_title="My Blog"
			instance_theme="$(uci_get default.theme)" || instance_theme="cybermind"
			instance_enabled="1"
		else
			instance_port=""
			instance_title=""
			instance_theme=""
			instance_enabled="0"
			return 1
		fi
	else
		instance_port="$(uci_get ${instance}.port)" || instance_port="4000"
		instance_title="$(uci_get ${instance}.title)" || instance_title="My Blog"
		instance_theme="$(uci_get ${instance}.theme)" || instance_theme="cybermind"
		instance_enabled="$(uci_get ${instance}.enabled)" || instance_enabled="0"
	fi

	instance_path="$data_path/instances/$instance"
	instance_site="$instance_path/site"

	return 0
}

# Get list of all enabled instances
get_enabled_instances() {
	local instances=""

	# Check for instance sections in UCI
	for section in $(uci show hexojs 2>/dev/null | grep '=instance$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local enabled=$(uci_get "${section}.enabled")
		[ "$enabled" = "1" ] && instances="$instances $section"
	done

	# If no instances defined, check for legacy default
	if [ -z "$instances" ]; then
		if [ -d "$data_path/site" ] || [ -d "$data_path/instances/default/site" ]; then
			instances="default"
		fi
	fi

	echo "$instances"
}

# Usage
usage() {
	cat <<EOF
SecuBox Hexo CMS Controller (Multi-Instance)

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

Container Commands:
  install           Download and setup Hexo LXC container
  uninstall         Remove Hexo container (keeps data)
  update            Update Hexo and dependencies
  status            Show service status

Instance Management:
  instance list              List all instances
  instance create <name>     Create new instance
  instance delete <name>     Delete an instance
  instance start <name>      Start instance server
  instance stop <name>       Stop instance server
  instance status <name>     Show instance status

Site Management (operates on current/specified instance):
  site create [instance]     Create Hexo site for instance
  site delete [instance]     Delete site for instance

Content Commands:
  new post "Title" [instance]   Create new blog post
  new page "Title" [instance]   Create new page
  new draft "Title" [instance]  Create new draft
  list posts [instance]         List all posts
  list drafts [instance]        List all drafts

Build Commands:
  serve [instance]        Start preview server
  build [instance]        Generate static files
  clean [instance]        Clean generated files
  deploy [instance]       Deploy to configured target
  publish [instance]      Copy static files to /www/

Service Commands:
  service-run         Run all instances (for init)
  service-stop        Stop all instances

Gitea Integration:
  gitea setup [instance]   Configure git credentials
  gitea clone [instance]   Clone content repo
  gitea sync [instance]    Pull latest content
  gitea push [instance]    Push changes to Gitea

GitHub Integration:
  github clone <url> [instance] [branch]  Clone from GitHub

Backup/Restore:
  backup [instance] [name]     Create backup
  backup list                  List all backups
  backup delete <name>         Delete a backup
  restore <name> [instance]    Restore from backup

Quick Commands:
  quick-publish [instance]     Clean, build, and publish

User Management:
  user add <name> [password]   Add user with password
  user del <name>              Delete user
  user list                    List all users
  user passwd <name> [pass]    Change user password
  user grant <name> <inst>     Grant access to instance
  user revoke <name> <inst>    Revoke access to instance
  user sync-secubox            Import users from SecuBox/LuCI

Authentication:
  auth enable <instance>       Enable auth for instance
  auth disable <instance>      Disable auth for instance
  auth status [instance]       Show auth status
  auth haproxy <instance>      Generate HAProxy auth ACLs
  auth apply <inst> [domain]   Auto-configure HAProxy with auth

KISS Static Sites (No Hexo Build):
  static create <name>         Create static-only site
  static upload <file> [inst]  Upload HTML/CSS/JS directly
  static publish [instance]    Publish to /www/ for immediate serving
  static quick <file> [inst]   Upload + publish in one step
  static list [instance]       List static files
  static serve [instance]      Serve static files (Python/httpd)
  static delete <name>         Delete static instance

Utility:
  shell               Open shell in container
  logs [instance]     View logs
  exec <cmd>          Execute command in container

Examples:
  hexoctl instance create myblog    # Create new instance
  hexoctl instance start myblog     # Start on configured port
  hexoctl site create myblog        # Initialize Hexo site
  hexoctl new post "Hello" myblog   # Create post in myblog

Configuration:
  /etc/config/hexojs

EOF
}

# Check prerequisites
lxc_check_prereqs() {
	if ! has_lxc; then
		log_error "LXC not installed. Install with: opkg install lxc lxc-common"
		return 1
	fi
	return 0
}

# Create Node.js LXC rootfs from Alpine
lxc_create_rootfs() {
	local rootfs="$LXC_ROOTFS"
	local arch=$(uname -m)

	log_info "Creating Alpine rootfs for Hexo..."

	ensure_dir "$rootfs"

	local alpine_version="3.21"
	case "$arch" in
		x86_64) alpine_arch="x86_64" ;;
		aarch64) alpine_arch="aarch64" ;;
		armv7l) alpine_arch="armv7" ;;
		*) log_error "Unsupported architecture: $arch"; return 1 ;;
	esac

	local alpine_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version}/releases/${alpine_arch}/alpine-minirootfs-${alpine_version}.0-${alpine_arch}.tar.gz"
	local tmpfile="/tmp/alpine-rootfs.tar.gz"

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

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

	# Setup resolv.conf
	cp /etc/resolv.conf "$rootfs/etc/resolv.conf" 2>/dev/null || \
		echo "nameserver 1.1.1.1" > "$rootfs/etc/resolv.conf"

	# Create Hexo directory
	ensure_dir "$rootfs/opt/hexojs"

	log_info "Rootfs created successfully"
	return 0
}

# Create LXC config
lxc_create_config() {
	load_config

	ensure_dir "$(dirname "$LXC_CONFIG")"

	# Convert memory limit to bytes
	local mem_bytes
	case "$memory_limit" in
		*G|*g) mem_bytes=$((${memory_limit%[Gg]} * 1024 * 1024 * 1024)) ;;
		*M|*m) mem_bytes=$((${memory_limit%[Mm]} * 1024 * 1024)) ;;
		*K|*k) mem_bytes=$((${memory_limit%[Kk]} * 1024)) ;;
		*) mem_bytes="$memory_limit" ;;
	esac

	cat > "$LXC_CONFIG" << EOF
# Hexo CMS LXC Configuration (Multi-Instance)
lxc.uts.name = $LXC_NAME
lxc.rootfs.path = dir:$LXC_ROOTFS
lxc.arch = $(uname -m)

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

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

# Environment
lxc.environment = NODE_ENV=production

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

# Resource limits
lxc.cgroup.memory.limit_in_bytes = $mem_bytes

# Init command - multi-instance manager
lxc.init.cmd = /opt/start-hexo-multi.sh
EOF

	log_info "LXC config created"
}

# Create multi-instance startup script
create_startup_script() {
	load_config

	local start_script="$LXC_ROOTFS/opt/start-hexo-multi.sh"
	cat > "$start_script" << 'STARTEOF'
#!/bin/sh
export PATH=/usr/local/bin:/usr/bin:/bin:$PATH
export HOME=/root
export NODE_ENV=production

HEXO_BASE="/opt/hexojs"
INSTANCES_DIR="$HEXO_BASE/instances"
PIDS_DIR="/var/run/hexo"
LOG_DIR="/var/log/hexo"

mkdir -p "$PIDS_DIR" "$LOG_DIR"

# Install dependencies on first run
if [ ! -f /opt/.installed ]; then
    echo "Installing Node.js and Hexo..."
    apk update
    apk add --no-cache nodejs npm git openssh-client
    npm install -g hexo-cli
    touch /opt/.installed
fi

# Function to start a single instance
start_instance() {
    local name="$1"
    local port="$2"
    local site_dir="$INSTANCES_DIR/$name/site"

    [ -d "$site_dir" ] || return 1
    [ -f "$site_dir/package.json" ] || return 1

    echo "Starting instance '$name' on port $port..."

    cd "$site_dir"
    [ -d "node_modules" ] || npm install

    # Start hexo server in background
    nohup npx hexo server -p "$port" -i 0.0.0.0 > "$LOG_DIR/$name.log" 2>&1 &
    echo $! > "$PIDS_DIR/$name.pid"

    echo "Instance '$name' started (PID: $!)"
}

# Function to stop an instance
stop_instance() {
    local name="$1"
    local pidfile="$PIDS_DIR/$name.pid"

    if [ -f "$pidfile" ]; then
        local pid=$(cat "$pidfile")
        if kill -0 "$pid" 2>/dev/null; then
            kill "$pid"
            echo "Stopped instance '$name' (PID: $pid)"
        fi
        rm -f "$pidfile"
    fi
}

# Read instances config from file
INSTANCES_CONF="$HEXO_BASE/instances.conf"

# Main loop - keep container running
if [ -f "$INSTANCES_CONF" ]; then
    echo "Loading instances from config..."
    while IFS=: read -r name port; do
        [ -n "$name" ] && [ -n "$port" ] && start_instance "$name" "$port"
    done < "$INSTANCES_CONF"
fi

# Legacy: check for old-style single site
if [ -d "$HEXO_BASE/site" ] && [ ! -L "$HEXO_BASE/site" ]; then
    echo "Starting legacy site on port ${HEXO_PORT:-4000}..."
    cd "$HEXO_BASE/site"
    [ -d "node_modules" ] || npm install
    nohup npx hexo server -p "${HEXO_PORT:-4000}" -i 0.0.0.0 > "$LOG_DIR/default.log" 2>&1 &
    echo $! > "$PIDS_DIR/default.pid"
fi

# Keep container running
echo "Hexo multi-instance manager running. Instances:"
ls -1 "$PIDS_DIR"/*.pid 2>/dev/null | while read f; do
    name=$(basename "$f" .pid)
    pid=$(cat "$f")
    echo "  - $name (PID: $pid)"
done

# Wait forever
exec tail -f /dev/null
STARTEOF
	chmod +x "$start_script"
}

# Generate instances.conf for container
generate_instances_conf() {
	load_config

	local conf_file="$data_path/instances.conf"
	> "$conf_file"

	for instance in $(get_enabled_instances); do
		load_instance_config "$instance" || continue
		[ "$instance_enabled" = "1" ] || continue
		[ -d "$instance_site" ] || continue
		echo "${instance}:${instance_port}" >> "$conf_file"
	done

	log_debug "Generated instances.conf with $(wc -l < "$conf_file") instances"
}

# Container control
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 Hexo container..."
		lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true
		sleep 2
	fi
}

lxc_run() {
	load_config
	lxc_stop

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

	# Regenerate config
	lxc_create_config
	create_startup_script
	generate_instances_conf

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

lxc_exec() {
	if ! lxc_running; then
		log_error "Container not running. Start with: /etc/init.d/hexojs start"
		return 1
	fi
	lxc-attach -n "$LXC_NAME" -- env PATH=/usr/local/bin:/usr/bin:/bin "$@"
}

# Instance management commands
cmd_instance_list() {
	load_config

	echo "Hexo Instances:"
	echo "---------------"

	local found=0
	for section in $(uci show hexojs 2>/dev/null | grep '=instance$' | cut -d'.' -f2 | cut -d'=' -f1); do
		found=1
		load_instance_config "$section"
		local status="disabled"
		[ "$instance_enabled" = "1" ] && status="enabled"

		local site_status="no site"
		[ -d "$instance_site" ] && site_status="site ready"

		local running=""
		if lxc_running && [ -f "$LXC_ROOTFS/var/run/hexo/${section}.pid" ]; then
			running=" [RUNNING]"
		fi

		printf "  %-15s port:%-5s %s (%s)%s\n" "$section" "$instance_port" "[$status]" "$site_status" "$running"
	done

	# Check for legacy default
	if [ "$found" = "0" ] && [ -d "$data_path/site" ]; then
		echo "  default         port:$http_port [legacy] (site ready)"
	fi

	[ "$found" = "0" ] && [ ! -d "$data_path/site" ] && echo "  (no instances)"
}

cmd_instance_create() {
	require_root
	load_config

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

	# Validate name
	echo "$name" | grep -qE '^[a-z][a-z0-9_]*$' || {
		log_error "Invalid instance name. Use lowercase letters, numbers, underscore. Start with letter."
		return 1
	}

	# Check if exists
	local existing=$(uci_get "$name")
	[ -n "$existing" ] && { log_error "Instance '$name' already exists"; return 1; }

	# Find next available port
	local port=4000
	while uci show hexojs 2>/dev/null | grep -q "port='$port'"; do
		port=$((port + 1))
	done

	# Create UCI config
	uci set hexojs.${name}=instance
	uci set hexojs.${name}.enabled='1'
	uci set hexojs.${name}.port="$port"
	uci set hexojs.${name}.title="$name Blog"
	uci set hexojs.${name}.theme='cybermind'
	uci commit hexojs

	# Create directory
	ensure_dir "$data_path/instances/$name"

	log_info "Instance '$name' created on port $port"
	log_info "Next: hexoctl site create $name"
}

cmd_instance_delete() {
	require_root
	load_config

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

	# Stop instance first
	cmd_instance_stop "$name" 2>/dev/null

	# Remove UCI config
	uci delete hexojs.${name} 2>/dev/null
	uci commit hexojs

	# Optionally remove data (ask user)
	local instance_dir="$data_path/instances/$name"
	if [ -d "$instance_dir" ]; then
		log_warn "Data directory exists: $instance_dir"
		log_info "Remove manually if needed: rm -rf $instance_dir"
	fi

	log_info "Instance '$name' deleted"
}

cmd_instance_start() {
	require_root
	load_config

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

	load_instance_config "$name" || { log_error "Instance '$name' not found"; return 1; }

	if ! lxc_running; then
		log_error "Container not running. Start with: /etc/init.d/hexojs start"
		return 1
	fi

	if [ ! -d "$instance_site" ]; then
		log_error "No site for instance '$name'. Create with: hexoctl site create $name"
		return 1
	fi

	log_info "Starting instance '$name' on port $instance_port..."

	lxc_exec sh -c "
		cd /opt/hexojs/instances/$name/site || exit 1
		[ -d node_modules ] || npm install

		# Kill existing if running
		[ -f /var/run/hexo/$name.pid ] && kill \$(cat /var/run/hexo/$name.pid) 2>/dev/null

		mkdir -p /var/run/hexo /var/log/hexo
		nohup npx hexo server -p $instance_port -i 0.0.0.0 > /var/log/hexo/$name.log 2>&1 &
		echo \$! > /var/run/hexo/$name.pid
		echo \"Started on port $instance_port (PID: \$!)\"
	"

	# Update instances.conf
	generate_instances_conf
}

cmd_instance_stop() {
	require_root
	load_config

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

	if ! lxc_running; then
		return 0
	fi

	log_info "Stopping instance '$name'..."

	lxc_exec sh -c "
		if [ -f /var/run/hexo/$name.pid ]; then
			kill \$(cat /var/run/hexo/$name.pid) 2>/dev/null
			rm -f /var/run/hexo/$name.pid
			echo 'Stopped'
		else
			echo 'Not running'
		fi
	"
}

cmd_instance_status() {
	load_config

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

	load_instance_config "$name" || { log_error "Instance '$name' not found"; return 1; }

	local running="false"
	local pid=""

	if lxc_running; then
		pid=$(lxc_exec cat /var/run/hexo/$name.pid 2>/dev/null)
		if [ -n "$pid" ] && lxc_exec kill -0 "$pid" 2>/dev/null; then
			running="true"
		fi
	fi

	local site_exists="false"
	[ -d "$instance_site" ] && site_exists="true"

	cat << EOF
Instance: $name
--------------
Enabled:  $([ "$instance_enabled" = "1" ] && echo "yes" || echo "no")
Running:  $([ "$running" = "true" ] && echo "yes (PID: $pid)" || echo "no")
Port:     $instance_port
Title:    $instance_title
Theme:    $instance_theme
Site:     $([ "$site_exists" = "true" ] && echo "ready" || echo "not created")
Path:     $instance_path

EOF

	if [ "$running" = "true" ]; then
		echo "URL: http://$(uci -q get network.lan.ipaddr || echo 'localhost'):$instance_port"
	fi
}

# Commands
cmd_install() {
	require_root
	load_config

	log_info "Installing Hexo CMS..."

	lxc_check_prereqs || exit 1

	if ! lxc_exists; then
		lxc_create_rootfs || exit 1
	fi

	lxc_create_config || exit 1
	create_startup_script

	# Copy theme
	if [ -d "$SHARE_PATH/themes/cybermind" ]; then
		log_info "Installing CyberMind theme..."
		ensure_dir "$data_path/themes"
		cp -r "$SHARE_PATH/themes/cybermind" "$data_path/themes/"
	fi

	# Copy scaffolds
	if [ -d "$SHARE_PATH/scaffolds" ]; then
		ensure_dir "$data_path/scaffolds"
		cp -r "$SHARE_PATH/scaffolds/"* "$data_path/scaffolds/"
	fi

	log_info "Installation complete!"
	log_info ""
	log_info "Next steps:"
	log_info "  1. Create instance: hexoctl instance create myblog"
	log_info "  2. Create site: hexoctl site create myblog"
	log_info "  3. Start service: /etc/init.d/hexojs start"
}

cmd_uninstall() {
	require_root

	log_info "Uninstalling Hexo CMS..."

	/etc/init.d/hexojs stop 2>/dev/null || true
	/etc/init.d/hexojs disable 2>/dev/null || true

	lxc_stop

	if [ -d "$LXC_PATH/$LXC_NAME" ]; then
		rm -rf "$LXC_PATH/$LXC_NAME"
		log_info "Container removed"
	fi

	log_info "Hexo CMS uninstalled"
	log_info "Data preserved in: $(uci_get main.data_path)"
}

cmd_update() {
	require_root
	load_config

	log_info "Updating Hexo CMS..."

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

	lxc_exec sh -c 'npm update -g hexo-cli'

	# Update each instance
	for instance in $(get_enabled_instances); do
		load_instance_config "$instance" || continue
		if [ -d "$instance_site" ]; then
			log_info "Updating instance '$instance'..."
			lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && npm update"
		fi
	done

	log_info "Update complete!"
}

cmd_status() {
	load_config

	local enabled="$(uci_get main.enabled)"
	local running="false"
	lxc_running && running="true"

	cat << EOF
Hexo CMS Status
===============
Enabled:     $([ "$enabled" = "1" ] && echo "yes" || echo "no")
Running:     $([ "$running" = "true" ] && echo "yes" || echo "no")
Data Path:   $data_path
Memory:      $memory_limit
Container:   $LXC_NAME

Instances:
EOF

	for instance in $(get_enabled_instances); do
		load_instance_config "$instance" || continue
		local status="stopped"
		if [ "$running" = "true" ]; then
			local pid=$(lxc_exec cat /var/run/hexo/$instance.pid 2>/dev/null)
			[ -n "$pid" ] && status="running:$instance_port"
		fi
		printf "  %-15s %s\n" "$instance" "[$status]"
	done
}

# Site management (instance-aware)
cmd_site_create() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || {
		# Auto-create instance if it doesn't exist
		log_info "Creating instance '$instance'..."
		cmd_instance_create "$instance"
		load_instance_config "$instance"
	}

	log_info "Creating Hexo site for instance: $instance"

	if [ -d "$instance_site" ]; then
		log_error "Site already exists at $instance_site"
		return 1
	fi

	ensure_dir "$instance_path"

	# Start container if not running
	local was_stopped=0
	if ! lxc_running; then
		was_stopped=1
		lxc_create_config
		create_startup_script
		lxc-start -n "$LXC_NAME" -d -f "$LXC_CONFIG"
		sleep 5
	fi

	# Create site in container
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance && hexo init site" || {
		log_error "Failed to initialize site"
		return 1
	}

	# Install dependencies
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && npm install" || {
		log_error "Failed to install dependencies"
		return 1
	}

	# Install deploy plugin
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && npm install hexo-deployer-git --save" || true

	# Install theme
	if [ -d "$data_path/themes/cybermind" ]; then
		log_info "Installing CyberMind theme..."
		cp -r "$data_path/themes/cybermind" "$instance_site/themes/"
		sed -i 's/^theme:.*/theme: cybermind/' "$instance_site/_config.yml"
	fi

	# Copy scaffolds
	if [ -d "$data_path/scaffolds" ]; then
		cp -r "$data_path/scaffolds/"* "$instance_site/scaffolds/" 2>/dev/null || true
	fi

	# Update config
	if [ -f "$instance_site/_config.yml" ]; then
		sed -i "s/^title:.*/title: $instance_title/" "$instance_site/_config.yml"
		sed -i "s|^url:.*|url: http://localhost:$instance_port|" "$instance_site/_config.yml"
	fi

	if [ "$was_stopped" = "1" ]; then
		lxc_stop
	fi

	log_info "Site created for instance '$instance'!"
	log_info "Start with: hexoctl instance start $instance"
}

cmd_site_delete() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	if [ ! -d "$instance_site" ]; then
		log_error "No site exists for instance '$instance'"
		return 1
	fi

	cmd_instance_stop "$instance" 2>/dev/null

	rm -rf "$instance_site"
	log_info "Site deleted for instance '$instance'"
}

# Content commands (instance-aware)
cmd_new_post() {
	require_root
	load_config

	local title="$1"
	local instance="${2:-default}"

	[ -z "$title" ] && { log_error "Title required"; return 1; }

	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo new post \"$title\""
}

cmd_new_page() {
	require_root
	load_config

	local title="$1"
	local instance="${2:-default}"

	[ -z "$title" ] && { log_error "Title required"; return 1; }

	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo new page \"$title\""
}

cmd_new_draft() {
	require_root
	load_config

	local title="$1"
	local instance="${2:-default}"

	[ -z "$title" ] && { log_error "Title required"; return 1; }

	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo new draft \"$title\""
}

cmd_list_posts() {
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local posts_dir="$instance_site/source/_posts"
	[ -d "$posts_dir" ] || { echo "[]"; return; }

	echo "["
	local first=1
	for f in "$posts_dir"/*.md; do
		[ -f "$f" ] || continue
		local filename=$(basename "$f")
		local slug="${filename%.md}"
		local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")

		[ "$first" = "1" ] || echo ","
		first=0
		echo "  {\"slug\": \"$slug\", \"title\": \"$title\"}"
	done
	echo "]"
}

cmd_list_drafts() {
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local drafts_dir="$instance_site/source/_drafts"
	[ -d "$drafts_dir" ] || { echo "[]"; return; }

	echo "["
	local first=1
	for f in "$drafts_dir"/*.md; do
		[ -f "$f" ] || continue
		local filename=$(basename "$f")
		local slug="${filename%.md}"
		local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")

		[ "$first" = "1" ] || echo ","
		first=0
		echo "  {\"slug\": \"$slug\", \"title\": \"$title\"}"
	done
	echo "]"
}

# Build commands (instance-aware)
cmd_serve() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	log_info "Starting preview server for '$instance' on port $instance_port..."
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo server -p $instance_port -i 0.0.0.0"
}

cmd_build() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	log_info "Generating static files for '$instance'..."
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo generate"
	log_info "Build complete!"
}

cmd_clean() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	log_info "Cleaning generated files for '$instance'..."
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo clean"
}

cmd_publish() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local public_dir="$instance_site/public"
	local portal_path="$(uci_get ${instance}.publish_path)" || portal_path="/www/${instance}"

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

	# Calculate web root
	local web_root="${portal_path#/www}"
	[ -z "$web_root" ] && web_root="/"
	[ "${web_root%/}" = "$web_root" ] && web_root="$web_root/"

	log_info "Setting root to: $web_root"

	# Update config
	sed -i "s|^root:.*|root: $web_root|" "$instance_site/_config.yml"

	log_info "Regenerating..."
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo clean && hexo generate"

	[ -d "$public_dir" ] || { log_error "Build failed"; return 1; }

	log_info "Publishing to $portal_path..."
	ensure_dir "$portal_path"
	rsync -av --delete "$public_dir/" "$portal_path/"

	log_info "Published $(find "$portal_path" -type f | wc -l) files"
}

cmd_logs() {
	load_config

	local instance="${1:-default}"

	if lxc_running; then
		lxc_exec cat /var/log/hexo/$instance.log 2>/dev/null || echo "No logs for '$instance'"
	else
		echo "Container not running"
	fi
}

cmd_shell() {
	require_root

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

	lxc-attach -n "$LXC_NAME" -- /bin/sh
}

cmd_exec() {
	require_root
	lxc_exec "$@"
}

cmd_service_run() {
	require_root
	load_config

	lxc_check_prereqs || exit 1
	lxc_run
}

cmd_service_stop() {
	require_root
	lxc_stop
}

# Gitea integration (instance-aware)
cmd_gitea_setup() {
	require_root
	load_config

	local instance="${1:-default}"

	if [ -z "$gitea_token" ]; then
		log_error "Gitea token not configured"
		return 1
	fi

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

	log_info "Configuring git credentials..."

	local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')

	lxc_exec sh -c "
		export PATH=/usr/local/bin:\$PATH
		git config --global user.name '$gitea_user'
		git config --global user.email '${gitea_user}@localhost'
		git config --global credential.helper store

		rm -rf ~/.git-credentials
		cat > ~/.git-credentials << CRED
https://${gitea_user}:${gitea_token}@${gitea_host}
http://${gitea_user}:${gitea_token}@${gitea_host}
CRED
		chmod 600 ~/.git-credentials
	"

	log_info "Git credentials configured"
}

cmd_gitea_clone() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	if [ "$gitea_enabled" != "1" ]; then
		log_error "Gitea integration not enabled"
		return 1
	fi

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

	local content_path="$instance_path/content"

	if [ -d "$content_path/.git" ]; then
		log_info "Content repo already cloned, pulling..."
		cd "$content_path" && git pull
	else
		log_info "Cloning content repo..."

		local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')
		local clone_url="http://${gitea_user}:${gitea_token}@${gitea_host}/${gitea_user}/${gitea_content_repo}.git"

		ensure_dir "$(dirname "$content_path")"
		rm -rf "$content_path"

		git clone -b "$gitea_content_branch" "$clone_url" "$content_path" || {
			log_error "Failed to clone"
			return 1
		}
	fi

	# Check if content is a full hexo site
	if [ -f "$content_path/package.json" ] && [ -d "$content_path/source" ]; then
		log_info "Content is a complete Hexo site, linking..."
		lxc_exec sh -c "
			rm -rf /opt/hexojs/instances/$instance/site
			ln -sf /opt/hexojs/instances/$instance/content /opt/hexojs/instances/$instance/site
			cd /opt/hexojs/instances/$instance/site && npm install
		"
	fi

	log_info "Content cloned for instance '$instance'"
}

cmd_gitea_sync() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local content_path="$instance_path/content"

	[ -d "$content_path/.git" ] || { log_error "Content not cloned"; return 1; }

	log_info "Pulling latest content..."
	cd "$content_path" && git pull

	log_info "Content synced for '$instance'"
}

cmd_gitea_push() {
	require_root
	load_config

	local instance="${1:-default}"
	local message="${2:-Auto-commit from SecuBox}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local content_path="$instance_path/content"
	[ -d "$content_path/.git" ] || { log_error "Content not cloned"; return 1; }

	log_info "Pushing changes for '$instance'..."
	cd "$content_path"
	git add -A
	git commit -m "$message" 2>/dev/null || log_info "Nothing to commit"
	git push

	log_info "Content pushed for '$instance'"
}

# GitHub integration (public repos)
cmd_github_clone() {
	require_root
	load_config

	local repo_url="$1"
	local instance="${2:-default}"
	local branch="${3:-main}"

	[ -z "$repo_url" ] && { log_error "GitHub repo URL required"; return 1; }
	load_instance_config "$instance" || {
		log_info "Creating instance '$instance'..."
		cmd_instance_create "$instance"
		load_instance_config "$instance"
	}

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

	local content_path="$instance_path/content"
	log_info "Cloning from GitHub: $repo_url (branch: $branch)"

	ensure_dir "$(dirname "$content_path")"
	rm -rf "$content_path"

	git clone -b "$branch" "$repo_url" "$content_path" || {
		log_error "Failed to clone from GitHub"
		return 1
	}

	# Check if content is a full hexo site
	if [ -f "$content_path/package.json" ] && [ -d "$content_path/source" ]; then
		log_info "Content is a complete Hexo site, linking..."
		lxc_exec sh -c "
			rm -rf /opt/hexojs/instances/$instance/site
			ln -sf /opt/hexojs/instances/$instance/content /opt/hexojs/instances/$instance/site
			cd /opt/hexojs/instances/$instance/site && npm install
		"
	fi

	log_info "GitHub repo cloned for instance '$instance'"
}

# Backup/Restore commands
cmd_backup() {
	require_root
	load_config

	local instance="${1:-default}"
	local backup_name="${2:-$(date +%Y%m%d-%H%M%S)}"

	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local backup_dir="$data_path/backups"
	ensure_dir "$backup_dir"

	local backup_file="$backup_dir/${instance}_${backup_name}.tar.gz"

	if [ ! -d "$instance_site" ]; then
		log_error "No site to backup for instance '$instance'"
		return 1
	fi

	log_info "Creating backup: $backup_file"

	# Backup site directory and config
	tar -czf "$backup_file" \
		-C "$instance_path" site \
		-C /etc/config hexojs 2>/dev/null || {
		log_error "Backup failed"
		return 1
	}

	local size=$(du -h "$backup_file" | cut -f1)
	log_info "Backup created: $backup_file ($size)"
}

cmd_restore() {
	require_root
	load_config

	local backup_name="$1"
	local instance="${2:-default}"

	[ -z "$backup_name" ] && { log_error "Backup name required"; return 1; }

	local backup_dir="$data_path/backups"
	local backup_file="$backup_dir/$backup_name"

	# Try with and without .tar.gz extension
	[ -f "$backup_file" ] || backup_file="$backup_dir/${backup_name}.tar.gz"
	[ -f "$backup_file" ] || backup_file="$backup_dir/${instance}_${backup_name}.tar.gz"
	[ -f "$backup_file" ] || { log_error "Backup not found: $backup_name"; return 1; }

	load_instance_config "$instance" || {
		log_info "Creating instance '$instance'..."
		cmd_instance_create "$instance"
		load_instance_config "$instance"
	}

	# Stop instance if running
	cmd_instance_stop "$instance" 2>/dev/null

	log_info "Restoring from: $backup_file"

	# Remove existing site
	rm -rf "$instance_site"
	ensure_dir "$instance_path"

	# Extract backup
	tar -xzf "$backup_file" -C "$instance_path" || {
		log_error "Restore failed"
		return 1
	}

	log_info "Backup restored for instance '$instance'"
	log_info "Start with: hexoctl instance start $instance"
}

cmd_backup_list() {
	load_config

	local backup_dir="$data_path/backups"
	[ -d "$backup_dir" ] || { echo "[]"; return; }

	echo "["
	local first=1
	for f in "$backup_dir"/*.tar.gz; do
		[ -f "$f" ] || continue
		local name=$(basename "$f" .tar.gz)
		local size=$(du -h "$f" | cut -f1)
		local ts=$(stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null)

		[ "$first" = "1" ] || echo ","
		first=0
		printf '  {"name": "%s", "size": "%s", "timestamp": %s}' "$name" "$size" "${ts:-0}"
	done
	echo ""
	echo "]"
}

cmd_backup_delete() {
	require_root
	load_config

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

	local backup_dir="$data_path/backups"
	local backup_file="$backup_dir/$backup_name"

	[ -f "$backup_file" ] || backup_file="$backup_dir/${backup_name}.tar.gz"
	[ -f "$backup_file" ] || { log_error "Backup not found: $backup_name"; return 1; }

	rm -f "$backup_file"
	log_info "Backup deleted: $backup_name"
}

# Quick publish (build + deploy in one command)
cmd_quick_publish() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

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

	log_info "Quick publish for '$instance'..."

	# Clean, build, publish
	lxc_exec sh -c "cd /opt/hexojs/instances/$instance/site && hexo clean && hexo generate" || {
		log_error "Build failed"
		return 1
	}

	cmd_publish "$instance"
	log_info "Quick publish complete!"
}

# Status JSON (for RPCD)
cmd_status_json() {
	load_config

	local enabled="$(uci_get main.enabled)"
	local running="false"
	lxc_running && running="true"

	local installed="false"
	lxc_exists && installed="true"

	cat << EOF
{
  "enabled": $([ "$enabled" = "1" ] && echo true || echo false),
  "running": $running,
  "installed": $installed,
  "data_path": "$data_path",
  "memory_limit": "$memory_limit",
  "instances": [
EOF

	local first=1
	for instance in $(get_enabled_instances); do
		load_instance_config "$instance" || continue
		local inst_running="false"
		if [ "$running" = "true" ]; then
			local pid=$(lxc_exec cat /var/run/hexo/$instance.pid 2>/dev/null)
			[ -n "$pid" ] && inst_running="true"
		fi
		local site_exists="false"
		[ -d "$instance_site" ] && site_exists="true"

		[ "$first" = "1" ] || printf ","
		first=0
		cat << EOF
    {
      "name": "$instance",
      "enabled": $([ "$instance_enabled" = "1" ] && echo true || echo false),
      "running": $inst_running,
      "port": $instance_port,
      "title": "$instance_title",
      "theme": "$instance_theme",
      "site_exists": $site_exists
    }
EOF
	done

	echo "  ]"
	echo "}"
}

# ===============================================
# User Management Commands
# ===============================================

HTPASSWD_FILE="/etc/hexojs/htpasswd"

# Generate password hash (apr1 format for HAProxy)
generate_password_hash() {
	local password="$1"
	# Use openssl for apr1 hash
	openssl passwd -apr1 "$password" 2>/dev/null || \
		openssl passwd -1 "$password" 2>/dev/null || \
		echo "$password"  # fallback to plain (not recommended)
}

cmd_user_add() {
	require_root

	local username="$1"
	local password="$2"

	[ -z "$username" ] && { log_error "Username required"; return 1; }

	# Validate username
	echo "$username" | grep -qE '^[a-z][a-z0-9_]*$' || {
		log_error "Invalid username. Use lowercase letters, numbers, underscore."
		return 1
	}

	# Check if user exists
	local existing=$(uci_get "user_$username")
	[ -n "$existing" ] && { log_error "User '$username' already exists"; return 1; }

	# Generate password if not provided
	[ -z "$password" ] && {
		password=$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 12)
		log_info "Generated password: $password"
	}

	# Create password hash
	local hash=$(generate_password_hash "$password")

	# Create UCI user section
	uci set hexojs.user_${username}=user
	uci set hexojs.user_${username}.password_hash="$hash"
	uci set hexojs.user_${username}.role='editor'
	uci commit hexojs

	# Update htpasswd file
	cmd_update_htpasswd

	log_info "User '$username' created"
}

cmd_user_del() {
	require_root

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

	# Remove from UCI
	uci delete hexojs.user_${username} 2>/dev/null || {
		log_error "User '$username' not found"
		return 1
	}
	uci commit hexojs

	# Update htpasswd file
	cmd_update_htpasswd

	log_info "User '$username' deleted"
}

# Sync users from SecuBox/LuCI (rpcd config)
cmd_user_sync_secubox() {
	require_root

	log_info "Syncing users from SecuBox/LuCI..."

	local added=0
	local skipped=0

	# Read all login sections from rpcd config
	for section in $(uci show rpcd 2>/dev/null | grep '=login$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local username=$(uci -q get rpcd.${section}.username)
		[ -z "$username" ] && continue

		# Skip if already exists in hexojs
		local existing=$(uci_get "user_$username" 2>/dev/null)
		if [ -n "$existing" ]; then
			log_info "  Skip: $username (already exists)"
			skipped=$((skipped + 1))
			continue
		fi

		# Get password - format is $p$<system_user> or direct hash
		local pw_ref=$(uci -q get rpcd.${section}.password)

		# If $p$username format, get hash from /etc/shadow
		local hash=""
		if echo "$pw_ref" | grep -q '^\$p\$'; then
			local sys_user="${pw_ref#\$p\$}"
			# Extract hash from shadow file
			local shadow_hash=$(grep "^${sys_user}:" /etc/shadow 2>/dev/null | cut -d: -f2)
			if [ -n "$shadow_hash" ] && [ "$shadow_hash" != "*" ] && [ "$shadow_hash" != "x" ]; then
				hash="$shadow_hash"
			fi
		else
			# Direct hash in rpcd config
			hash="$pw_ref"
		fi

		if [ -z "$hash" ]; then
			log_warn "  Skip: $username (no password hash found)"
			skipped=$((skipped + 1))
			continue
		fi

		# Create HexoJS user with the same hash
		uci set hexojs.user_${username}=user
		uci set hexojs.user_${username}.password_hash="$hash"
		uci set hexojs.user_${username}.role='editor'
		uci set hexojs.user_${username}.source='secubox'

		log_info "  Added: $username"
		added=$((added + 1))
	done

	uci commit hexojs

	# Update htpasswd file
	cmd_update_htpasswd

	log_info "Sync complete: $added added, $skipped skipped"
}

cmd_user_list() {
	load_config

	echo "HexoJS Users:"
	echo "-------------"

	local found=0
	for section in $(uci show hexojs 2>/dev/null | grep '=user$' | cut -d'.' -f2 | cut -d'=' -f1); do
		found=1
		local username="${section#user_}"
		local role=$(uci_get "${section}.role")
		local instances=$(uci_get "${section}.instances")

		printf "  %-15s role:%-8s instances: %s\n" "$username" "${role:-editor}" "${instances:-all}"
	done

	[ "$found" = "0" ] && echo "  (no users)"
}

cmd_user_passwd() {
	require_root

	local username="$1"
	local password="$2"

	[ -z "$username" ] && { log_error "Username required"; return 1; }

	# Check if user exists
	local existing=$(uci_get "user_$username")
	[ -z "$existing" ] && { log_error "User '$username' not found"; return 1; }

	# Generate password if not provided
	[ -z "$password" ] && {
		password=$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 12)
		log_info "Generated password: $password"
	}

	# Create password hash
	local hash=$(generate_password_hash "$password")

	# Update UCI
	uci set hexojs.user_${username}.password_hash="$hash"
	uci commit hexojs

	# Update htpasswd file
	cmd_update_htpasswd

	log_info "Password changed for '$username'"
}

cmd_user_grant() {
	require_root

	local username="$1"
	local instance="$2"

	[ -z "$username" ] && { log_error "Username required"; return 1; }
	[ -z "$instance" ] && { log_error "Instance required"; return 1; }

	# Check if user exists
	local existing=$(uci_get "user_$username")
	[ -z "$existing" ] && { log_error "User '$username' not found"; return 1; }

	# Add instance to user's list
	uci add_list hexojs.user_${username}.instances="$instance"
	uci commit hexojs

	log_info "Granted '$username' access to '$instance'"
}

cmd_user_revoke() {
	require_root

	local username="$1"
	local instance="$2"

	[ -z "$username" ] && { log_error "Username required"; return 1; }
	[ -z "$instance" ] && { log_error "Instance required"; return 1; }

	# Remove instance from user's list
	uci del_list hexojs.user_${username}.instances="$instance" 2>/dev/null
	uci commit hexojs

	log_info "Revoked '$username' access from '$instance'"
}

cmd_update_htpasswd() {
	local htpasswd_dir=$(dirname "$HTPASSWD_FILE")
	ensure_dir "$htpasswd_dir"

	> "$HTPASSWD_FILE"

	for section in $(uci show hexojs 2>/dev/null | grep '=user$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local username="${section#user_}"
		local hash=$(uci_get "${section}.password_hash")
		[ -n "$hash" ] && echo "${username}:${hash}" >> "$HTPASSWD_FILE"
	done

	chmod 600 "$HTPASSWD_FILE"
}

# ===============================================
# Authentication Commands
# ===============================================

cmd_auth_enable() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	uci set hexojs.${instance}.auth_enabled='1'
	uci commit hexojs

	log_info "Authentication enabled for '$instance'"
	log_info "Configure HAProxy with: hexoctl auth haproxy $instance"
}

cmd_auth_disable() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	uci set hexojs.${instance}.auth_enabled='0'
	uci commit hexojs

	log_info "Authentication disabled for '$instance'"
}

cmd_auth_status() {
	load_config

	local instance="${1:-}"

	if [ -n "$instance" ]; then
		load_instance_config "$instance" || { log_error "Instance not found"; return 1; }
		local auth_enabled=$(uci_get "${instance}.auth_enabled")
		local auth_users=$(uci_get "${instance}.auth_users")

		echo "Instance: $instance"
		echo "Auth Enabled: $([ "$auth_enabled" = "1" ] && echo "yes" || echo "no")"
		echo "Allowed Users: ${auth_users:-all}"
	else
		echo "Authentication Status:"
		echo "---------------------"

		for inst in $(get_enabled_instances); do
			load_instance_config "$inst" || continue
			local auth_enabled=$(uci_get "${inst}.auth_enabled")
			printf "  %-15s %s\n" "$inst" "$([ "$auth_enabled" = "1" ] && echo "[AUTH]" || echo "[PUBLIC]")"
		done

		echo ""
		echo "Users:"
		cmd_user_list | tail -n +3
	fi
}

cmd_auth_haproxy() {
	require_root
	load_config

	local instance="${1:-default}"
	load_instance_config "$instance" || { log_error "Instance not found"; return 1; }

	local auth_enabled=$(uci_get "${instance}.auth_enabled")
	[ "$auth_enabled" != "1" ] && { log_error "Auth not enabled for '$instance'"; return 1; }

	# Update htpasswd
	cmd_update_htpasswd

	local domain=$(uci_get "${instance}.domain")
	[ -z "$domain" ] && domain="${instance}.hexo.local"

	log_info "HAProxy configuration for authenticated instance '$instance':"
	echo ""
	echo "# Add to HAProxy frontend https-in:"
	echo "    acl host_${instance}_hexo hdr(host) -i ${domain}"
	echo "    http-request auth realm \"SecuBox HexoJS\" if host_${instance}_hexo !{ http_auth(hexojs_users) }"
	echo "    use_backend hexo_${instance} if host_${instance}_hexo"
	echo ""
	echo "# Add userlist section:"
	echo "userlist hexojs_users"

	for section in $(uci show hexojs 2>/dev/null | grep '=user$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local username="${section#user_}"
		local hash=$(uci_get "${section}.password_hash")
		[ -n "$hash" ] && echo "    user ${username} password ${hash}"
	done

	echo ""
	echo "# Add backend:"
	echo "backend hexo_${instance}"
	echo "    mode http"
	echo "    server hexo 127.0.0.1:${instance_port} check"
	echo ""

	log_info "Copy the above to your HAProxy config and reload"
}

# Generate full HAProxy config for all authenticated instances
cmd_auth_haproxy_all() {
	require_root
	load_config

	echo "# HexoJS Authenticated Instances - HAProxy Config"
	echo "# Generated: $(date)"
	echo ""

	echo "userlist hexojs_users"
	for section in $(uci show hexojs 2>/dev/null | grep '=user$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local username="${section#user_}"
		local hash=$(uci_get "${section}.password_hash")
		[ -n "$hash" ] && echo "    user ${username} password ${hash}"
	done
	echo ""

	for inst in $(get_enabled_instances); do
		load_instance_config "$inst" || continue
		local auth_enabled=$(uci_get "${inst}.auth_enabled")
		[ "$auth_enabled" != "1" ] && continue

		local domain=$(uci_get "${inst}.domain")
		[ -z "$domain" ] && domain="${inst}.hexo.local"

		echo "# Instance: $inst (port $instance_port)"
		echo "acl host_${inst}_hexo hdr(host) -i ${domain}"
		echo "http-request auth realm \"SecuBox HexoJS\" if host_${inst}_hexo !{ http_auth(hexojs_users) }"
		echo "use_backend hexo_${inst} if host_${inst}_hexo"
		echo ""
		echo "backend hexo_${inst}"
		echo "    mode http"
		echo "    server hexo 127.0.0.1:${instance_port} check"
		echo ""
	done
}

# Apply auth configuration to HAProxy (auto-configure)
cmd_auth_apply() {
	require_root
	load_config

	local instance="${1:-}"
	local domain="${2:-}"

	[ -z "$instance" ] && { log_error "Instance name required"; return 1; }
	[ -z "$domain" ] && domain="${instance}.secubox.local"

	local auth_enabled=$(uci_get "${instance}.auth_enabled")
	[ "$auth_enabled" != "1" ] && {
		log_info "Enabling auth for '$instance'..."
		uci set hexojs.${instance}.auth_enabled='1'
		uci commit hexojs
	}

	# Save domain
	uci set hexojs.${instance}.domain="$domain"
	uci commit hexojs

	# Update htpasswd
	cmd_update_htpasswd

	# Write userlist file for HAProxy include
	local userlist_file="/srv/haproxy/config/hexojs-users.cfg"
	log_info "Writing userlist to $userlist_file..."

	cat > "$userlist_file" << 'USERLIST_HEAD'
# HexoJS Users - Auto-generated by hexoctl
# Include this in haproxy.cfg before frontends
USERLIST_HEAD
	echo "userlist hexojs_users" >> "$userlist_file"

	for section in $(uci show hexojs 2>/dev/null | grep '=user$' | cut -d'.' -f2 | cut -d'=' -f1); do
		local username="${section#user_}"
		local hash=$(uci_get "${section}.password_hash")
		[ -n "$hash" ] && echo "    user ${username} password ${hash}" >> "$userlist_file"
	done

	# Check if haproxyctl exists
	if command -v haproxyctl >/dev/null 2>&1; then
		# Add backend pointing to static files (served by uhttpd)
		local static_url="/static/${instance}/"
		local router_ip=$(uci -q get network.lan.ipaddr || echo "192.168.255.1")

		log_info "Configuring HAProxy vhost: $domain"

		# Remove existing vhost if present
		haproxyctl vhost del "$domain" 2>/dev/null

		# Add vhost pointing to uhttpd
		haproxyctl vhost add "$domain" uhttpd "$router_ip" 80 2>/dev/null || true

		# The auth rules need to be added to HAProxy config manually or via template
		log_info "Vhost created: $domain -> uhttpd"
		log_info ""
		log_info "Add these lines to HAProxy frontend https-in:"
		echo ""
		echo "    # HexoJS Auth for $instance"
		echo "    acl host_${instance}_hexo hdr(host) -i ${domain}"
		echo "    http-request auth realm \"SecuBox HexoJS\" if host_${instance}_hexo !{ http_auth(hexojs_users) }"
		echo ""
		log_info "Then reload HAProxy: haproxyctl reload"
	else
		log_warn "haproxyctl not found"
		log_info "Manual HAProxy config required - see: hexoctl auth haproxy $instance"
	fi

	log_info "Auth applied for '$instance' at $domain"
}

# ===============================================
# KISS Static Upload (No Hexo Build Required)
# ===============================================

# Create a static-only instance (no Hexo, just serves files)
cmd_static_create() {
	require_root
	load_config

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

	# Validate name
	echo "$name" | grep -qE '^[a-z][a-z0-9_]*$' || {
		log_error "Invalid name. Use lowercase letters, numbers, underscore."
		return 1
	}

	# Find next available port
	local port=4000
	while uci show hexojs 2>/dev/null | grep -q "port='$port'"; do
		port=$((port + 1))
	done

	# Create UCI config
	uci set hexojs.${name}=instance
	uci set hexojs.${name}.enabled='1'
	uci set hexojs.${name}.port="$port"
	uci set hexojs.${name}.title="$name"
	uci set hexojs.${name}.type='static'
	uci commit hexojs

	# Create directory structure
	local static_dir="$data_path/static/$name"
	ensure_dir "$static_dir"

	# Create default index.html
	cat > "$static_dir/index.html" << 'HTML'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SecuBox Static Site</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: system-ui, -apple-system, sans-serif;
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #e0e0e0;
        }
        .container {
            text-align: center;
            padding: 40px;
            background: rgba(255,255,255,0.05);
            border-radius: 20px;
            backdrop-filter: blur(10px);
            border: 1px solid rgba(255,255,255,0.1);
        }
        h1 { font-size: 2.5em; margin-bottom: 20px; color: #f97316; }
        p { color: #999; margin: 10px 0; }
        .upload-hint {
            margin-top: 30px;
            padding: 15px;
            background: rgba(249,115,22,0.1);
            border-radius: 10px;
            font-family: monospace;
            font-size: 0.9em;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>SecuBox Static Site</h1>
        <p>Upload your HTML files to get started</p>
        <div class="upload-hint">
            hexoctl static upload &lt;file.html&gt; [instance]
        </div>
    </div>
</body>
</html>
HTML

	log_info "Static instance '$name' created on port $port"
	log_info "Directory: $static_dir"
	log_info "Upload files: hexoctl static upload <file> $name"
}

# Upload file to static instance (KISS fast publish)
cmd_static_upload() {
	require_root
	load_config

	local file="$1"
	local instance="${2:-default}"

	[ -z "$file" ] && { log_error "File path required"; return 1; }
	[ -f "$file" ] || { log_error "File not found: $file"; return 1; }

	local static_dir="$data_path/static/$instance"
	[ -d "$static_dir" ] || {
		log_info "Creating static instance '$instance'..."
		cmd_static_create "$instance"
	}

	local filename=$(basename "$file")
	cp "$file" "$static_dir/$filename"

	log_info "Uploaded: $filename → $static_dir/"
	log_info "URL: http://$(uci -q get network.lan.ipaddr || echo localhost):$(uci_get ${instance}.port || echo 4000)/$filename"
}

# List files in static instance
cmd_static_list() {
	load_config

	local instance="${1:-}"

	if [ -n "$instance" ]; then
		local static_dir="$data_path/static/$instance"
		[ -d "$static_dir" ] || { log_error "Static instance '$instance' not found"; return 1; }

		echo "Files in '$instance':"
		ls -lh "$static_dir" | tail -n +2
	else
		echo "Static Instances:"
		echo "-----------------"
		for dir in "$data_path/static"/*; do
			[ -d "$dir" ] || continue
			local name=$(basename "$dir")
			local count=$(find "$dir" -type f | wc -l)
			local port=$(uci_get "${name}.port")
			printf "  %-15s %3d files  port:%s\n" "$name" "$count" "${port:-?}"
		done
	fi
}

# Start simple static server (no Hexo, just Python/BusyBox httpd)
cmd_static_serve() {
	require_root
	load_config

	local instance="${1:-default}"
	local static_dir="$data_path/static/$instance"

	[ -d "$static_dir" ] || { log_error "Static instance '$instance' not found"; return 1; }

	local port=$(uci_get "${instance}.port")
	[ -z "$port" ] && port=4000

	log_info "Serving '$instance' on port $port..."

	# Try Python first, fallback to busybox httpd
	if command -v python3 >/dev/null 2>&1; then
		cd "$static_dir" && python3 -m http.server "$port" --bind 0.0.0.0
	elif command -v busybox >/dev/null 2>&1; then
		busybox httpd -f -p "$port" -h "$static_dir"
	else
		log_error "No HTTP server available (need python3 or busybox)"
		return 1
	fi
}

# Delete static instance
cmd_static_delete() {
	require_root
	load_config

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

	local static_dir="$data_path/static/$name"
	[ -d "$static_dir" ] || { log_error "Static instance '$name' not found"; return 1; }

	# Remove directory
	rm -rf "$static_dir"

	# Remove UCI config if exists
	uci delete hexojs.${name} 2>/dev/null
	uci commit hexojs 2>/dev/null

	log_info "Static instance '$name' deleted"
}

# Publish static instance to /www/ (immediate serving via uhttpd)
cmd_static_publish() {
	require_root
	load_config

	local instance="${1:-default}"
	local static_dir="$data_path/static/$instance"

	[ -d "$static_dir" ] || { log_error "Static instance '$instance' not found"; return 1; }

	local publish_path="/www/static/${instance}"
	ensure_dir "$publish_path"

	log_info "Publishing to $publish_path..."
	rsync -av --delete "$static_dir/" "$publish_path/"

	local file_count=$(find "$publish_path" -type f | wc -l)
	log_info "Published $file_count files"
	log_info "URL: http://$(uci -q get network.lan.ipaddr || echo localhost)/static/${instance}/"
}

# Quick upload + publish workflow (KISS one-liner)
cmd_static_quick() {
	require_root
	load_config

	local file="$1"
	local instance="${2:-quick}"

	[ -z "$file" ] && { log_error "File path required"; return 1; }
	[ -f "$file" ] || { log_error "File not found: $file"; return 1; }

	# Upload
	cmd_static_upload "$file" "$instance"

	# Publish immediately
	cmd_static_publish "$instance"
}

cmd_instance_list_json() {
	load_config

	echo "["
	local first=1
	for section in $(uci show hexojs 2>/dev/null | grep '=instance$' | cut -d'.' -f2 | cut -d'=' -f1); do
		load_instance_config "$section"
		local running="false"
		if lxc_running && [ -f "$LXC_ROOTFS/var/run/hexo/${section}.pid" ]; then
			running="true"
		fi
		local site_exists="false"
		[ -d "$instance_site" ] && site_exists="true"

		[ "$first" = "1" ] || echo ","
		first=0
		cat << EOF
  {
    "name": "$section",
    "enabled": $([ "$instance_enabled" = "1" ] && echo true || echo false),
    "running": $running,
    "port": $instance_port,
    "title": "$instance_title",
    "theme": "$instance_theme",
    "site_exists": $site_exists
  }
EOF
	done
	echo "]"
}

# Main
case "${1:-}" in
	install)    shift; cmd_install "$@" ;;
	uninstall)  shift; cmd_uninstall "$@" ;;
	update)     shift; cmd_update "$@" ;;
	status)     shift; cmd_status "$@" ;;

	instance)
		shift
		case "${1:-}" in
			list)   shift; cmd_instance_list "$@" ;;
			create) shift; cmd_instance_create "$@" ;;
			delete) shift; cmd_instance_delete "$@" ;;
			start)  shift; cmd_instance_start "$@" ;;
			stop)   shift; cmd_instance_stop "$@" ;;
			status) shift; cmd_instance_status "$@" ;;
			*)      echo "Usage: hexoctl instance {list|create|delete|start|stop|status} [name]" ;;
		esac
		;;

	site)
		shift
		case "${1:-}" in
			create) shift; cmd_site_create "$@" ;;
			delete) shift; cmd_site_delete "$@" ;;
			list)   shift; cmd_instance_list "$@" ;;
			*)      echo "Usage: hexoctl site {create|delete|list} [instance]" ;;
		esac
		;;

	new)
		shift
		case "${1:-}" in
			post)  shift; cmd_new_post "$@" ;;
			page)  shift; cmd_new_page "$@" ;;
			draft) shift; cmd_new_draft "$@" ;;
			*)     echo "Usage: hexoctl new {post|page|draft} \"Title\" [instance]" ;;
		esac
		;;

	list)
		shift
		case "${1:-}" in
			posts)  shift; cmd_list_posts "$@" ;;
			drafts) shift; cmd_list_drafts "$@" ;;
			*)      echo "Usage: hexoctl list {posts|drafts} [instance]" ;;
		esac
		;;

	serve)      shift; cmd_serve "$@" ;;
	build|generate) shift; cmd_build "$@" ;;
	clean)      shift; cmd_clean "$@" ;;
	publish)    shift; cmd_publish "$@" ;;

	logs)       shift; cmd_logs "$@" ;;
	shell)      shift; cmd_shell "$@" ;;
	exec)       shift; cmd_exec "$@" ;;

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

	gitea)
		shift
		case "${1:-}" in
			setup)  shift; cmd_gitea_setup "$@" ;;
			clone)  shift; cmd_gitea_clone "$@" ;;
			sync)   shift; cmd_gitea_sync "$@" ;;
			push)   shift; cmd_gitea_push "$@" ;;
			*)      echo "Usage: hexoctl gitea {setup|clone|sync|push} [instance] [message]" ;;
		esac
		;;

	github)
		shift
		case "${1:-}" in
			clone)  shift; cmd_github_clone "$@" ;;
			*)      echo "Usage: hexoctl github clone <repo_url> [instance] [branch]" ;;
		esac
		;;

	backup)
		shift
		case "${1:-}" in
			list)   shift; cmd_backup_list "$@" ;;
			create) shift; cmd_backup "$@" ;;
			delete) shift; cmd_backup_delete "$@" ;;
			restore) shift; cmd_restore "$@" ;;
			"")     cmd_backup "$@" ;;
			*)      cmd_backup "$@" ;;
		esac
		;;

	restore)    shift; cmd_restore "$@" ;;
	quick-publish) shift; cmd_quick_publish "$@" ;;
	status-json) cmd_status_json ;;
	instance-list-json) cmd_instance_list_json ;;

	user)
		shift
		case "${1:-}" in
			add)    shift; cmd_user_add "$@" ;;
			del|delete) shift; cmd_user_del "$@" ;;
			list)   shift; cmd_user_list "$@" ;;
			passwd) shift; cmd_user_passwd "$@" ;;
			grant)  shift; cmd_user_grant "$@" ;;
			revoke) shift; cmd_user_revoke "$@" ;;
			sync-secubox|sync) shift; cmd_user_sync_secubox "$@" ;;
			*)      echo "Usage: hexoctl user {add|del|list|passwd|grant|revoke|sync-secubox}" ;;
		esac
		;;

	auth)
		shift
		case "${1:-}" in
			enable)  shift; cmd_auth_enable "$@" ;;
			disable) shift; cmd_auth_disable "$@" ;;
			status)  shift; cmd_auth_status "$@" ;;
			haproxy) shift; cmd_auth_haproxy "$@" ;;
			haproxy-all) cmd_auth_haproxy_all ;;
			apply)   shift; cmd_auth_apply "$@" ;;
			*)       echo "Usage: hexoctl auth {enable|disable|status|haproxy|apply} [instance]" ;;
		esac
		;;

	# KISS Static Upload (no Hexo, direct HTML)
	static)
		shift
		case "${1:-}" in
			create)  shift; cmd_static_create "$@" ;;
			upload)  shift; cmd_static_upload "$@" ;;
			publish) shift; cmd_static_publish "$@" ;;
			quick)   shift; cmd_static_quick "$@" ;;
			list)    shift; cmd_static_list "$@" ;;
			serve)   shift; cmd_static_serve "$@" ;;
			delete)  shift; cmd_static_delete "$@" ;;
			*)       echo "Usage: hexoctl static {create|upload|publish|quick|list|serve|delete} [instance]" ;;
		esac
		;;

	*)          usage ;;
esac
