#!/bin/sh

#
# SecuBox AppStore Manager
# Module discovery, installation, and management
#

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

CATALOG_DIR="/usr/share/secubox/plugins/catalog"
REMOTE_CATALOG="/tmp/secubox/remote-catalog"
STATE_DIR="/var/run/secubox"
CACHE_DIR="/var/cache/secubox/catalogs"
METADATA_FILE="/var/lib/secubox/catalog-metadata.json"
MAIN_CATALOG="/usr/share/secubox/catalog.json"
INSTALLED_CACHE="/tmp/secubox-installed-cache"
INSTALLED_CACHE_TTL=300
OPKG_STATUS_DB="/usr/lib/opkg/status"
TIMEOUT_BIN=$(command -v timeout 2>/dev/null || echo "")

# Build/refresh cache of installed packages (pkg -> version)
ensure_installed_cache() {
	local now cache_mtime cache_age tmp_cache

	now=$(date +%s)
	if [ -f "$INSTALLED_CACHE" ]; then
		cache_mtime=$(stat -c %Y "$INSTALLED_CACHE" 2>/dev/null || echo 0)
		cache_age=$((now - cache_mtime))
		if [ "$cache_age" -lt "$INSTALLED_CACHE_TTL" ]; then
			return 0
		fi
	fi

	tmp_cache="${INSTALLED_CACHE}.$$"

	# Preferred: parse opkg status database (fast, no process spawning per pkg)
	if [ -r "$OPKG_STATUS_DB" ]; then
		awk '
			/^Package: / { pkg=$2; next }
			/^Version: / { if (pkg != "") { print pkg " " $2; pkg="" } next }
			/^$/ { pkg="" }
		' "$OPKG_STATUS_DB" > "$tmp_cache" 2>/dev/null
	fi

	# Fallback: run opkg list-installed once (guarded by timeout when available)
	if [ ! -s "$tmp_cache" ]; then
		if [ -n "$TIMEOUT_BIN" ]; then
			$TIMEOUT_BIN 15 opkg list-installed 2>/dev/null | awk '{ if (NF >= 3) print $1 " " $3 }' > "$tmp_cache"
		else
			opkg list-installed 2>/dev/null | awk '{ if (NF >= 3) print $1 " " $3 }' > "$tmp_cache"
		fi

		if [ ! -s "$tmp_cache" ]; then
			rm -f "$tmp_cache"
			return 1
		fi
	fi

	mv "$tmp_cache" "$INSTALLED_CACHE" 2>/dev/null || cp "$tmp_cache" "$INSTALLED_CACHE"
	rm -f "$tmp_cache"
	return 0
}

get_installed_version_from_cache() {
	local pkg="$1"
	[ -n "$pkg" ] || return 1
	[ -f "$INSTALLED_CACHE" ] || return 1
	awk -v pkg="$pkg" '$1 == pkg { print $2; exit }' "$INSTALLED_CACHE"
}

is_pkg_installed_cached() {
	local pkg="$1"
	local version
	version=$(get_installed_version_from_cache "$pkg")
	[ -n "$version" ]
}

# Get active catalog path (from cache or embedded)
get_active_catalog() {
	if [ -f "$METADATA_FILE" ]; then
		local active_source=$(jsonfilter -i "$METADATA_FILE" -e '@.active_source' 2>/dev/null)
		if [ -n "$active_source" ] && [ -f "$CACHE_DIR/${active_source}.json" ]; then
			echo "$CACHE_DIR/${active_source}.json"
			return
		fi
	fi
	# Fallback to embedded
	echo "$MAIN_CATALOG"
}

# Check if service is enabled (will start on boot)
is_service_enabled() {
	local service="$1"
	[ -z "$service" ] && return 1
	[ -f "/etc/init.d/$service" ] || return 1
	# Check if service has symlink in /etc/rc.d/
	ls /etc/rc.d/S*"$service" >/dev/null 2>&1
}

# Check if service is running/active
is_service_active() {
	local service="$1"
	[ -z "$service" ] && return 1
	[ -f "/etc/init.d/$service" ] || return 1
	/etc/init.d/"$service" status >/dev/null 2>&1
}

# List all modules
list_modules() {
	local format="${1:-table}"
	local cache_ready=0

	mkdir -p "$CATALOG_DIR"

	if ensure_installed_cache; then
		cache_ready=1
	fi

	if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
		json_init
		json_add_array "modules"
	else
		echo "Available Modules:"
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
		printf "%-20s %-12s %-12s %-10s\n" "MODULE" "CATEGORY" "STATUS" "VERSION"
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	fi

	for catalog in "$CATALOG_DIR"/*.json; do
		[ -f "$catalog" ] || continue

		local module_id=$(jsonfilter -i "$catalog" -e '@.id' 2>/dev/null)
		local module_name=$(jsonfilter -i "$catalog" -e '@.name' 2>/dev/null)
		local module_category=$(jsonfilter -i "$catalog" -e '@.category' 2>/dev/null)
		local module_version=$(jsonfilter -i "$catalog" -e '@.version' 2>/dev/null)
		local service_name=$(jsonfilter -i "$catalog" -e '@.procd.service' 2>/dev/null)

		# Auto-detect service name if not defined in catalog
		if [ -z "$service_name" ]; then
			case "$module_id" in
				*crowdsec*) service_name="crowdsec" ;;
				*netifyd*) service_name="netifyd" ;;
				*adguardhome*) service_name="AdGuardHome" ;;
				*wireguard*) service_name="wg-quick" ;;
				*nginx*|*vhost*) service_name="nginx" ;;
				*dnscrypt*) service_name="dnscrypt-proxy" ;;
				*unbound*) service_name="unbound" ;;
				*stubby*) service_name="stubby" ;;
				*shadowsocks*) service_name="shadowsocks-libev" ;;
				*zerotier*) service_name="zerotier" ;;
				*tailscale*) service_name="tailscale" ;;
				*docker*|*portainer*) service_name="dockerd" ;;
				*homeassistant*) service_name="homeassistant" ;;
				*jellyfin*) service_name="jellyfin" ;;
				*nextcloud*) service_name="nextcloud" ;;
				*uptimekuma*|*uptime-kuma*) service_name="uptime-kuma" ;;
				*vaultwarden*) service_name="vaultwarden" ;;
				*gitea*) service_name="gitea" ;;
				*domoticz*) service_name="domoticz" ;;
				*zigbee2mqtt*) service_name="zigbee2mqtt" ;;
				*mosquitto*|*mqtt*) service_name="mosquitto" ;;
				*netdata*) service_name="netdata" ;;
			esac
		fi

		# Check if installed (support both packages.required[0] and packages[0] formats)
		local packages=$(jsonfilter -i "$catalog" -e '@.packages.required[0]' 2>/dev/null)
		[ -z "$packages" ] && packages=$(jsonfilter -i "$catalog" -e '@.packages[0]' 2>/dev/null)
		local status="available"
		local installed_version=""
		local is_installed=0
		local is_enabled=0
		local is_active=0

		if [ "$cache_ready" -eq 1 ] && [ -n "$packages" ]; then
			installed_version=$(get_installed_version_from_cache "$packages")
			if [ -n "$installed_version" ]; then
				status="installed"
				is_installed=1

				# Check enabled/active status if service defined
				if [ -n "$service_name" ]; then
					if is_service_enabled "$service_name"; then
						is_enabled=1
						status="enabled"
					fi
					if is_service_active "$service_name"; then
						is_active=1
						status="active"
					fi
				fi
			fi
		fi

		if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
			json_add_object ""
				json_add_string "id" "$module_id"
				json_add_string "name" "$module_name"
				json_add_string "category" "$module_category"
				json_add_string "version" "$module_version"
				json_add_string "status" "$status"
				[ -n "$installed_version" ] && json_add_string "installed_version" "$installed_version"
				[ -n "$service_name" ] && json_add_string "service" "$service_name"
				json_add_boolean "installed" "$is_installed"
				json_add_boolean "enabled" "$is_enabled"
				json_add_boolean "active" "$is_active"
			json_close_object
		else
			printf "%-20s %-12s %-12s %-10s\n" \
				"$module_id" "$module_category" "$status" "$module_version"
		fi
	done

	if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
		json_close_array
		json_dump
	else
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	fi
}

# Get module info
get_module_info() {
	local module_id="$1"
	local catalog_file="$CATALOG_DIR/${module_id}.json"

	if [ ! -f "$catalog_file" ]; then
		echo "ERROR: Module not found: $module_id"
		return 1
	fi

	echo "Module Information: $module_id"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	jsonfilter -i "$catalog_file" \
		-e 'Name: @.name' \
		-e 'Version: @.version' \
		-e 'Category: @.category' \
		-e 'Runtime: @.runtime' \
		-e 'Description: @.description' \
		-e 'Packages: @.packages.required' \
		-e 'Capabilities: @.capabilities' \
		-e 'Min RAM: @.requirements.min_ram_mb MB' \
		-e 'Min Storage: @.requirements.min_storage_mb MB'
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# Search modules
search_modules() {
	local query="$1"

	echo "Searching for: $query"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	for catalog in "$CATALOG_DIR"/*.json; do
		[ -f "$catalog" ] || continue

		if grep -qi "$query" "$catalog"; then
			local module_id=$(jsonfilter -i "$catalog" -e '@.id')
			local module_name=$(jsonfilter -i "$catalog" -e '@.name')
			local module_desc=$(jsonfilter -i "$catalog" -e '@.description')

			echo "[$module_id] $module_name"
			echo "  $module_desc"
			echo ""
		fi
	done
}

# Install module
install_module() {
	local module_id="$1"
	local dryrun="$2"
	local catalog_file="$CATALOG_DIR/${module_id}.json"

	if [ ! -f "$catalog_file" ]; then
		echo "ERROR: Module not found: $module_id"
		return 1
	fi

	echo "Installing module: $module_id"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	# Update state to installing
	if [ "$dryrun" != "--dryrun" ] && [ -f /usr/sbin/secubox-state ]; then
		/usr/sbin/secubox-state set "$module_id" installing "user_install" > /dev/null 2>&1 || true
	fi

	# Get package list
	local packages=$(jsonfilter -i "$catalog_file" -e '@.packages.required[@]')

	# Check if already installed
	local already_installed=true
	for pkg in $packages; do
		if ! opkg list-installed | grep -q "^$pkg "; then
			already_installed=false
			break
		fi
	done

	if [ "$already_installed" = "true" ]; then
		echo "Module already installed"
		return 0
	fi

	# Run pre-install hook if exists
	local pre_hook=$(jsonfilter -i "$catalog_file" -e '@.hooks.pre_install')
	if [ -n "$pre_hook" ] && [ -f "$pre_hook" ]; then
		echo "[1/4] Running pre-install checks..."
		if [ "$dryrun" != "--dryrun" ]; then
			if ! bash "$pre_hook"; then
				echo "ERROR: Pre-install check failed"
				# Update state to error
				[ -f /usr/sbin/secubox-state ] && /usr/sbin/secubox-state set "$module_id" error "pre_install_failed" > /dev/null 2>&1 || true
				return 1
			fi
		else
			echo "[DRYRUN] Would run: $pre_hook"
		fi
	fi

	# Install packages
	echo "[2/4] Installing packages..."
	for pkg in $packages; do
		if [ "$dryrun" = "--dryrun" ]; then
			echo "[DRYRUN] Would install: $pkg"
		else
			if ! opkg install "$pkg"; then
				echo "ERROR: Failed to install package: $pkg"
				# Update state to error
				[ -f /usr/sbin/secubox-state ] && /usr/sbin/secubox-state set "$module_id" error "package_install_failed" > /dev/null 2>&1 || true
				return 1
			fi
		fi
	done

	# Run post-install hook
	local post_hook=$(jsonfilter -i "$catalog_file" -e '@.hooks.post_install')
	if [ -n "$post_hook" ] && [ -f "$post_hook" ]; then
		echo "[3/4] Running post-install configuration..."
		if [ "$dryrun" != "--dryrun" ]; then
			if ! bash "$post_hook"; then
				echo "WARNING: Post-install hook failed"
			fi
		else
			echo "[DRYRUN] Would run: $post_hook"
		fi
	fi

	# Check if Docker app and auto-install
	local runtime=$(jsonfilter -i "$catalog_file" -e '@.runtime')
	if [ "$runtime" = "docker" ]; then
		echo "[4/5] Configuring Docker application..."
		if [ "$dryrun" != "--dryrun" ]; then
			# Detect control script name from package
			local pkg_name=$(echo "$packages" | head -n1)
			local ctl_name=""

			# Convert package name to control script name
			# secubox-app-adguardhome -> adguardhomectl
			# secubox-app-mailinabox -> mailinaboxctl
			if echo "$pkg_name" | grep -q "^secubox-app-"; then
				ctl_name=$(echo "$pkg_name" | sed 's/^secubox-app-//' | sed 's/-//g')ctl
			fi

			if [ -n "$ctl_name" ] && [ -f "/usr/sbin/$ctl_name" ]; then
				echo "  → Running /usr/sbin/$ctl_name install..."
				if /usr/sbin/$ctl_name install; then
					echo "  ✓ Docker app configured successfully"
					# Auto-enable and start the service
					/etc/init.d/$(echo "$ctl_name" | sed 's/ctl$//') enable 2>/dev/null || true
					echo "  ✓ Service enabled"
				else
					echo "  ⚠ Docker app configuration completed with warnings"
					echo "  → You may need to configure /etc/config/$(echo "$ctl_name" | sed 's/ctl$//')"
				fi
			else
				echo "  ℹ Manual configuration required"
				echo "  → Check /etc/config/ for app configuration"
			fi
		else
			echo "[DRYRUN] Would configure Docker application"
		fi
	fi

	# Health check
	echo "[5/5] Running health check..."
	if [ "$dryrun" != "--dryrun" ]; then
		sleep 2
		/usr/sbin/secubox-diagnostics health > /dev/null 2>&1 || true
	fi

	# Update state to installed
	if [ "$dryrun" != "--dryrun" ] && [ -f /usr/sbin/secubox-state ]; then
		/usr/sbin/secubox-state set "$module_id" installed "install_success" > /dev/null 2>&1 || true
	fi

	echo "✓ Module installed successfully: $module_id"
	return 0
}

# Remove module
remove_module() {
	local module_id="$1"
	local catalog_file="$CATALOG_DIR/${module_id}.json"

	if [ ! -f "$catalog_file" ]; then
		echo "ERROR: Module not found: $module_id"
		return 1
	fi

	echo "Removing module: $module_id"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	# Update state to uninstalling
	[ -f /usr/sbin/secubox-state ] && /usr/sbin/secubox-state set "$module_id" uninstalling "user_remove" > /dev/null 2>&1 || true

	# Get package list
	local packages=$(jsonfilter -i "$catalog_file" -e '@.packages.required[@]')

	# Run pre-remove hook
	local pre_hook=$(jsonfilter -i "$catalog_file" -e '@.hooks.pre_remove')
	if [ -n "$pre_hook" ] && [ -f "$pre_hook" ]; then
		echo "[1/3] Running pre-remove cleanup..."
		bash "$pre_hook" || true
	fi

	# Remove packages
	echo "[2/3] Removing packages..."
	for pkg in $packages; do
		opkg remove "$pkg" || true
	done

	# Run post-remove hook
	local post_hook=$(jsonfilter -i "$catalog_file" -e '@.hooks.post_remove')
	if [ -n "$post_hook" ] && [ -f "$post_hook" ]; then
		echo "[3/3] Running post-remove cleanup..."
		bash "$post_hook" || true
	fi

	# Update state to available
	[ -f /usr/sbin/secubox-state ] && /usr/sbin/secubox-state set "$module_id" available "remove_success" > /dev/null 2>&1 || true

	echo "✓ Module removed successfully: $module_id"
	return 0
}

# Update module
update_module() {
	local module_id="$1"

	if [ -z "$module_id" ]; then
		# Update all modules
		echo "Updating all installed modules..."
		opkg update
		opkg upgrade
	else
		# Update specific module
		echo "Updating module: $module_id"
		local catalog_file="$CATALOG_DIR/${module_id}.json"

		if [ ! -f "$catalog_file" ]; then
			echo "ERROR: Module not found: $module_id"
			return 1
		fi

		local packages=$(jsonfilter -i "$catalog_file" -e '@.packages.required[@]')

		opkg update
		for pkg in $packages; do
			opkg upgrade "$pkg"
		done
	fi
}

# Check module health
check_health() {
	echo "Module Health Check"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	local total=0
	local healthy=0
	local unhealthy=0

	for catalog in "$CATALOG_DIR"/*.json; do
		[ -f "$catalog" ] || continue

		local module_id=$(jsonfilter -i "$catalog" -e '@.id')
		local packages=$(jsonfilter -i "$catalog" -e '@.packages.required[0]')

		# Check if installed
		if [ -n "$packages" ] && opkg list-installed | grep -q "^$packages "; then
			total=$((total + 1))

			# Run health checks from manifest
			local health_checks=$(jsonfilter -i "$catalog" -e '@.health_checks.checks[@]')
			local check_failed=false

			# For simplicity, just check if service is running
			local service=$(jsonfilter -i "$catalog" -e '@.procd.service')
			if [ -n "$service" ]; then
				if /etc/init.d/"$service" status >/dev/null 2>&1; then
					echo "✓ $module_id: healthy"
					healthy=$((healthy + 1))
				else
					echo "✗ $module_id: service not running"
					unhealthy=$((unhealthy + 1))
				fi
			else
				echo "✓ $module_id: installed (no health check)"
				healthy=$((healthy + 1))
			fi
		fi
	done

	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	echo "Total modules: $total | Healthy: $healthy | Unhealthy: $unhealthy"
}

# Sync catalog from sources
sync_catalog() {
	local source="$1"

	echo "Syncing catalog..."
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	if [ -n "$source" ]; then
		/usr/sbin/secubox-catalog-sync --force "$source"
	else
		/usr/sbin/secubox-catalog-sync
	fi

	local result=$?

	if [ $result -eq 0 ]; then
		echo "✓ Catalog synced successfully"
		return 0
	else
		echo "✗ Catalog sync failed"
		return 1
	fi
}

# Check for updates (OPTIMIZED with caching)
check_updates() {
	local format="${1:-table}"
	local json_mode=0
	local cache_ready=0

	local active_catalog=$(get_active_catalog)

	if [ ! -f "$active_catalog" ]; then
		if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
			json_init
			json_add_boolean "error" true
			json_add_string "message" "No catalog available. Run secubox-appstore sync first."
			json_dump
		else
			echo "ERROR: No catalog available. Run 'secubox-appstore sync' first."
		fi
		return 1
	fi

	if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
		json_mode=1
		json_init
		json_add_array "updates"
	else
		echo "Checking for updates..."
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
		printf "%-25s %-15s %-15s %-10s\n" "APP" "INSTALLED" "AVAILABLE" "STATUS"
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	fi

	local updates_count=0

	if ensure_installed_cache; then
		cache_ready=1
	fi

	local plugin_count=$(jsonfilter -i "$active_catalog" -e '@.plugins[#]' 2>/dev/null || echo 0)
	if [ "$plugin_count" -eq 0 ]; then
		if [ "$json_mode" -eq 1 ]; then
			json_close_array
			json_add_int "total_updates_available" 0
			json_add_boolean "cache_ready" $([ "$cache_ready" -eq 1 ] && echo true || echo false)
			json_dump
		else
			echo "No plugins in catalog"
		fi
		return 0
	fi

	for plugin in $(jsonfilter -i "$active_catalog" -e '@.plugins[@.id]' 2>/dev/null); do
		local app_id="$plugin"
		local pkg_version=$(jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].pkg_version" 2>/dev/null)
		local pkg_name=$(jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null)

		[ -z "$pkg_name" ] && continue

		local installed_version=""
		if [ "$cache_ready" -eq 1 ]; then
			installed_version=$(get_installed_version_from_cache "$pkg_name")
		fi

		if [ -n "$installed_version" ] && [ -n "$pkg_version" ] && [ "$installed_version" != "$pkg_version" ]; then
			local compare_ok=1
			if [ -n "$TIMEOUT_BIN" ]; then
				$TIMEOUT_BIN 1 opkg compare-versions "$pkg_version" '>>' "$installed_version" 2>/dev/null || compare_ok=0
			else
				opkg compare-versions "$pkg_version" '>>' "$installed_version" 2>/dev/null || compare_ok=0
			fi

			if [ "$compare_ok" -eq 1 ]; then
				updates_count=$((updates_count + 1))

				if [ "$format" = "--json" ] || [ "$format" = "json" ]; then
					json_add_object ""
						json_add_string "app_id" "$app_id"
						json_add_string "installed_version" "$installed_version"
						json_add_string "available_version" "$pkg_version"
						json_add_string "type" "upgrade"
					json_close_object
				else
					printf "%-25s %-15s %-15s %-10s\n" \
						"$app_id" "$installed_version" "$pkg_version" "UPDATE"
				fi
			fi
		fi
	done

	if [ "$json_mode" -eq 1 ]; then
		json_close_array
		json_add_int "total_updates_available" "$updates_count"
		json_add_boolean "cache_ready" $([ "$cache_ready" -eq 1 ] && echo true || echo false)
		if [ "$cache_ready" -ne 1 ]; then
			json_add_boolean "cache_warning" true
			json_add_string "message" "Installed package cache unavailable (opkg status missing?)"
		fi
		json_dump
	else
		echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
		echo "Total updates available: $updates_count"
		if [ "$cache_ready" -ne 1 ]; then
			echo "WARNING: Installed package cache unavailable - versions may be outdated"
		fi
	fi
}

# Get changelog for app
get_changelog() {
	local app_id="$1"
	local from_version="$2"
	local to_version="$3"

	local active_catalog=$(get_active_catalog)

	if [ ! -f "$active_catalog" ]; then
		echo "ERROR: No catalog available"
		return 1
	fi

	echo "Changelog for $app_id"
	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

	# Extract changelog from catalog
	local changelog=$(jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].changelog")

	if [ -z "$changelog" ]; then
		echo "No changelog available for $app_id"
		return 1
	fi

	# If specific version requested
	if [ -n "$to_version" ]; then
		local version_changelog=$(jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].changelog['$to_version']")
		if [ -n "$version_changelog" ]; then
			echo "Version: $to_version"
			echo "Date: $(jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].changelog['$to_version'].date")"
			echo ""
			echo "Changes:"
			jsonfilter -i "$active_catalog" -e "@.plugins[@.id='$app_id'].changelog['$to_version'].changes[@]" | \
				while read -r change; do
					echo "  • $change"
				done
		fi
	else
		# Show all versions
		echo "Full changelog:"
		echo "$changelog" | jq -r 'to_entries[] | "\nVersion: \(.key)\nDate: \(.value.date)\nChanges:\n" + ([.value.changes[] | "  • \(.)"] | join("\n"))'
	fi

	echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# Main command router
case "$1" in
	list)
		shift
		list_modules "$@"
		;;
	info)
		get_module_info "$2"
		;;
	search)
		search_modules "$2"
		;;
	install)
		shift
		install_module "$@"
		;;
	remove)
		remove_module "$2"
		;;
	update)
		update_module "$2"
		;;
	health)
		check_health
		;;
	sync)
		shift
		sync_catalog "$@"
		;;
	check-updates)
		shift
		check_updates "$@"
		;;
	changelog)
		shift
		get_changelog "$@"
		;;
	*)
		echo "Usage: $0 {list|info|search|install|remove|update|health|sync|check-updates|changelog} [args]"
		echo ""
		echo "Commands:"
		echo "  list [--json]              List all modules"
		echo "  info <module>              Show module information"
		echo "  search <query>             Search for modules"
		echo "  install <module>           Install a module"
		echo "  remove <module>            Remove a module"
		echo "  update [module]            Update module(s)"
		echo "  health                     Check module health"
		echo "  sync [source]              Sync catalog from sources"
		echo "  check-updates [--json]     Check for available updates"
		echo "  changelog <app> [version]  Show app changelog"
		exit 1
		;;
esac
