#!/bin/sh
# SPDX-License-Identifier: MIT
# Service Registry RPCD backend
# Copyright (C) 2025 CyberMind.fr

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

UCI_CONFIG="service-registry"
TOR_DATA="/var/lib/tor"
LAN_IP="192.168.255.1"

# Helper: Get UCI value
get_uci() {
	local section="$1"
	local option="$2"
	local default="$3"
	local value
	value=$(uci -q get "$UCI_CONFIG.$section.$option")
	echo "${value:-$default}"
}

# Helper: Get LAN IP address
get_lan_ip() {
	local ip
	ip=$(uci -q get network.lan.ipaddr)
	[ -z "$ip" ] && ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
	echo "${ip:-$LAN_IP}"
}

# Helper: Check if port is listening
is_port_listening() {
	local port="$1"
	netstat -tln 2>/dev/null | grep -q ":${port} " && return 0
	return 1
}

# Helper: Check if HAProxy is available and running
haproxy_available() {
	# Check if HAProxy container is running (preferred method)
	if command -v lxc-info >/dev/null 2>&1; then
		lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING" && return 0
	fi

	# Fallback: check if haproxy process is running
	pgrep haproxy >/dev/null 2>&1 && return 0

	# Fallback: check if RPCD haproxy interface is available
	ubus call luci.haproxy list 2>/dev/null | jsonfilter -e '@' >/dev/null 2>&1 && return 0

	return 1
}

# Helper: Get process name for port
get_process_for_port() {
	local port="$1"
	netstat -tlnp 2>/dev/null | grep ":${port} " | awk '{print $7}' | cut -d'/' -f2 | head -1
}

# Helper: Ensure firewall rules for HAProxy (HTTP/HTTPS from WAN)
ensure_haproxy_firewall_rules() {
	local http_port="${1:-80}"
	local https_port="${2:-443}"
	local changed=0

	# Check and create HTTP rule
	local http_exists=0
	local i=0
	while uci -q get firewall.@rule[$i] >/dev/null 2>&1; do
		local name=$(uci -q get firewall.@rule[$i].name)
		[ "$name" = "HAProxy-HTTP" ] && http_exists=1 && break
		i=$((i + 1))
	done

	if [ "$http_exists" = "0" ]; then
		uci add firewall rule >/dev/null
		uci set firewall.@rule[-1].name='HAProxy-HTTP'
		uci set firewall.@rule[-1].src='wan'
		uci set firewall.@rule[-1].dest_port="$http_port"
		uci set firewall.@rule[-1].proto='tcp'
		uci set firewall.@rule[-1].target='ACCEPT'
		uci set firewall.@rule[-1].enabled='1'
		changed=1
	fi

	# Check and create HTTPS rule
	local https_exists=0
	i=0
	while uci -q get firewall.@rule[$i] >/dev/null 2>&1; do
		local name=$(uci -q get firewall.@rule[$i].name)
		[ "$name" = "HAProxy-HTTPS" ] && https_exists=1 && break
		i=$((i + 1))
	done

	if [ "$https_exists" = "0" ]; then
		uci add firewall rule >/dev/null
		uci set firewall.@rule[-1].name='HAProxy-HTTPS'
		uci set firewall.@rule[-1].src='wan'
		uci set firewall.@rule[-1].dest_port="$https_port"
		uci set firewall.@rule[-1].proto='tcp'
		uci set firewall.@rule[-1].target='ACCEPT'
		uci set firewall.@rule[-1].enabled='1'
		changed=1
	fi

	# Apply firewall changes
	if [ "$changed" = "1" ]; then
		uci commit firewall
		/etc/init.d/firewall reload >/dev/null 2>&1 &
	fi
}

# List all services aggregated from providers
method_list_services() {
	local lan_ip
	lan_ip=$(get_lan_ip)

	json_init
	json_add_array "services"

	# Temporary file for service aggregation (avoid subshell issues)
	local TMP_SERVICES="/tmp/sr_services_$$"
	> "$TMP_SERVICES"

	# 1. Get published services from UCI
	config_load "$UCI_CONFIG"
	config_foreach _add_published_service service "$lan_ip" "$TMP_SERVICES"

	# 2. Get HAProxy vhosts
	local haproxy_enabled
	haproxy_enabled=$(get_uci haproxy enabled 1)
	if [ "$haproxy_enabled" = "1" ]; then
		_aggregate_haproxy_services "$lan_ip" "$TMP_SERVICES"
	fi

	# 3. Get Tor hidden services
	local tor_enabled
	tor_enabled=$(get_uci tor enabled 1)
	if [ "$tor_enabled" = "1" ]; then
		_aggregate_tor_services "$TMP_SERVICES"
	fi

	# 4. Get direct listening services (from luci.secubox)
	# NOTE: Disabled by default - too slow with many ports (spawns jsonfilter per service)
	# Enable in UCI: uci set service-registry.direct.enabled=1
	local direct_enabled
	direct_enabled=$(get_uci direct enabled 0)
	if [ "$direct_enabled" = "1" ]; then
		_aggregate_direct_services "$lan_ip" "$TMP_SERVICES"
	fi

	# 5. Get LXC container services
	local lxc_enabled
	lxc_enabled=$(get_uci lxc enabled 1)
	if [ "$lxc_enabled" = "1" ]; then
		_aggregate_lxc_services "$lan_ip" "$TMP_SERVICES"
	fi

	rm -f "$TMP_SERVICES"
	json_close_array

	# Provider status
	json_add_object "providers"

	json_add_object "haproxy"
	if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	local haproxy_count
	haproxy_count=$(uci -q show haproxy | grep -c "=vhost$")
	json_add_int "count" "${haproxy_count:-0}"
	json_close_object

	json_add_object "tor"
	if pgrep -f "/usr/sbin/tor" >/dev/null 2>&1; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	local tor_count
	tor_count=$(uci -q show tor-shield | grep -c "=hidden_service$")
	json_add_int "count" "${tor_count:-0}"
	json_close_object

	json_add_object "direct"
	local direct_count
	direct_count=$(netstat -tln 2>/dev/null | grep -c LISTEN)
	json_add_int "count" "${direct_count:-0}"
	json_close_object

	json_add_object "lxc"
	local lxc_running
	lxc_running=$(lxc-ls --running 2>/dev/null | wc -w)
	json_add_int "count" "${lxc_running:-0}"
	json_close_object

	json_close_object

	json_dump
}

# Add published service from UCI
_add_published_service() {
	local section="$1"
	local lan_ip="$2"
	local tmp_file="$3"
	local name category icon local_port published
	local haproxy_enabled haproxy_domain haproxy_ssl
	local tor_enabled tor_onion tor_port

	config_get name "$section" name "$section"
	config_get category "$section" category "services"
	config_get icon "$section" icon ""
	config_get local_port "$section" local_port ""
	config_get published "$section" published "0"

	[ "$published" != "1" ] && return
	[ -z "$local_port" ] && return

	config_get haproxy_enabled "$section" haproxy_enabled "0"
	config_get haproxy_domain "$section" haproxy_domain ""
	config_get haproxy_ssl "$section" haproxy_ssl "0"
	config_get tor_enabled "$section" tor_enabled "0"
	config_get tor_onion "$section" tor_onion ""
	config_get tor_port "$section" tor_port "80"

	# Check if service is running
	local status="stopped"
	is_port_listening "$local_port" && status="running"

	json_add_object
	json_add_string "id" "$section"
	json_add_string "name" "$name"
	json_add_string "category" "$category"
	json_add_string "icon" "$icon"
	json_add_int "local_port" "$local_port"
	json_add_string "status" "$status"
	json_add_boolean "published" 1

	# URLs
	json_add_object "urls"
	json_add_string "local" "http://${lan_ip}:${local_port}"
	[ "$haproxy_enabled" = "1" ] && [ -n "$haproxy_domain" ] && {
		if [ "$haproxy_ssl" = "1" ]; then
			json_add_string "clearnet" "https://${haproxy_domain}"
		else
			json_add_string "clearnet" "http://${haproxy_domain}"
		fi
	}
	[ "$tor_enabled" = "1" ] && [ -n "$tor_onion" ] && {
		json_add_string "onion" "http://${tor_onion}:${tor_port}"
	}
	json_close_object

	# HAProxy config
	json_add_object "haproxy"
	json_add_boolean "enabled" "$haproxy_enabled"
	[ -n "$haproxy_domain" ] && json_add_string "domain" "$haproxy_domain"
	json_add_boolean "ssl" "$haproxy_ssl"
	json_close_object

	# Tor config
	json_add_object "tor"
	json_add_boolean "enabled" "$tor_enabled"
	[ -n "$tor_onion" ] && json_add_string "onion_address" "$tor_onion"
	json_add_int "virtual_port" "$tor_port"
	json_close_object

	json_close_object

	# Mark as processed
	echo "$local_port" >> "$tmp_file"
}

# Aggregate HAProxy vhosts
_aggregate_haproxy_services() {
	local lan_ip="$1"
	local tmp_file="$2"

	# Check if HAProxy is available before calling RPCD
	if ! haproxy_available; then
		return 0
	fi

	# Call HAProxy RPCD to get vhosts and backends
	local vhosts_json backends_json certs_json
	vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null)
	[ -z "$vhosts_json" ] && return

	backends_json=$(ubus call luci.haproxy list_backends 2>/dev/null)
	certs_json=$(ubus call luci.haproxy list_certificates 2>/dev/null)

	# Get array length
	local count
	count=$(echo "$vhosts_json" | jsonfilter -e '@.vhosts[*].domain' 2>/dev/null | wc -l)
	[ "$count" -eq 0 ] && return

	local i=0
	while [ $i -lt "$count" ]; do
		local id domain backend ssl ssl_redirect acme enabled
		id=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].id" 2>/dev/null)
		domain=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].domain" 2>/dev/null)
		backend=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].backend" 2>/dev/null)
		ssl=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].ssl" 2>/dev/null)
		ssl_redirect=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].ssl_redirect" 2>/dev/null)
		acme=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].acme" 2>/dev/null)
		enabled=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].enabled" 2>/dev/null)

		i=$((i + 1))

		# Skip if domain empty or disabled
		[ -z "$domain" ] && continue

		# Check if already processed
		grep -q "^haproxy_${domain}$" "$tmp_file" 2>/dev/null && continue

		# Get backend port from servers
		local backend_port=""
		if [ -n "$backends_json" ] && [ -n "$backend" ]; then
			backend_port=$(echo "$backends_json" | jsonfilter -e "@.backends[@.name='$backend'].servers[0].port" 2>/dev/null)
			[ -z "$backend_port" ] && backend_port=$(echo "$backends_json" | jsonfilter -e "@.backends[@.id='$backend'].servers[0].port" 2>/dev/null)
		fi

		# Check certificate status
		local cert_status="none"
		if [ "$ssl" = "true" ] || [ "$ssl" = "1" ]; then
			if [ "$acme" = "true" ] || [ "$acme" = "1" ]; then
				cert_status="acme"
			else
				cert_status="manual"
			fi
		fi

		# Determine status based on enabled and HAProxy running
		local status="stopped"
		if [ "$enabled" = "true" ] || [ "$enabled" = "1" ]; then
			# Check if HAProxy container is running
			if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
				status="running"
			fi
		else
			status="disabled"
		fi

		json_add_object
		json_add_string "id" "$id"
		json_add_string "name" "$domain"
		json_add_string "category" "proxy"
		json_add_string "icon" "arrow"
		json_add_string "status" "$status"
		json_add_boolean "published" 1
		json_add_string "source" "haproxy"

		# URLs
		json_add_object "urls"
		json_add_string "local" "http://${lan_ip}${backend_port:+:$backend_port}"
		if [ "$ssl" = "true" ] || [ "$ssl" = "1" ]; then
			json_add_string "clearnet" "https://${domain}"
		else
			json_add_string "clearnet" "http://${domain}"
		fi
		json_close_object

		# HAProxy details
		json_add_object "haproxy"
		json_add_string "id" "$id"
		json_add_string "domain" "$domain"
		json_add_string "backend" "$backend"
		[ -n "$backend_port" ] && json_add_int "backend_port" "$backend_port"
		if [ "$ssl" = "true" ] || [ "$ssl" = "1" ]; then
			json_add_boolean "ssl" 1
		else
			json_add_boolean "ssl" 0
		fi
		if [ "$ssl_redirect" = "true" ] || [ "$ssl_redirect" = "1" ]; then
			json_add_boolean "ssl_redirect" 1
		else
			json_add_boolean "ssl_redirect" 0
		fi
		if [ "$acme" = "true" ] || [ "$acme" = "1" ]; then
			json_add_boolean "acme" 1
		else
			json_add_boolean "acme" 0
		fi
		if [ "$enabled" = "true" ] || [ "$enabled" = "1" ]; then
			json_add_boolean "enabled" 1
		else
			json_add_boolean "enabled" 0
		fi
		json_add_string "cert_status" "$cert_status"
		json_close_object

		json_close_object

		echo "haproxy_${domain}" >> "$tmp_file"
	done
}

# Aggregate Tor hidden services
_aggregate_tor_services() {
	local tmp_file="$1"

	# Get hidden services from tor-shield config
	config_load "tor-shield"
	config_foreach _add_tor_hidden_service hidden_service "$tmp_file"
}

_add_tor_hidden_service() {
	local section="$1"
	local tmp_file="$2"
	local enabled name local_port virtual_port

	config_get enabled "$section" enabled "0"
	[ "$enabled" != "1" ] && return

	config_get name "$section" name "$section"
	config_get local_port "$section" local_port "80"
	config_get virtual_port "$section" virtual_port "80"

	# Check if already processed
	grep -q "^tor_${name}$" "$tmp_file" 2>/dev/null && continue

	# Get onion address
	local hostname_file="$TOR_DATA/hidden_service_${name}/hostname"
	local onion_addr=""
	[ -f "$hostname_file" ] && onion_addr=$(cat "$hostname_file")

	json_add_object
	json_add_string "id" "tor_${name}"
	json_add_string "name" "${name} (Tor)"
	json_add_string "category" "privacy"
	json_add_string "icon" "onion"
	json_add_int "local_port" "$local_port"
	json_add_string "status" "running"
	json_add_boolean "published" 1
	json_add_string "source" "tor"

	json_add_object "urls"
	[ -n "$onion_addr" ] && json_add_string "onion" "http://${onion_addr}:${virtual_port}"
	json_close_object

	json_add_object "tor"
	json_add_boolean "enabled" 1
	json_add_string "onion_address" "$onion_addr"
	json_add_int "virtual_port" "$virtual_port"
	json_close_object

	json_close_object

	echo "tor_${name}" >> "$tmp_file"
}

# Aggregate direct listening services
_aggregate_direct_services() {
	local lan_ip="$1"
	local tmp_file="$2"

	# Get services from luci.secubox get_services
	local services_json
	services_json=$(ubus call luci.secubox get_services 2>/dev/null)
	[ -z "$services_json" ] && return

	# Parse all services at once using jsonfilter list mode for better performance
	# Extract port|name|category|icon tuples in a single pass
	local data_file="/tmp/sr_direct_$$"
	echo "$services_json" | jsonfilter -e '@.services[*].port' -e '@.services[*].name' -e '@.services[*].category' -e '@.services[*].icon' > "$data_file" 2>/dev/null

	# Count services
	local count=0
	local ports=""
	local names=""
	local categories=""
	local icons=""

	# Read ports line by line (each jsonfilter -e outputs one line per match)
	count=$(echo "$services_json" | jsonfilter -e '@.services[*].port' 2>/dev/null | wc -l)
	[ "$count" -eq 0 ] && { rm -f "$data_file"; return; }

	# Limit to first 20 direct services to avoid performance issues
	[ "$count" -gt 20 ] && count=20

	# Get all values at once using array notation
	local i=0
	while [ $i -lt "$count" ]; do
		local port name category icon
		port=$(echo "$services_json" | jsonfilter -e "@.services[$i].port" 2>/dev/null)
		name=$(echo "$services_json" | jsonfilter -e "@.services[$i].name" 2>/dev/null)
		category=$(echo "$services_json" | jsonfilter -e "@.services[$i].category" 2>/dev/null)
		icon=$(echo "$services_json" | jsonfilter -e "@.services[$i].icon" 2>/dev/null)

		i=$((i + 1))

		[ -z "$port" ] && continue
		# Skip if already processed
		grep -q "^${port}$" "$tmp_file" 2>/dev/null && continue
		# Skip common system ports (often not user services)
		case "$port" in 22|53|67|68|123|547|953) continue ;; esac

		json_add_object
		json_add_string "id" "direct_${port}"
		json_add_string "name" "${name:-Port $port}"
		json_add_string "category" "${category:-other}"
		json_add_string "icon" "$icon"
		json_add_int "local_port" "$port"
		json_add_string "status" "running"
		json_add_boolean "published" 0
		json_add_string "source" "direct"

		json_add_object "urls"
		json_add_string "local" "http://${lan_ip}:${port}"
		json_close_object

		json_close_object

		echo "$port" >> "$tmp_file"
	done

	rm -f "$data_file"
}

# Aggregate LXC container services
_aggregate_lxc_services() {
	local lan_ip="$1"
	local tmp_file="$2"

	# Get running containers
	local containers
	containers=$(lxc-ls --running 2>/dev/null)
	[ -z "$containers" ] && return

	for container in $containers; do
		# Skip if already processed
		grep -q "^lxc_${container}$" "$tmp_file" 2>/dev/null && continue

		# Get container IP
		local container_ip
		container_ip=$(lxc-info -n "$container" -iH 2>/dev/null | head -1)

		json_add_object
		json_add_string "id" "lxc_${container}"
		json_add_string "name" "$container"
		json_add_string "category" "container"
		json_add_string "icon" "box"
		json_add_string "status" "running"
		json_add_boolean "published" 0
		json_add_string "source" "lxc"

		json_add_object "urls"
		[ -n "$container_ip" ] && json_add_string "local" "http://${container_ip}"
		json_close_object

		json_add_object "container"
		json_add_string "name" "$container"
		[ -n "$container_ip" ] && json_add_string "ip" "$container_ip"
		json_close_object

		json_close_object

		echo "lxc_${container}" >> "$tmp_file"
	done
}

# Get single service
method_get_service() {
	local service_id

	read -r input
	json_load "$input"
	json_get_var service_id service_id

	json_init

	if [ -z "$service_id" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id is required"
		json_dump
		return
	fi

	# Check if it's a published service
	if uci -q get "$UCI_CONFIG.$service_id" >/dev/null 2>&1; then
		local lan_ip
		lan_ip=$(get_lan_ip)

		json_add_boolean "success" 1
		config_load "$UCI_CONFIG"
		_add_published_service "$service_id" "$lan_ip" "/dev/null"
	else
		json_add_boolean "success" 0
		json_add_string "error" "Service not found"
	fi

	json_dump
}

# Publish a service
method_publish_service() {
	local name local_port domain tor_enabled category icon

	read -r input
	json_load "$input"
	json_get_var name name
	json_get_var local_port local_port
	json_get_var domain domain ""
	json_get_var tor_enabled tor_enabled "0"
	json_get_var category category "services"
	json_get_var icon icon ""

	json_init

	if [ -z "$name" ] || [ -z "$local_port" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "name and local_port are required"
		json_dump
		return
	fi

	# Sanitize name for section ID
	local section_id
	section_id=$(echo "$name" | tr -cd 'a-zA-Z0-9_-' | tr '[:upper:]' '[:lower:]')

	# Create UCI service entry
	uci set "$UCI_CONFIG.$section_id=service"
	uci set "$UCI_CONFIG.$section_id.name=$name"
	uci set "$UCI_CONFIG.$section_id.local_port=$local_port"
	uci set "$UCI_CONFIG.$section_id.category=$category"
	[ -n "$icon" ] && uci set "$UCI_CONFIG.$section_id.icon=$icon"
	uci set "$UCI_CONFIG.$section_id.published=1"

	local lan_ip
	lan_ip=$(get_lan_ip)
	local urls_local="http://${lan_ip}:${local_port}"
	local urls_clearnet=""
	local urls_onion=""

	# Create HAProxy vhost if domain specified and HAProxy is available
	if [ -n "$domain" ]; then
		if haproxy_available; then
			# Ensure firewall allows HTTP/HTTPS from WAN (for public access + ACME)
			ensure_haproxy_firewall_rules

			# Create backend
			ubus call luci.haproxy create_backend "{\"name\":\"$section_id\",\"mode\":\"http\"}" 2>/dev/null

			# Create server pointing to local port
			ubus call luci.haproxy create_server "{\"backend\":\"$section_id\",\"name\":\"local\",\"address\":\"127.0.0.1\",\"port\":$local_port}" 2>/dev/null

			# Create vhost with SSL
			ubus call luci.haproxy create_vhost "{\"domain\":\"$domain\",\"backend\":\"$section_id\",\"ssl\":1,\"ssl_redirect\":1,\"acme\":1,\"enabled\":1}" 2>/dev/null

			# Regenerate HAProxy config to include the new vhost
			ubus call luci.haproxy generate 2>/dev/null
			ubus call luci.haproxy reload 2>/dev/null

			uci set "$UCI_CONFIG.$section_id.haproxy_enabled=1"
			uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain"
			uci set "$UCI_CONFIG.$section_id.haproxy_ssl=1"

			urls_clearnet="https://${domain}"
		else
			# Store domain for later HAProxy configuration when it becomes available
			uci set "$UCI_CONFIG.$section_id.haproxy_enabled=0"
			uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain"
			uci set "$UCI_CONFIG.$section_id.haproxy_pending=1"
			logger -t service-registry "HAProxy unavailable, domain $domain saved for later"
		fi
	fi

	# Create Tor hidden service if enabled
	if [ "$tor_enabled" = "1" ]; then
		ubus call luci.tor-shield add_hidden_service "{\"name\":\"$section_id\",\"local_port\":$local_port,\"virtual_port\":80}" 2>/dev/null

		uci set "$UCI_CONFIG.$section_id.tor_enabled=1"
		uci set "$UCI_CONFIG.$section_id.tor_port=80"

		# Wait for onion address (max 5 seconds)
		local wait_count=0
		local onion_addr=""
		while [ $wait_count -lt 5 ]; do
			sleep 1
			local hostname_file="$TOR_DATA/hidden_service_${section_id}/hostname"
			if [ -f "$hostname_file" ]; then
				onion_addr=$(cat "$hostname_file")
				break
			fi
			wait_count=$((wait_count + 1))
		done

		if [ -n "$onion_addr" ]; then
			uci set "$UCI_CONFIG.$section_id.tor_onion=$onion_addr"
			urls_onion="http://${onion_addr}:80"
		fi
	fi

	uci commit "$UCI_CONFIG"

	# Regenerate landing page if auto-regen enabled
	local auto_regen
	auto_regen=$(get_uci main landing_auto_regen 1)
	[ "$auto_regen" = "1" ] && /usr/sbin/secubox-landing-gen >/dev/null 2>&1 &

	json_add_boolean "success" 1
	json_add_string "id" "$section_id"
	json_add_string "name" "$name"

	json_add_object "urls"
	json_add_string "local" "$urls_local"
	[ -n "$urls_clearnet" ] && json_add_string "clearnet" "$urls_clearnet"
	[ -n "$urls_onion" ] && json_add_string "onion" "$urls_onion"
	json_close_object

	json_dump
}

# Unpublish a service
method_unpublish_service() {
	local service_id

	read -r input
	json_load "$input"
	json_get_var service_id service_id

	json_init

	if [ -z "$service_id" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id is required"
		json_dump
		return
	fi

	# Check if service exists
	if ! uci -q get "$UCI_CONFIG.$service_id" >/dev/null 2>&1; then
		json_add_boolean "success" 0
		json_add_string "error" "Service not found"
		json_dump
		return
	fi

	# Get service config
	config_load "$UCI_CONFIG"
	local haproxy_enabled haproxy_domain tor_enabled
	config_get haproxy_enabled "$service_id" haproxy_enabled "0"
	config_get haproxy_domain "$service_id" haproxy_domain ""
	config_get tor_enabled "$service_id" tor_enabled "0"

	# Remove HAProxy vhost (if HAProxy is available)
	if [ "$haproxy_enabled" = "1" ] && [ -n "$haproxy_domain" ]; then
		if haproxy_available; then
			local vhost_id
			vhost_id=$(echo "$haproxy_domain" | sed 's/[^a-zA-Z0-9]/_/g')
			ubus call luci.haproxy delete_vhost "{\"id\":\"$vhost_id\"}" 2>/dev/null
			ubus call luci.haproxy delete_backend "{\"id\":\"$service_id\"}" 2>/dev/null
		fi
	fi

	# Remove Tor hidden service
	if [ "$tor_enabled" = "1" ]; then
		ubus call luci.tor-shield remove_hidden_service "{\"name\":\"$service_id\"}" 2>/dev/null
	fi

	# Remove UCI entry
	uci delete "$UCI_CONFIG.$service_id"
	uci commit "$UCI_CONFIG"

	# Regenerate landing page
	local auto_regen
	auto_regen=$(get_uci main landing_auto_regen 1)
	[ "$auto_regen" = "1" ] && /usr/sbin/secubox-landing-gen >/dev/null 2>&1 &

	json_add_boolean "success" 1
	json_add_string "message" "Service unpublished"

	json_dump
}

# Update service
method_update_service() {
	local service_id name category icon

	read -r input
	json_load "$input"
	json_get_var service_id service_id
	json_get_var name name ""
	json_get_var category category ""
	json_get_var icon icon ""

	json_init

	if [ -z "$service_id" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id is required"
		json_dump
		return
	fi

	if ! uci -q get "$UCI_CONFIG.$service_id" >/dev/null 2>&1; then
		json_add_boolean "success" 0
		json_add_string "error" "Service not found"
		json_dump
		return
	fi

	[ -n "$name" ] && uci set "$UCI_CONFIG.$service_id.name=$name"
	[ -n "$category" ] && uci set "$UCI_CONFIG.$service_id.category=$category"
	[ -n "$icon" ] && uci set "$UCI_CONFIG.$service_id.icon=$icon"
	uci commit "$UCI_CONFIG"

	json_add_boolean "success" 1

	json_dump
}

# Delete service
method_delete_service() {
	method_unpublish_service
}

# Sync providers (refresh data)
method_sync_providers() {
	json_init
	json_add_boolean "success" 1
	json_add_string "message" "Providers synced"
	json_dump
}

# Generate landing page
method_generate_landing_page() {
	json_init

	local result
	result=$(/usr/sbin/secubox-landing-gen 2>&1)
	local rc=$?

	if [ $rc -eq 0 ]; then
		json_add_boolean "success" 1
		json_add_string "message" "Landing page generated"
		json_add_string "path" "$(get_uci main landing_path '/www/secubox-services.html')"
	else
		json_add_boolean "success" 0
		json_add_string "error" "$result"
	fi

	json_dump
}

# Get QR code data for a URL
method_get_qr_data() {
	local service_id url_type

	read -r input
	json_load "$input"
	json_get_var service_id service_id
	json_get_var url_type url_type "local"

	json_init

	if [ -z "$service_id" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id is required"
		json_dump
		return
	fi

	# Get service URL
	config_load "$UCI_CONFIG"
	local lan_ip local_port haproxy_domain haproxy_ssl tor_onion
	lan_ip=$(get_lan_ip)
	config_get local_port "$service_id" local_port ""
	config_get haproxy_domain "$service_id" haproxy_domain ""
	config_get haproxy_ssl "$service_id" haproxy_ssl "0"
	config_get tor_onion "$service_id" tor_onion ""

	local url=""
	case "$url_type" in
		local)
			[ -n "$local_port" ] && url="http://${lan_ip}:${local_port}"
			;;
		clearnet)
			if [ -n "$haproxy_domain" ]; then
				if [ "$haproxy_ssl" = "1" ]; then
					url="https://${haproxy_domain}"
				else
					url="http://${haproxy_domain}"
				fi
			fi
			;;
		onion)
			[ -n "$tor_onion" ] && url="http://${tor_onion}"
			;;
	esac

	if [ -n "$url" ]; then
		json_add_boolean "success" 1
		json_add_string "url" "$url"
		json_add_string "type" "$url_type"
	else
		json_add_boolean "success" 0
		json_add_string "error" "URL not available for type: $url_type"
	fi

	json_dump
}

# List categories
method_list_categories() {
	json_init
	json_add_array "categories"

	config_load "$UCI_CONFIG"
	config_foreach _add_category category

	json_close_array
	json_dump
}

_add_category() {
	local section="$1"
	local name icon order

	config_get name "$section" name "$section"
	config_get icon "$section" icon ""
	config_get order "$section" order "99"

	json_add_object
	json_add_string "id" "$section"
	json_add_string "name" "$name"
	json_add_string "icon" "$icon"
	json_add_int "order" "$order"
	json_close_object
}

# Helper: Check DNS resolution for a domain
check_dns_resolution() {
	local domain="$1"
	local expected_ip="$2"

	# Try nslookup first (most common on OpenWrt)
	local resolved_ip
	if command -v nslookup >/dev/null 2>&1; then
		resolved_ip=$(nslookup "$domain" 2>/dev/null | grep -A1 "Name:" | grep "Address" | head -1 | awk '{print $2}')
		[ -z "$resolved_ip" ] && resolved_ip=$(nslookup "$domain" 2>/dev/null | grep "Address" | tail -1 | awk '{print $2}' | grep -v "^$")
	elif command -v host >/dev/null 2>&1; then
		resolved_ip=$(host "$domain" 2>/dev/null | grep "has address" | head -1 | awk '{print $4}')
	fi

	if [ -n "$resolved_ip" ]; then
		echo "$resolved_ip"
		return 0
	fi
	return 1
}

# Helper: Get WAN IP address (local interface)
get_wan_ip() {
	local wan_ip=""
	# Try to get WAN IP from interface
	wan_ip=$(uci -q get network.wan.ipaddr)
	if [ -z "$wan_ip" ]; then
		# Try to get from ip command
		wan_ip=$(ip -4 addr show dev eth0 2>/dev/null | grep -oE 'inet [0-9.]+' | head -1 | awk '{print $2}')
	fi
	if [ -z "$wan_ip" ]; then
		# Try to get public IP (uses local IP for comparison)
		wan_ip=$(ifconfig 2>/dev/null | grep -E "inet addr:[0-9]" | grep -v "127.0.0.1" | head -1 | sed 's/.*inet addr:\([0-9.]*\).*/\1/')
	fi
	echo "$wan_ip"
}

# Helper: Get public IPv4 address (from external service)
get_public_ipv4() {
	local ip=""
	# Try multiple services for reliability
	ip=$(wget -qO- -T 5 "http://ipv4.icanhazip.com" 2>/dev/null | tr -d '\n')
	[ -z "$ip" ] && ip=$(wget -qO- -T 5 "http://api.ipify.org" 2>/dev/null | tr -d '\n')
	[ -z "$ip" ] && ip=$(wget -qO- -T 5 "http://v4.ident.me" 2>/dev/null | tr -d '\n')
	echo "$ip"
}

# Helper: Get public IPv6 address (from external service)
get_public_ipv6() {
	local ip=""
	# Try multiple services for reliability
	ip=$(wget -qO- -T 5 "http://ipv6.icanhazip.com" 2>/dev/null | tr -d '\n')
	[ -z "$ip" ] && ip=$(wget -qO- -T 5 "http://v6.ident.me" 2>/dev/null | tr -d '\n')
	echo "$ip"
}

# Helper: Check external port accessibility using portchecker service
check_external_port() {
	local ip="$1"
	local port="$2"
	local result=""

	# Use canyouseeme.org API or similar
	# Try portquiz.net which echoes back on any port
	result=$(wget -qO- -T 5 "http://portquiz.net:${port}/" 2>/dev/null)
	if echo "$result" | grep -q "Port ${port}"; then
		return 0
	fi

	# Alternative: try to connect to our own IP from outside perspective
	# Use online port checker API
	result=$(wget -qO- -T 8 "https://ports.yougetsignal.com/short-url-check-port.php?remoteAddress=${ip}&portNumber=${port}" 2>/dev/null)
	if echo "$result" | grep -qi "open"; then
		return 0
	fi

	return 1
}

# Helper: Reverse DNS lookup
get_reverse_dns() {
	local ip="$1"
	local hostname=""

	if command -v nslookup >/dev/null 2>&1; then
		hostname=$(nslookup "$ip" 2>/dev/null | grep "name =" | head -1 | awk '{print $NF}' | sed 's/\.$//')
	elif command -v host >/dev/null 2>&1; then
		hostname=$(host "$ip" 2>/dev/null | grep "pointer" | head -1 | awk '{print $NF}' | sed 's/\.$//')
	fi

	echo "$hostname"
}

# Helper: Check certificate expiry
check_cert_expiry() {
	local domain="$1"
	local cert_file=""

	# Try multiple possible certificate locations
	# 1. HAProxy certs directory (combined pem files)
	if [ -f "/srv/haproxy/certs/${domain}.pem" ]; then
		cert_file="/srv/haproxy/certs/${domain}.pem"
	# 2. ACME standard path
	elif [ -f "/etc/acme/${domain}/${domain}.cer" ]; then
		cert_file="/etc/acme/${domain}/${domain}.cer"
	# 3. ACME fullchain (some setups use this)
	elif [ -f "/etc/acme/${domain}/fullchain.cer" ]; then
		cert_file="/etc/acme/${domain}/fullchain.cer"
	# 4. ACME with _ecc suffix (ECC certs)
	elif [ -f "/etc/acme/${domain}_ecc/${domain}.cer" ]; then
		cert_file="/etc/acme/${domain}_ecc/${domain}.cer"
	# 5. Let's Encrypt standard path
	elif [ -f "/etc/letsencrypt/live/${domain}/cert.pem" ]; then
		cert_file="/etc/letsencrypt/live/${domain}/cert.pem"
	fi

	if [ -n "$cert_file" ] && [ -f "$cert_file" ]; then
		# Get expiry date using openssl
		local expiry_date
		expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
		if [ -n "$expiry_date" ]; then
			# Convert to epoch - try multiple date formats for compatibility
			local expiry_epoch now_epoch days_left

			# BusyBox date may not support -d with GMT format
			# Try direct parsing first
			expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null)

			# If that fails, try converting the format
			if [ -z "$expiry_epoch" ]; then
				# Parse "Apr 27 04:05:21 2026 GMT" format manually
				local month day time year
				month=$(echo "$expiry_date" | awk '{print $1}')
				day=$(echo "$expiry_date" | awk '{print $2}')
				time=$(echo "$expiry_date" | awk '{print $3}')
				year=$(echo "$expiry_date" | awk '{print $4}')

				# Convert month name to number
				case "$month" in
					Jan) month="01" ;; Feb) month="02" ;; Mar) month="03" ;;
					Apr) month="04" ;; May) month="05" ;; Jun) month="06" ;;
					Jul) month="07" ;; Aug) month="08" ;; Sep) month="09" ;;
					Oct) month="10" ;; Nov) month="11" ;; Dec) month="12" ;;
				esac

				# Try with reformatted date
				expiry_epoch=$(date -d "${year}-${month}-${day}" +%s 2>/dev/null)
			fi

			now_epoch=$(date +%s)

			if [ -n "$expiry_epoch" ] && [ -n "$now_epoch" ]; then
				days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
				echo "$days_left"
				return 0
			fi
		fi
	fi
	return 1
}

# Helper: Check if external port is accessible (basic check via firewall rules)
check_port_firewall_open() {
	local port="$1"
	local rule_name=""

	case "$port" in
		80) rule_name="HAProxy-HTTP" ;;
		443) rule_name="HAProxy-HTTPS" ;;
	esac

	# Check if firewall rule exists and is enabled
	local i=0
	while uci -q get firewall.@rule[$i] >/dev/null 2>&1; do
		local name=$(uci -q get firewall.@rule[$i].name)
		local enabled=$(uci -q get firewall.@rule[$i].enabled)
		local dest_port=$(uci -q get firewall.@rule[$i].dest_port)
		if [ "$name" = "$rule_name" ] || [ "$dest_port" = "$port" ]; then
			if [ "$enabled" != "0" ]; then
				return 0
			fi
		fi
		i=$((i + 1))
	done
	return 1
}

# Get network connectivity info (public IPs, port accessibility)
# NOTE: External port checks disabled - too slow (HTTP requests to external services)
method_get_network_info() {
	json_init
	json_add_boolean "success" 1

	local lan_ip
	lan_ip=$(get_lan_ip)
	json_add_string "lan_ip" "$lan_ip"

	# Get public IPv4 (use uclient-fetch with IPv4 only for faster response)
	json_add_object "ipv4"
	local public_ipv4
	public_ipv4=$(uclient-fetch -4 -q -T 2 -O - "http://ipv4.icanhazip.com" 2>/dev/null | tr -d '\n')
	if [ -n "$public_ipv4" ]; then
		json_add_string "address" "$public_ipv4"
		json_add_string "status" "ok"
	else
		json_add_string "status" "unavailable"
	fi
	json_close_object

	# IPv6 - skip network check (often broken/slow), just report firewall status
	json_add_object "ipv6"
	# Check if IPv6 is enabled in network config
	local ipv6_enabled=0
	if uci -q get network.wan6 >/dev/null 2>&1 || \
	   uci -q get network.wan.ipv6 >/dev/null 2>&1; then
		ipv6_enabled=1
	fi
	if [ "$ipv6_enabled" = "1" ]; then
		json_add_string "status" "configured"
	else
		json_add_string "status" "disabled"
	fi
	json_close_object

	# External port accessibility - use firewall check only (fast)
	# NOTE: Real external check would require slow HTTP requests to external services
	json_add_object "external_ports"
	local http_fw=0
	local https_fw=0
	check_port_firewall_open 80 && http_fw=1
	check_port_firewall_open 443 && https_fw=1

	json_add_object "http"
	if [ "$http_fw" = "1" ]; then
		json_add_string "status" "firewall_open"
		json_add_string "hint" "Firewall allows port 80"
	else
		json_add_string "status" "firewall_closed"
		json_add_string "hint" "Add firewall rule for port 80"
	fi
	json_close_object

	json_add_object "https"
	if [ "$https_fw" = "1" ]; then
		json_add_string "status" "firewall_open"
		json_add_string "hint" "Firewall allows port 443"
	else
		json_add_string "status" "firewall_closed"
		json_add_string "hint" "Add firewall rule for port 443"
	fi
	json_close_object
	json_close_object

	# Local firewall status
	json_add_object "firewall"
	json_add_boolean "http_open" "$http_fw"
	json_add_boolean "https_open" "$https_fw"
	if [ "$http_fw" = "1" ] && [ "$https_fw" = "1" ]; then
		json_add_string "status" "ok"
	elif [ "$http_fw" = "1" ] || [ "$https_fw" = "1" ]; then
		json_add_string "status" "partial"
	else
		json_add_string "status" "closed"
	fi
	json_close_object

	# HAProxy status
	json_add_object "haproxy"
	if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	json_close_object

	json_dump
}

# Check health status for a service
method_check_service_health() {
	local service_id domain

	read -r input
	json_load "$input"
	json_get_var service_id service_id
	json_get_var domain domain ""

	json_init

	if [ -z "$service_id" ] && [ -z "$domain" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id or domain is required"
		json_dump
		return
	fi

	# If only service_id provided, get domain from config
	if [ -z "$domain" ] && [ -n "$service_id" ]; then
		config_load "$UCI_CONFIG"
		config_get domain "$service_id" haproxy_domain ""
	fi

	json_add_boolean "success" 1
	json_add_string "service_id" "$service_id"
	json_add_string "domain" "$domain"

	# Get public IPv4 (short timeout for responsiveness)
	local public_ipv4
	public_ipv4=$(wget -qO- -T 3 "http://ipv4.icanhazip.com" 2>/dev/null | tr -d '\n')

	# Public IP info
	json_add_object "public_ip"
	json_add_string "ipv4" "${public_ipv4:-unknown}"
	json_close_object

	# DNS check with public IP comparison
	json_add_object "dns"
	if [ -n "$domain" ]; then
		local resolved_ip
		resolved_ip=$(check_dns_resolution "$domain")
		if [ -n "$resolved_ip" ]; then
			json_add_string "resolved_ip" "$resolved_ip"
			# Check if DNS points to public IP or private IP
			case "$resolved_ip" in
				10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.2*|172.30.*|172.31.*|192.168.*)
					json_add_string "status" "private"
					json_add_string "error" "DNS points to private IP (not reachable from internet)"
					json_add_string "expected" "$public_ipv4"
					;;
				*)
					if [ "$resolved_ip" = "$public_ipv4" ]; then
						json_add_string "status" "ok"
					else
						json_add_string "status" "mismatch"
						json_add_string "expected" "$public_ipv4"
						json_add_string "hint" "DNS points to different IP than your public IP"
					fi
					;;
			esac
		else
			json_add_string "status" "failed"
			json_add_string "error" "DNS resolution failed"
		fi
	else
		json_add_string "status" "none"
	fi
	json_close_object

	# External port accessibility check (firewall-based, fast)
	json_add_object "external_access"
	local http_fw=0
	local https_fw=0
	check_port_firewall_open 80 && http_fw=1
	check_port_firewall_open 443 && https_fw=1
	json_add_boolean "http_accessible" "$http_fw"
	json_add_boolean "https_accessible" "$https_fw"
	if [ "$http_fw" = "1" ] && [ "$https_fw" = "1" ]; then
		json_add_string "status" "firewall_ok"
	elif [ "$http_fw" = "1" ] || [ "$https_fw" = "1" ]; then
		json_add_string "status" "partial"
	else
		json_add_string "status" "closed"
		json_add_string "hint" "Open firewall ports 80/443 for external access"
	fi
	json_close_object

	# Certificate check
	json_add_object "certificate"
	if [ -n "$domain" ]; then
		local days_left
		days_left=$(check_cert_expiry "$domain")
		if [ -n "$days_left" ]; then
			if [ "$days_left" -lt 0 ]; then
				json_add_string "status" "expired"
			elif [ "$days_left" -lt 7 ]; then
				json_add_string "status" "critical"
			elif [ "$days_left" -lt 30 ]; then
				json_add_string "status" "warning"
			else
				json_add_string "status" "ok"
			fi
			json_add_int "days_left" "$days_left"
		else
			json_add_string "status" "missing"
		fi
	else
		json_add_string "status" "none"
	fi
	json_close_object

	# Port/Firewall check
	json_add_object "firewall"
	local http_open=0
	local https_open=0
	check_port_firewall_open 80 && http_open=1
	check_port_firewall_open 443 && https_open=1

	if [ "$http_open" = "1" ] && [ "$https_open" = "1" ]; then
		json_add_string "status" "ok"
	elif [ "$http_open" = "1" ] || [ "$https_open" = "1" ]; then
		json_add_string "status" "partial"
	else
		json_add_string "status" "closed"
	fi
	json_add_boolean "http_open" "$http_open"
	json_add_boolean "https_open" "$https_open"
	json_close_object

	# HAProxy status
	json_add_object "haproxy"
	if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	json_close_object

	json_dump
}

# Batch health check for all published services (for dashboard)
method_check_all_health() {
	json_init
	json_add_object "health"

	local wan_ip
	wan_ip=$(get_wan_ip)
	json_add_string "wan_ip" "$wan_ip"

	# Check HAProxy status
	json_add_object "haproxy"
	if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	json_close_object

	# Check Tor status
	json_add_object "tor"
	if pgrep -f "/usr/sbin/tor" >/dev/null 2>&1; then
		json_add_string "status" "running"
	else
		json_add_string "status" "stopped"
	fi
	json_close_object

	# Check firewall ports
	json_add_object "firewall"
	local http_open=0
	local https_open=0
	check_port_firewall_open 80 && http_open=1
	check_port_firewall_open 443 && https_open=1
	json_add_boolean "http_open" "$http_open"
	json_add_boolean "https_open" "$https_open"
	if [ "$http_open" = "1" ] && [ "$https_open" = "1" ]; then
		json_add_string "status" "ok"
	elif [ "$http_open" = "1" ] || [ "$https_open" = "1" ]; then
		json_add_string "status" "partial"
	else
		json_add_string "status" "closed"
	fi
	json_close_object

	# Check individual services with domains
	json_add_array "services"

	# Get all published services with domains from HAProxy (if available)
	local vhosts_json
	if haproxy_available; then
		vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null)
	fi
	if [ -n "$vhosts_json" ]; then
		local count
		count=$(echo "$vhosts_json" | jsonfilter -e '@.vhosts[*].domain' 2>/dev/null | wc -l)
		local i=0
		while [ $i -lt "$count" ]; do
			local domain enabled
			domain=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].domain" 2>/dev/null)
			enabled=$(echo "$vhosts_json" | jsonfilter -e "@.vhosts[$i].enabled" 2>/dev/null)

			i=$((i + 1))

			[ -z "$domain" ] && continue
			[ "$enabled" = "false" ] || [ "$enabled" = "0" ] && continue

			json_add_object
			json_add_string "domain" "$domain"

			# DNS check
			local resolved_ip
			resolved_ip=$(check_dns_resolution "$domain")
			if [ -n "$resolved_ip" ]; then
				json_add_string "dns_status" "ok"
				json_add_string "dns_ip" "$resolved_ip"
			else
				json_add_string "dns_status" "failed"
			fi

			# Cert check
			local days_left
			days_left=$(check_cert_expiry "$domain")
			if [ -n "$days_left" ]; then
				if [ "$days_left" -lt 0 ]; then
					json_add_string "cert_status" "expired"
				elif [ "$days_left" -lt 7 ]; then
					json_add_string "cert_status" "critical"
				elif [ "$days_left" -lt 30 ]; then
					json_add_string "cert_status" "warning"
				else
					json_add_string "cert_status" "ok"
				fi
				json_add_int "cert_days" "$days_left"
			else
				json_add_string "cert_status" "missing"
			fi

			json_close_object
		done
	fi

	json_close_array
	json_close_object

	json_dump
}

# Get certificate status for service
method_get_certificate_status() {
	local service_id

	read -r input
	json_load "$input"
	json_get_var service_id service_id

	json_init

	if [ -z "$service_id" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "service_id is required"
		json_dump
		return
	fi

	config_load "$UCI_CONFIG"
	local haproxy_domain
	config_get haproxy_domain "$service_id" haproxy_domain ""

	if [ -z "$haproxy_domain" ]; then
		json_add_boolean "success" 0
		json_add_string "error" "No domain configured"
		json_dump
		return
	fi

	# Get certificate expiry info
	local days_left
	days_left=$(check_cert_expiry "$haproxy_domain")

	json_add_boolean "success" 1
	json_add_string "domain" "$haproxy_domain"

	if [ -n "$days_left" ]; then
		if [ "$days_left" -lt 0 ]; then
			json_add_string "status" "expired"
		elif [ "$days_left" -lt 7 ]; then
			json_add_string "status" "critical"
		elif [ "$days_left" -lt 30 ]; then
			json_add_string "status" "warning"
		else
			json_add_string "status" "valid"
		fi
		json_add_int "days_left" "$days_left"
	else
		json_add_string "status" "missing"
	fi

	json_dump
}

# Get landing page config
method_get_landing_config() {
	json_init

	config_load "$UCI_CONFIG"
	local landing_path auto_regen theme
	config_get landing_path main landing_path "/www/secubox-services.html"
	config_get auto_regen main landing_auto_regen "1"
	config_get theme main landing_theme "mirrorbox"

	json_add_string "path" "$landing_path"
	json_add_boolean "auto_regen" "$auto_regen"
	json_add_string "theme" "$theme"

	# Check if file exists
	if [ -f "$landing_path" ]; then
		json_add_boolean "exists" 1
		local mtime
		mtime=$(stat -c %Y "$landing_path" 2>/dev/null)
		json_add_int "modified" "${mtime:-0}"
	else
		json_add_boolean "exists" 0
	fi

	json_dump
}

# Save landing page config
method_save_landing_config() {
	local auto_regen

	read -r input
	json_load "$input"
	json_get_var auto_regen auto_regen ""

	json_init

	[ -n "$auto_regen" ] && uci set "$UCI_CONFIG.main.landing_auto_regen=$auto_regen"
	uci commit "$UCI_CONFIG"

	json_add_boolean "success" 1

	json_dump
}

# Set landing page theme
method_set_landing_theme() {
	local theme

	read -r input
	json_load "$input"
	json_get_var theme theme "mirrorbox"

	json_init

	# Validate theme
	case "$theme" in
		mirrorbox|cyberpunk|minimal|terminal|light)
			uci set "$UCI_CONFIG.main.landing_theme=$theme"
			uci commit "$UCI_CONFIG"
			json_add_boolean "success" 1
			json_add_string "theme" "$theme"
			;;
		*)
			json_add_boolean "success" 0
			json_add_string "error" "Invalid theme: $theme"
			;;
	esac

	json_dump
}

# Main RPC interface
case "$1" in
	list)
		cat <<'EOF'
{
	"list_services": {},
	"get_service": { "service_id": "string" },
	"publish_service": { "name": "string", "local_port": "integer", "domain": "string", "tor_enabled": "boolean", "category": "string", "icon": "string" },
	"unpublish_service": { "service_id": "string" },
	"update_service": { "service_id": "string", "name": "string", "category": "string", "icon": "string" },
	"delete_service": { "service_id": "string" },
	"sync_providers": {},
	"generate_landing_page": {},
	"get_qr_data": { "service_id": "string", "url_type": "string" },
	"list_categories": {},
	"get_certificate_status": { "service_id": "string" },
	"check_service_health": { "service_id": "string", "domain": "string" },
	"check_all_health": {},
	"get_network_info": {},
	"get_landing_config": {},
	"save_landing_config": { "auto_regen": "boolean" },
	"set_landing_theme": { "theme": "string" }
}
EOF
		;;
	call)
		case "$2" in
			list_services) method_list_services ;;
			get_service) method_get_service ;;
			publish_service) method_publish_service ;;
			unpublish_service) method_unpublish_service ;;
			update_service) method_update_service ;;
			delete_service) method_delete_service ;;
			sync_providers) method_sync_providers ;;
			generate_landing_page) method_generate_landing_page ;;
			get_qr_data) method_get_qr_data ;;
			list_categories) method_list_categories ;;
			get_certificate_status) method_get_certificate_status ;;
			check_service_health) method_check_service_health ;;
			check_all_health) method_check_all_health ;;
			get_network_info) method_get_network_info ;;
			get_landing_config) method_get_landing_config ;;
			save_landing_config) method_save_landing_config ;;
			set_landing_theme) method_set_landing_theme ;;
			*)
				json_init
				json_add_boolean "error" 1
				json_add_string "message" "Unknown method: $2"
				json_dump
				;;
		esac
		;;
esac
