#!/bin/sh /etc/rc.common
# luci-app-hermes — procd init script

USE_PROCD=1
START=99
STOP=10

EXTRA_COMMANDS="setup status_service restart_gateway"
EXTRA_HELP="    setup            Install Python and Hermes Agent
        status_service   Show service status
        restart_gateway  Restart only Gateway instance (does not affect Web PTY)"

HERMES_BASE="/opt/hermes"
HERMES_VENV="${HERMES_BASE}/venv"
HERMES_DATA="${HERMES_BASE}/data"
HERMES_CONFIG="${HERMES_DATA}/config.yaml"
HERMES_BIN="${HERMES_VENV}/bin/hermes"
PYTHON_BIN="${HERMES_VENV}/bin/python3"

# ── OverlayFS compatibility fix ──
# Docker bind mount (/overlay/upper/opt/docker) makes /opt unwritable
# Solution: bind mount upper layer /opt to merged view /opt
_hermes_fix_opt() {
	mkdir -p /opt/hermes/.probe 2>/dev/null && { rmdir /opt/hermes/.probe 2>/dev/null; return 0; }
	if [ -d /overlay/upper/opt ]; then
		mkdir -p /overlay/upper/opt/hermes 2>/dev/null
		mount --bind /overlay/upper/opt /opt 2>/dev/null && return 0
	fi
	return 1
}
_hermes_fix_opt

# ── Port listening check ──
_ensure_port_free() {
	local p="$1"
	local i=0 max_wait=3

	# Check if port is already in use
	if command -v ss >/dev/null 2>&1; then
		ss -tulnp 2>/dev/null | grep -q ":${p} " || return 0
	else
		netstat -tulnp 2>/dev/null | grep -q ":${p} " || return 0
	fi

	# Port is occupied, try to clean up
	for hermes_pid in $(pgrep -f "hermes.*gateway" 2>/dev/null); do
		kill "$hermes_pid" 2>/dev/null
	done

	while [ $i -lt $max_wait ]; do
		if command -v ss >/dev/null 2>&1; then
			ss -tulnp 2>/dev/null | grep -q ":${p} " || return 0
		else
			netstat -tulnp 2>/dev/null | grep -q ":${p} " || return 0
		fi
		sleep 1
		i=$((i + 1))
	done

	# Last resort: SIGKILL
	local port_pid=""
	if command -v ss >/dev/null 2>&1; then
		port_pid=$(ss -tulnp 2>/dev/null | grep ":${p} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
	else
		port_pid=$(netstat -tulnp 2>/dev/null | grep ":${p} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
	fi
	[ -n "$port_pid" ] && kill -9 "$port_pid" 2>/dev/null && sleep 1
	return 0
}

port_listening() {
	local p="$1"
	if command -v ss >/dev/null 2>&1; then
		ss -tulnp 2>/dev/null | grep -q ":${p} "
	else
		netstat -tulnp 2>/dev/null | grep -q ":${p} "
	fi
}

sync_uci_to_yaml() {
	# Sync UCI config to /root/.hermes/config.yaml
	local port bind api_endpoint api_key model
	port=$(uci -q get hermes.main.port || echo "3000")
	bind=$(uci -q get hermes.main.bind || echo "lan")
	api_endpoint=$(uci -q get hermes.main.api_endpoint || echo "https://api.boos.lat/v1")
	api_key=$(uci -q get hermes.main.api_key || echo "")
	model=$(uci -q get hermes.main.model || echo "opus4.6")

	# Map UCI bind to listen address
	local listen_host="127.0.0.1"
	case "$bind" in
		lan)      listen_host="0.0.0.0" ;;
		loopback) listen_host="127.0.0.1" ;;
		all)      listen_host="0.0.0.0" ;;
		*)        listen_host="0.0.0.0" ;;
	esac

	# Ensure config directory exists
	mkdir -p /root/.hermes 2>/dev/null
	mkdir -p "$(dirname "$HERMES_CONFIG")" 2>/dev/null

	# Generate token if not set
	local token
	token=$(uci -q get hermes.main.token || echo "")
	if [ -z "$token" ]; then
		token=$(head -c 24 /dev/urandom | hexdump -e '24/1 "%02x"' 2>/dev/null || openssl rand -hex 24 2>/dev/null || echo "hermes_$(date +%s)")
		uci set hermes.main.token="$token"
		uci commit hermes 2>/dev/null
	fi

	# Write config.yaml using shell (no Python dependency at this point)
	cat > /root/.hermes/config.yaml << YAMLEOF
# Hermes Agent configuration — auto-generated by luci-app-hermes
# UCI sync: $(date '+%Y-%m-%d %H:%M:%S')

gateway:
  port: ${port}
  bind: ${listen_host}
  auth:
    mode: token
    token: ${token}

api:
  endpoint: "${api_endpoint}"
  key: "${api_key}"

model:
  primary: "${model}"

tools:
  profile: coding

update:
  checkOnStart: false
YAMLEOF

	# Also sync to HERMES_DATA if different
	if [ -n "$HERMES_CONFIG" ] && [ "$HERMES_CONFIG" != "/root/.hermes/config.yaml" ]; then
		mkdir -p "$(dirname "$HERMES_CONFIG")" 2>/dev/null
		cp /root/.hermes/config.yaml "$HERMES_CONFIG" 2>/dev/null || true
	fi

	chmod 600 /root/.hermes/config.yaml 2>/dev/null || true
}

start_service() {
	local enabled port bind pty_port api_endpoint api_key model
	config_load hermes
	config_get enabled main enabled "0"
	config_get port main port "3000"
	config_get bind main bind "lan"
	config_get pty_port main pty_port "3001"
	config_get api_endpoint main api_endpoint "https://api.boos.lat/v1"
	config_get api_key main api_key ""
	config_get model main model "opus4.6"

	[ "$enabled" = "1" ] || {
		echo "hermes is disabled. Set enabled to 1 in /etc/config/hermes"
		return 0
	}

	# Check Python
	if [ ! -x "$PYTHON_BIN" ]; then
		echo "Python not found: $PYTHON_BIN"
		echo "Please run: hermes-env setup"
		return 1
	fi

	# Check hermes binary
	if [ ! -x "$HERMES_BIN" ]; then
		echo "Hermes not installed. Please run: hermes-env setup"
		return 1
	fi

	# Sync UCI to config.yaml
	sync_uci_to_yaml

	# Ensure gateway port is not occupied by stale processes
	_ensure_port_free "$port"

	# Calculate memory limit based on system RAM
	local mem_limit=""
	local total_kb
	total_kb=$(awk '/MemTotal/{print $2}' /proc/meminfo 2>/dev/null || echo "0")
	if [ "$total_kb" -ge 3000000 ] 2>/dev/null; then
		mem_limit="512m"
	elif [ "$total_kb" -ge 1500000 ] 2>/dev/null; then
		mem_limit="256m"
	elif [ "$total_kb" -ge 700000 ] 2>/dev/null; then
		mem_limit="128m"
	else
		mem_limit="64m"
	fi

	# Start Hermes Gateway (main service, foreground)
	procd_open_instance "gateway"
	procd_set_param command "$HERMES_BIN" gateway run \
		--port "$port" --bind "$bind"
	procd_set_param env \
		HOME="/root" \
		HERMES_HOME="/root/.hermes" \
		HERMES_CONFIG="/root/.hermes/config.yaml" \
		PATH="${HERMES_VENV}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
		PYTHONPATH="${HERMES_VENV}/lib/python3*/site-packages" \
		HERMES_API_ENDPOINT="$api_endpoint" \
		HERMES_API_KEY="$api_key" \
		HERMES_MODEL="$model"
	procd_set_param respawn 3600 10 5
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_set_param pidfile /var/run/hermes.pid
	procd_close_instance

	# Start Web PTY config terminal (auxiliary service)
	local pty_token
	pty_token=$(uci -q get hermes.main.pty_token || echo "")
	if [ -z "$pty_token" ]; then
		pty_token=$(head -c 16 /dev/urandom | hexdump -e '16/1 "%02x"' 2>/dev/null || openssl rand -hex 16 2>/dev/null || echo "pty_$(date +%s)")
		uci set hermes.main.pty_token="$pty_token"
		uci commit hermes 2>/dev/null
	fi

	procd_open_instance "pty"
	procd_set_param command "$PYTHON_BIN" /usr/share/hermes/web-pty.py
	procd_set_param env \
		HERMES_PTY_PORT="$pty_port" \
		HERMES_PTY_TOKEN="$pty_token" \
		HERMES_VENV="$HERMES_VENV" \
		HERMES_DATA="$HERMES_DATA" \
		HOME="/root" \
		PATH="${HERMES_VENV}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
	procd_set_param respawn 3600 5 5
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_set_param pidfile /var/run/hermes-pty.pid
	procd_close_instance
}

_ensure_port_free() {
	local p="$1"
	local i=0 max_wait=3

	# Check if port is already in use
	if command -v ss >/dev/null 2>&1; then
		ss -tulnp 2>/dev/null | grep -q ":${p} " || return 0
	else
		netstat -tulnp 2>/dev/null | grep -q ":${p} " || return 0
	fi

	# Port is occupied, try to clean up
	for hermes_pid in $(pgrep -f "hermes.*gateway" 2>/dev/null); do
		kill "$hermes_pid" 2>/dev/null
	done

	while [ $i -lt $max_wait ]; do
		if command -v ss >/dev/null 2>&1; then
			ss -tulnp 2>/dev/null | grep -q ":${p} " || return 0
		else
			netstat -tulnp 2>/dev/null | grep -q ":${p} " || return 0
		fi
		sleep 1
		i=$((i + 1))
	done

	# Last resort: SIGKILL
	local port_pid=""
	if command -v ss >/dev/null 2>&1; then
		port_pid=$(ss -tulnp 2>/dev/null | grep ":${p} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
	else
		port_pid=$(netstat -tulnp 2>/dev/null | grep ":${p} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
	fi
	[ -n "$port_pid" ] && kill -9 "$port_pid" 2>/dev/null && sleep 1
	return 0
}

stop_service() {
	local port
	port=$(uci -q get hermes.main.port || echo "3000")

	# Get all related PIDs
	local gw_pids=""
	gw_pids=$(pgrep -f "hermes.*gateway" 2>/dev/null)
	gw_pids="$gw_pids $(pgrep -f "hermes.*gateway.*run" 2>/dev/null)"

	# Send SIGTERM to all processes
	for pid in $gw_pids; do
		[ -n "$pid" ] && kill "$pid" 2>/dev/null
	done

	# Wait for port to be released
	local wait_count=0
	local max_wait=4
	while [ $wait_count -lt $max_wait ]; do
		if command -v ss >/dev/null 2>&1; then
			ss -tulnp 2>/dev/null | grep -q ":${port} " || break
		else
			netstat -tulnp 2>/dev/null | grep -q ":${port} " || break
		fi
		sleep 1
		wait_count=$((wait_count + 1))
	done

	# Force kill if still running
	if [ $wait_count -ge $max_wait ]; then
		local port_pid=""
		if command -v ss >/dev/null 2>&1; then
			port_pid=$(ss -tulnp 2>/dev/null | grep ":${port} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
		else
			port_pid=$(netstat -tulnp 2>/dev/null | grep ":${port} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
		fi
		if [ -n "$port_pid" ]; then
			kill -9 "$port_pid" 2>/dev/null
			sleep 1
		fi
	fi
}

service_triggers() {
	procd_add_reload_trigger "hermes"
}

reload_service() {
	stop
	sleep 1
	start
}

setup() {
	echo "Calling hermes-env setup..."
	/usr/bin/hermes-env setup
}

restart_gateway() {
	# Restart only the Gateway process, does not affect PTY
	local port
	port=$(uci -q get hermes.main.port || echo "3000")

	# Get gateway PID
	local port_pid="" gw_pid=""
	if command -v ss >/dev/null 2>&1; then
		port_pid=$(ss -tulnp 2>/dev/null | grep ":${port} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
	else
		port_pid=$(netstat -tulnp 2>/dev/null | grep ":${port} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
	fi
	gw_pid=$(ubus call service list '{"name":"hermes"}' 2>/dev/null | \
		jsonfilter -e '$.hermes.instances.gateway.pid' 2>/dev/null) || true

	# Send SIGTERM
	[ -n "$port_pid" ] && kill "$port_pid" 2>/dev/null
	[ -n "$gw_pid" ] && kill -0 "$gw_pid" 2>/dev/null && kill "$gw_pid" 2>/dev/null

	# Kill any remaining hermes gateway processes
	for pid in $(pgrep -f "hermes.*gateway" 2>/dev/null) $(pgrep -f "hermes.*gateway.*run" 2>/dev/null); do
		kill "$pid" 2>/dev/null
	done

	# Wait for port to be released
	local wait_count=0
	while [ $wait_count -lt 3 ]; do
		if command -v ss >/dev/null 2>&1; then
			ss -tulnp 2>/dev/null | grep -q ":${port} " || break
		else
			netstat -tulnp 2>/dev/null | grep -q ":${port} " || break
		fi
		sleep 1
		wait_count=$((wait_count + 1))
	done

	# Force kill if still running
	if [ $wait_count -ge 3 ]; then
		[ -n "$port_pid" ] && kill -9 "$port_pid" 2>/dev/null
		sleep 1
	fi

	# If no procd gateway, restart the service
	if [ -z "$gw_pid" ]; then
		/etc/init.d/hermes start >/dev/null 2>&1
	fi
}

status_service() {
	local port pty_port
	port=$(uci -q get hermes.main.port || echo "3000")
	pty_port=$(uci -q get hermes.main.pty_port || echo "3001")

	echo "=== Hermes Agent Service Status ==="

	# Python
	if [ -x "$PYTHON_BIN" ]; then
		echo "Python:    $($PYTHON_BIN --version 2>/dev/null)"
	else
		echo "Python:    Not installed"
	fi

	# Hermes Agent
	if [ -x "$HERMES_BIN" ]; then
		echo "Hermes:    $($HERMES_BIN --version 2>/dev/null | head -1)"
	else
		echo "Hermes:    Not installed"
	fi

	# Gateway
	if port_listening "$port"; then
		echo "Gateway:   ✅ Running (:$port)"
	else
		echo "Gateway:   ⏹ Stopped"
	fi

	# PTY
	if port_listening "$pty_port"; then
		echo "Config PTY: ✅ Running (:$pty_port)"
	else
		echo "Config PTY: ⏹ Stopped"
	fi

	# Config
	echo "Config:    $HERMES_CONFIG"
	echo "Data Dir:  $HERMES_DATA"
}
