#!/bin/sh
# SecuBox MagicMirror2 manager - LXC container support with module management
# Copyright (C) 2024-2026 CyberMind.fr

CONFIG="magicmirror2"
LXC_NAME="magicmirror2"
OPKG_UPDATED=0

# Paths
LXC_PATH="/srv/lxc"
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"

# MagicMirror paths inside container
MM_PATH="/opt/magic_mirror"
MM_MODULES="$MM_PATH/modules"
MM_CONFIG="$MM_PATH/config"

# Third-party modules registry
MM_REGISTRY_URL="https://raw.githubusercontent.com/MagicMirrorOrg/MagicMirror-3rd-Party-Modules/master/modules.json"

usage() {
	cat <<'EOF'
Usage: mm2ctl <command> [options]

Commands:
  install           Install prerequisites and create LXC container
  update            Update MagicMirror2 in container
  status            Show container and service status
  logs              Show MagicMirror2 logs (use -f to follow)
  shell             Open shell in container
  config            Generate/update config.js from UCI
  service-run       Internal: run container under procd
  service-stop      Stop container

Module Management:
  module list                    List installed modules
  module available [search]      List available third-party modules
  module install <name|url>      Install a module (MMM-name or git URL)
  module remove <name>           Remove an installed module
  module update [name]           Update module(s)

Configuration:
  set <key> <value>     Set UCI configuration value
  get <key>             Get UCI configuration value

Examples:
  mm2ctl install
  mm2ctl module install MMM-WeatherChart
  mm2ctl module install https://github.com/user/MMM-Custom
  mm2ctl module list
  mm2ctl config

Web Interface: http://<router-ip>:8082
EOF
}

require_root() { [ "$(id -u)" -eq 0 ] || { echo "Root required" >&2; exit 1; }; }

log_info() { echo "[INFO] $*"; }
log_warn() { echo "[WARN] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; }

uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }

# Load configuration with defaults
load_config() {
	port="$(uci_get main.port || echo 8082)"
	address="$(uci_get main.address || echo 0.0.0.0)"
	data_path="$(uci_get main.data_path || echo /srv/magicmirror2)"
	memory_limit="$(uci_get main.memory_limit || echo 512M)"
	language="$(uci_get main.language || echo en)"
	timezone="$(uci_get main.timezone || echo Europe/Paris)"
	units="$(uci_get main.units || echo metric)"
	electron_enabled="$(uci_get main.electron_enabled || echo 0)"

	# Display settings
	display_width="$(uci_get display.width || echo 1920)"
	display_height="$(uci_get display.height || echo 1080)"
	display_zoom="$(uci_get display.zoom || echo 1.0)"

	# Weather settings
	weather_enabled="$(uci_get weather.enabled || echo 0)"
	weather_provider="$(uci_get weather.provider || echo openweathermap)"
	weather_api_key="$(uci_get weather.api_key || echo '')"
	weather_location="$(uci_get weather.location || echo '')"
	weather_location_id="$(uci_get weather.location_id || echo '')"

	# Clock settings
	clock_enabled="$(uci_get clock.enabled || echo 1)"
	clock_display_seconds="$(uci_get clock.display_seconds || echo 1)"
	clock_show_date="$(uci_get clock.show_date || echo 1)"

	# Calendar settings
	calendar_enabled="$(uci_get calendar.enabled || echo 0)"
	calendar_max_entries="$(uci_get calendar.max_entries || echo 10)"

	# Newsfeed settings
	newsfeed_enabled="$(uci_get newsfeed.enabled || echo 0)"
	newsfeed_max_items="$(uci_get newsfeed.max_news_items || echo 5)"

	# Compliments settings
	compliments_enabled="$(uci_get compliments.enabled || echo 1)"
}

ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }

has_lxc() {
	command -v lxc-start >/dev/null 2>&1 && \
	command -v lxc-stop >/dev/null 2>&1
}

# Ensure required packages are installed
ensure_packages() {
	require_root
	for pkg in "$@"; do
		if ! opkg list-installed | grep -q "^$pkg "; then
			if [ "$OPKG_UPDATED" -eq 0 ]; then
				opkg update || return 1
				OPKG_UPDATED=1
			fi
			opkg install "$pkg" || return 1
		fi
	done
}

# =============================================================================
# LXC CONTAINER FUNCTIONS
# =============================================================================

lxc_check_prereqs() {
	log_info "Checking LXC prerequisites..."
	ensure_packages lxc lxc-common lxc-attach lxc-start lxc-stop lxc-destroy || return 1

	if [ ! -d /sys/fs/cgroup ]; then
		log_error "cgroups not mounted at /sys/fs/cgroup"
		return 1
	fi

	log_info "LXC ready"
}

lxc_create_rootfs() {
	load_config

	if [ -d "$LXC_ROOTFS" ] && [ -d "$LXC_ROOTFS/opt/magic_mirror" ]; then
		log_info "LXC rootfs already exists with MagicMirror2"
		return 0
	fi

	log_info "Creating LXC rootfs for MagicMirror2..."
	ensure_dir "$LXC_PATH/$LXC_NAME"

	lxc_create_docker_rootfs || return 1
	lxc_create_config || return 1

	log_info "LXC rootfs created successfully"
}

lxc_create_docker_rootfs() {
	local rootfs="$LXC_ROOTFS"
	local image="karsten13/magicmirror"
	local tag="latest"
	local registry="registry-1.docker.io"
	local arch
	local tmp_layer="/tmp/mm2_layer.tar"

	# Detect architecture for Docker manifest
	case "$(uname -m)" in
		x86_64) arch="amd64" ;;
		aarch64) arch="arm64" ;;
		armv7l) arch="arm" ;;
		*) arch="amd64" ;;
	esac

	log_info "Extracting MagicMirror2 Docker image ($arch)..."
	ensure_dir "$rootfs"

	# Get Docker Hub token
	local token=$(wget -q -O - "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$image:pull" | jsonfilter -e '@.token')
	[ -z "$token" ] && { log_error "Failed to get Docker Hub token"; return 1; }

	# Get manifest list
	local manifest=$(wget -q -O - --header="Authorization: Bearer $token" \
		--header="Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
		"https://$registry/v2/$image/manifests/$tag")

	# Find digest for our architecture
	local digest=$(echo "$manifest" | jsonfilter -e "@.manifests[@.platform.architecture='$arch'].digest")
	[ -z "$digest" ] && { log_error "No manifest found for $arch"; return 1; }

	# Get image manifest
	local img_manifest=$(wget -q -O - --header="Authorization: Bearer $token" \
		--header="Accept: application/vnd.docker.distribution.manifest.v2+json" \
		"https://$registry/v2/$image/manifests/$digest")

	# Extract layers and download them
	log_info "Downloading and extracting layers..."
	local layers=$(echo "$img_manifest" | jsonfilter -e '@.layers[*].digest')

	for layer_digest in $layers; do
		log_info "  Layer: ${layer_digest:7:12}..."

		# Download layer to temp file
		wget -q -O "$tmp_layer" --header="Authorization: Bearer $token" \
			"https://$registry/v2/$image/blobs/$layer_digest" || {
			log_warn "    Failed to download layer"
			continue
		}

		# Try decompression methods in order (gzip most common, then zstd, then plain tar)
		# Method 1: Try gzip
		if gunzip -t "$tmp_layer" 2>/dev/null; then
			gunzip -c "$tmp_layer" | tar xf - -C "$rootfs" 2>/dev/null || true
		# Method 2: Try zstd
		elif command -v zstd >/dev/null 2>&1 && zstd -t "$tmp_layer" 2>/dev/null; then
			zstd -d -c "$tmp_layer" | tar xf - -C "$rootfs" 2>/dev/null || true
		# Method 3: Try plain tar
		elif tar tf "$tmp_layer" >/dev/null 2>&1; then
			tar xf "$tmp_layer" -C "$rootfs" 2>/dev/null || true
		else
			# Last resort: try zstd even if test failed (might need to install it)
			if ! command -v zstd >/dev/null 2>&1; then
				log_warn "    Installing zstd for compressed layers..."
				opkg update >/dev/null 2>&1 && opkg install zstd >/dev/null 2>&1
			fi
			if command -v zstd >/dev/null 2>&1; then
				zstd -d -c "$tmp_layer" 2>/dev/null | tar xf - -C "$rootfs" 2>/dev/null || \
				gunzip -c "$tmp_layer" 2>/dev/null | tar xf - -C "$rootfs" 2>/dev/null || \
				tar xf "$tmp_layer" -C "$rootfs" 2>/dev/null || true
			fi
		fi
	done

	rm -f "$tmp_layer"

	# Configure container
	echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
	mkdir -p "$rootfs/opt/magic_mirror/config" "$rootfs/opt/magic_mirror/modules" "$rootfs/tmp"

	# Ensure proper shell setup
	log_info "Checking shell availability..."
	if [ ! -x "$rootfs/bin/sh" ]; then
		if [ -x "$rootfs/bin/bash" ]; then
			ln -sf bash "$rootfs/bin/sh"
		elif [ -x "$rootfs/bin/dash" ]; then
			ln -sf dash "$rootfs/bin/sh"
		fi
	fi

	# Create startup script (mimics Docker entrypoint.sh)
	# Use printf for shebang to avoid shell escaping issues
	printf '%s\n' '#!/bin/sh' > "$rootfs/opt/start-mm2.sh"
	cat >> "$rootfs/opt/start-mm2.sh" << 'START'
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
export NODE_ENV=production
export MM_PORT="${MM2_PORT:-8082}"
export MM_ADDRESS="${MM2_ADDRESS:-0.0.0.0}"

MM_DIR="/opt/magic_mirror"
modules_dir="${MM_DIR}/modules"
default_dir="${modules_dir}/default"
config_dir="${MM_DIR}/config"
css_dir="${MM_DIR}/css"

cd "$MM_DIR"

# Setup default modules symlink (like Docker entrypoint)
if [ -d "${MM_DIR}/__modules/default" ]; then
	mkdir -p "${modules_dir}"
	if [ ! -e "${default_dir}" ]; then
		echo "Symlinking default modules..."
		ln -sf "${MM_DIR}/__modules/default" "${modules_dir}/default"
	fi
fi

# Setup CSS files
if [ -d "${MM_DIR}/__css" ] && [ ! -f "${css_dir}/main.css" ]; then
	mkdir -p "${css_dir}"
	echo "Copying CSS files..."
	cp ${MM_DIR}/__css/* "${css_dir}/" 2>/dev/null || true
fi

# Ensure custom.css exists
[ ! -f "${css_dir}/custom.css" ] && touch "${css_dir}/custom.css"

# Wait for config to be available
for i in 1 2 3 4 5; do
	[ -f "${config_dir}/config.js" ] && break
	echo "Waiting for config.js..."
	sleep 2
done

if [ ! -f "${config_dir}/config.js" ]; then
	echo "ERROR: config.js not found, using default"
	if [ -f "${MM_DIR}/__config/config.js.sample" ]; then
		mkdir -p "${config_dir}"
		cp "${MM_DIR}/__config/config.js.sample" "${config_dir}/config.js"
	fi
fi

echo "Starting MagicMirror2 on port $MM_PORT..."

# Run MagicMirror in server-only mode
exec npm run server
START
	chmod +x "$rootfs/opt/start-mm2.sh"

	log_info "MagicMirror2 Docker image extracted successfully"
}

lxc_create_config() {
	load_config

	cat > "$LXC_CONFIG" << EOF
# MagicMirror2 LXC Configuration
lxc.uts.name = $LXC_NAME

# Root filesystem
lxc.rootfs.path = dir:$LXC_ROOTFS

# Network - use host network for simplicity
lxc.net.0.type = none

# Mounts
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.mount.entry = $data_path/config opt/magic_mirror/config none bind,create=dir 0 0
lxc.mount.entry = $data_path/modules opt/magic_mirror/modules none bind,create=dir 0 0
lxc.mount.entry = $data_path/css opt/magic_mirror/css/custom none bind,create=dir 0 0

# Environment variables
lxc.environment = MM2_PORT=$port
lxc.environment = MM2_ADDRESS=$address
lxc.environment = TZ=$timezone
lxc.environment = NODE_ENV=production

# Capabilities
lxc.cap.drop = sys_admin sys_module mac_admin mac_override

# cgroups limits
lxc.cgroup.memory.limit_in_bytes = $memory_limit

# Init
lxc.init.cmd = /opt/start-mm2.sh

# Console
lxc.console.size = 1024
lxc.pty.max = 1024
EOF

	log_info "LXC config created at $LXC_CONFIG"
}

lxc_stop() {
	if lxc-info -n "$LXC_NAME" >/dev/null 2>&1; then
		lxc-stop -n "$LXC_NAME" -k >/dev/null 2>&1 || true
	fi
}

lxc_run() {
	load_config
	lxc_stop

	if [ ! -f "$LXC_CONFIG" ]; then
		log_error "LXC not configured. Run 'mm2ctl install' first."
		return 1
	fi

	# Regenerate config to pick up UCI changes
	lxc_create_config

	# Ensure mount points exist
	ensure_dir "$data_path/config"
	ensure_dir "$data_path/modules"
	ensure_dir "$data_path/css"

	# Generate MagicMirror config.js from UCI
	generate_mm_config

	log_info "Starting MagicMirror2 LXC container..."
	log_info "Web interface: http://0.0.0.0:$port"
	exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG"
}

lxc_status() {
	load_config
	echo "=== MagicMirror2 Status ==="
	echo ""

	if lxc-info -n "$LXC_NAME" >/dev/null 2>&1; then
		lxc-info -n "$LXC_NAME"
	else
		echo "LXC container '$LXC_NAME' not found or not configured"
	fi

	echo ""
	echo "=== Configuration ==="
	echo "Port: $port"
	echo "Address: $address"
	echo "Data path: $data_path"
	echo "Language: $language"
	echo "Timezone: $timezone"

	echo ""
	echo "=== Installed Modules ==="
	list_installed_modules
}

lxc_logs() {
	if lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; then
		if [ "$1" = "-f" ]; then
			logread -f -e magicmirror2
		else
			logread -e magicmirror2 | tail -100
		fi
	else
		log_warn "Container not running. Try: logread -e magicmirror2"
	fi
}

lxc_shell() {
	lxc-attach -n "$LXC_NAME" -- /bin/sh
}

lxc_destroy() {
	lxc_stop
	if [ -d "$LXC_PATH/$LXC_NAME" ]; then
		rm -rf "$LXC_PATH/$LXC_NAME"
		log_info "LXC container destroyed"
	fi
}

# =============================================================================
# MAGICMIRROR CONFIGURATION
# =============================================================================

generate_mm_config() {
	load_config
	local config_file="$data_path/config/config.js"

	log_info "Generating MagicMirror config.js..."

	cat > "$config_file" << CONFIGJS
/* MagicMirror² Config - Generated by SecuBox mm2ctl */
let config = {
	address: "$address",
	port: $port,
	basePath: "/",
	ipWhitelist: [],
	useHttps: false,
	httpsPrivateKey: "",
	httpsCertificate: "",

	language: "$language",
	locale: "$language",
	logLevel: ["INFO", "LOG", "WARN", "ERROR"],
	timeFormat: 24,
	units: "$units",

	modules: [
CONFIGJS

	# Add clock module
	if [ "$clock_enabled" = "1" ]; then
		cat >> "$config_file" << CONFIGJS
		{
			module: "clock",
			position: "top_left",
			config: {
				displaySeconds: $([ "$clock_display_seconds" = "1" ] && echo "true" || echo "false"),
				showDate: $([ "$clock_show_date" = "1" ] && echo "true" || echo "false"),
			}
		},
CONFIGJS
	fi

	# Add weather module
	if [ "$weather_enabled" = "1" ] && [ -n "$weather_api_key" ]; then
		cat >> "$config_file" << CONFIGJS
		{
			module: "weather",
			position: "top_right",
			config: {
				weatherProvider: "$weather_provider",
				type: "current",
				location: "$weather_location",
				locationID: "$weather_location_id",
				apiKey: "$weather_api_key",
				units: "$units"
			}
		},
		{
			module: "weather",
			position: "top_right",
			header: "Weather Forecast",
			config: {
				weatherProvider: "$weather_provider",
				type: "forecast",
				location: "$weather_location",
				locationID: "$weather_location_id",
				apiKey: "$weather_api_key",
				units: "$units"
			}
		},
CONFIGJS
	fi

	# Add calendar module
	if [ "$calendar_enabled" = "1" ]; then
		cat >> "$config_file" << CONFIGJS
		{
			module: "calendar",
			header: "Calendar",
			position: "top_left",
			config: {
				maximumEntries: $calendar_max_entries,
				calendars: []
			}
		},
CONFIGJS
	fi

	# Add newsfeed module
	if [ "$newsfeed_enabled" = "1" ]; then
		cat >> "$config_file" << CONFIGJS
		{
			module: "newsfeed",
			position: "bottom_bar",
			config: {
				feeds: [
					{
						title: "BBC News",
						url: "https://feeds.bbci.co.uk/news/rss.xml"
					}
				],
				showSourceTitle: true,
				showPublishDate: true,
				broadcastNewsFeeds: true,
				broadcastNewsUpdates: true,
				maxNewsItems: $newsfeed_max_items
			}
		},
CONFIGJS
	fi

	# Add compliments module
	if [ "$compliments_enabled" = "1" ]; then
		cat >> "$config_file" << CONFIGJS
		{
			module: "compliments",
			position: "lower_third",
			config: {
				compliments: {
					anytime: ["Welcome to SecuBox MagicMirror!"],
					morning: ["Good morning!"],
					afternoon: ["Good afternoon!"],
					evening: ["Good evening!"]
				}
			}
		},
CONFIGJS
	fi

	# Load custom modules from data directory
	if [ -d "$data_path/modules" ]; then
		for module_dir in "$data_path/modules"/MMM-*; do
			if [ -d "$module_dir" ] && [ -f "$module_dir/package.json" ]; then
				local module_name=$(basename "$module_dir")
				# Check if module has a config file
				if [ -f "$data_path/config/${module_name}.json" ]; then
					local module_config=$(cat "$data_path/config/${module_name}.json")
					cat >> "$config_file" << CONFIGJS
		{
			module: "$module_name",
			position: "top_center",
			config: $module_config
		},
CONFIGJS
				else
					cat >> "$config_file" << CONFIGJS
		{
			module: "$module_name",
			position: "top_center"
		},
CONFIGJS
				fi
			fi
		done
	fi

	# Close config
	cat >> "$config_file" << CONFIGJS
	]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}
CONFIGJS

	log_info "Config generated at $config_file"
}

# =============================================================================
# MODULE MANAGEMENT
# =============================================================================

list_installed_modules() {
	load_config
	local modules_dir="$data_path/modules"

	if [ ! -d "$modules_dir" ]; then
		echo "No modules directory found"
		return
	fi

	echo ""
	# List MMM-* modules
	for module_dir in "$modules_dir"/MMM-*; do
		[ -d "$module_dir" ] || continue
		[ -f "$module_dir/package.json" ] || continue
		local name=$(basename "$module_dir")
		local version=$(jsonfilter -i "$module_dir/package.json" -e '@.version' 2>/dev/null || echo "unknown")
		local desc=$(jsonfilter -i "$module_dir/package.json" -e '@.description' 2>/dev/null | head -c 60)
		printf "  %-30s v%-10s %s\n" "$name" "$version" "$desc"
	done
	# List mm-* modules
	for module_dir in "$modules_dir"/mm-*; do
		[ -d "$module_dir" ] || continue
		[ -f "$module_dir/package.json" ] || continue
		local name=$(basename "$module_dir")
		local version=$(jsonfilter -i "$module_dir/package.json" -e '@.version' 2>/dev/null || echo "unknown")
		local desc=$(jsonfilter -i "$module_dir/package.json" -e '@.description' 2>/dev/null | head -c 60)
		printf "  %-30s v%-10s %s\n" "$name" "$version" "$desc"
	done
}

list_available_modules() {
	local search="${1:-}"
	local cache_file="/tmp/mm2_modules_cache.json"

	# Download registry if not cached or old
	if [ ! -f "$cache_file" ] || [ $(find "$cache_file" -mmin +60 2>/dev/null | wc -l) -gt 0 ]; then
		log_info "Fetching module registry..."
		wget -q -O "$cache_file" "$MM_REGISTRY_URL" || {
			log_error "Failed to fetch module registry"
			return 1
		}
	fi

	echo "Available third-party modules:"
	echo ""

	if [ -n "$search" ]; then
		grep -i "$search" "$cache_file" | head -50 || echo "No modules matching '$search'"
	else
		# Show first 30 modules
		jsonfilter -i "$cache_file" -e '@[*].title' 2>/dev/null | head -30 | while read title; do
			echo "  $title"
		done
		echo ""
		echo "Use 'mm2ctl module available <search>' to filter"
	fi
}

install_module() {
	local module_name="$1"
	load_config

	if [ -z "$module_name" ]; then
		log_error "Module name required"
		return 1
	fi

	# Check if container is running
	if ! lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; then
		log_error "Container not running. Start it first: /etc/init.d/magicmirror2 start"
		return 1
	fi

	# Use MMPM if available (has module registry)
	if command -v mmpmctl >/dev/null 2>&1; then
		log_info "Using MMPM to install: $module_name"
		mmpmctl install-module "$module_name"
		return $?
	fi

	# Fallback: manual git clone
	local modules_dir="$data_path/modules"
	ensure_dir "$modules_dir"

	local git_url=""
	local node_path="/opt/nodejs/node-v24.13.0/bin:/usr/local/bin:/usr/bin:/bin"

	# Check if it's a URL
	case "$module_name" in
		http*|git@*)
			git_url="$module_name"
			module_name=$(basename "$git_url" .git)
			;;
		MMM-*|mm-*)
			# Direct URL - no registry lookup
			git_url=""
			;;
		*)
			module_name="MMM-$module_name"
			git_url=""
			;;
	esac

	if [ -d "$modules_dir/$module_name" ]; then
		log_warn "Module $module_name already installed"
		return 0
	fi

	# If no URL provided, we need MMPM for registry lookup
	if [ -z "$git_url" ]; then
		log_error "Cannot install $module_name without URL"
		log_error "Please provide full Git URL or install MMPM (opkg install secubox-app-mmpm)"
		return 1
	fi

	log_info "Installing module: $module_name from $git_url"

	# Clone the module with proper PATH
	lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$node_path:\$PATH && cd /opt/magic_mirror/modules && git clone --depth 1 '$git_url' '$module_name'" || {
		log_error "Failed to clone module"
		return 1
	}

	# Install dependencies if package.json exists
	if lxc-attach -n "$LXC_NAME" -- test -f "/opt/magic_mirror/modules/$module_name/package.json"; then
		log_info "Installing module dependencies..."
		lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$node_path:\$PATH && cd /opt/magic_mirror/modules/$module_name && npm install --production" || {
			log_warn "Failed to install dependencies (module may still work)"
		}
	fi

	log_info "Module $module_name installed successfully"
	log_info "Restart MagicMirror2 to load the module: /etc/init.d/magicmirror2 restart"
}

remove_module() {
	local module_name="$1"
	load_config

	if [ -z "$module_name" ]; then
		log_error "Module name required"
		return 1
	fi

	local module_path="$data_path/modules/$module_name"

	if [ ! -d "$module_path" ]; then
		log_error "Module $module_name not found"
		return 1
	fi

	log_info "Removing module: $module_name"
	rm -rf "$module_path"

	# Remove config if exists
	rm -f "$data_path/config/${module_name}.json"

	log_info "Module $module_name removed"
	log_info "Restart MagicMirror2 to apply: /etc/init.d/magicmirror2 restart"
}

update_module() {
	local module_name="$1"
	load_config

	# Check if container is running
	if ! lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; then
		log_error "Container not running. Start it first: /etc/init.d/magicmirror2 start"
		return 1
	fi

	# Use MMPM if available
	if command -v mmpmctl >/dev/null 2>&1; then
		log_info "Using MMPM to update modules"
		mmpmctl upgrade "$module_name"
		return $?
	fi

	local node_path="/opt/nodejs/node-v24.13.0/bin:/usr/local/bin:/usr/bin:/bin"

	if [ -z "$module_name" ]; then
		# Update all modules
		log_info "Updating all modules..."
		# Update MMM-* modules
		for module_dir in "$data_path/modules"/MMM-*; do
			[ -d "$module_dir/.git" ] || continue
			local name=$(basename "$module_dir")
			log_info "Updating $name..."
			lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$node_path:\$PATH && cd /opt/magic_mirror/modules/$name && git pull && npm install --production 2>/dev/null" || true
		done
		# Update mm-* modules
		for module_dir in "$data_path/modules"/mm-*; do
			[ -d "$module_dir/.git" ] || continue
			local name=$(basename "$module_dir")
			log_info "Updating $name..."
			lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$node_path:\$PATH && cd /opt/magic_mirror/modules/$name && git pull && npm install --production 2>/dev/null" || true
		done
	else
		local module_path="$data_path/modules/$module_name"
		if [ ! -d "$module_path" ]; then
			log_error "Module $module_name not found"
			return 1
		fi

		log_info "Updating module: $module_name"
		lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$node_path:\$PATH && cd /opt/magic_mirror/modules/$module_name && git pull && npm install --production 2>/dev/null" || {
			log_error "Failed to update module"
			return 1
		}
	fi

	log_info "Update complete. Restart MagicMirror2 to apply."
}

# =============================================================================
# COMMANDS
# =============================================================================

cmd_install() {
	require_root
	load_config

	if ! has_lxc; then
		log_error "LXC not available. Install lxc packages first."
		exit 1
	fi

	log_info "Installing MagicMirror2..."

	# Create directories
	ensure_dir "$data_path/config"
	ensure_dir "$data_path/modules"
	ensure_dir "$data_path/css"

	lxc_check_prereqs || exit 1
	lxc_create_rootfs || exit 1

	# Generate initial config
	generate_mm_config

	uci_set main.enabled '1'
	/etc/init.d/magicmirror2 enable

	log_info "MagicMirror2 installed."
	log_info "Start with: /etc/init.d/magicmirror2 start"
	log_info "Web interface: http://<router-ip>:$port"
}

cmd_update() {
	require_root
	load_config

	log_info "Updating MagicMirror2..."
	lxc_destroy
	lxc_create_rootfs || exit 1

	if /etc/init.d/magicmirror2 enabled >/dev/null 2>&1; then
		/etc/init.d/magicmirror2 restart
	else
		log_info "Update complete. Restart manually to apply."
	fi
}

cmd_status() {
	lxc_status
}

cmd_logs() {
	lxc_logs "$@"
}

cmd_shell() {
	lxc_shell
}

cmd_config() {
	generate_mm_config
}

cmd_module() {
	local action="$1"
	shift

	case "$action" in
		list)
			list_installed_modules
			;;
		available)
			list_available_modules "$@"
			;;
		install)
			install_module "$@"
			;;
		remove|uninstall)
			remove_module "$@"
			;;
		update)
			update_module "$@"
			;;
		*)
			echo "Unknown module action: $action"
			echo "Use: list, available, install, remove, update"
			exit 1
			;;
	esac
}

cmd_service_run() {
	require_root
	load_config

	if ! has_lxc; then
		log_error "LXC not available"
		exit 1
	fi

	lxc_check_prereqs || exit 1
	lxc_run
}

cmd_service_stop() {
	require_root
	lxc_stop
}

cmd_set() {
	local key="$1"
	local value="$2"

	if [ -z "$key" ] || [ -z "$value" ]; then
		log_error "Usage: mm2ctl set <key> <value>"
		exit 1
	fi

	uci_set "$key" "$value"
	log_info "Set $key = $value"
}

cmd_get() {
	local key="$1"

	if [ -z "$key" ]; then
		log_error "Usage: mm2ctl get <key>"
		exit 1
	fi

	uci_get "$key"
}

# Main Entry Point
case "${1:-}" in
	install) shift; cmd_install "$@" ;;
	update) shift; cmd_update "$@" ;;
	status) shift; cmd_status "$@" ;;
	logs) shift; cmd_logs "$@" ;;
	shell) shift; cmd_shell "$@" ;;
	config) shift; cmd_config "$@" ;;
	module) shift; cmd_module "$@" ;;
	set) shift; cmd_set "$@" ;;
	get) shift; cmd_get "$@" ;;
	service-run) shift; cmd_service_run "$@" ;;
	service-stop) shift; cmd_service_stop "$@" ;;
	help|--help|-h|'') usage ;;
	*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac
