#!/bin/sh
# SecuBox Streamlit Platform Controller
# Copyright (C) 2025 CyberMind.fr
#
# Manages Streamlit in LXC container

CONFIG="streamlit"
LXC_NAME="streamlit"

# Paths
LXC_PATH="/srv/lxc"
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"
APPS_PATH="/srv/streamlit/apps"
DEFAULT_APP="/usr/share/streamlit/hello.py"

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

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

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

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

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

# Load configuration
load_config() {
	http_port="$(uci_get main.http_port)" || http_port="8501"
	http_host="$(uci_get main.http_host)" || http_host="0.0.0.0"
	data_path="$(uci_get main.data_path)" || data_path="/srv/streamlit"
	memory_limit="$(uci_get main.memory_limit)" || memory_limit="512M"
	active_app="$(uci_get main.active_app)" || active_app="hello"

	# Server settings
	headless="$(uci_get server.headless)" || headless="true"
	gather_stats="$(uci_get server.browser_gather_usage_stats)" || gather_stats="false"
	theme_base="$(uci_get server.theme_base)" || theme_base="dark"
	theme_primary="$(uci_get server.theme_primary_color)" || theme_primary="#0ff"

	ensure_dir "$data_path"
	ensure_dir "$data_path/apps"
	ensure_dir "$data_path/logs"
	APPS_PATH="$data_path/apps"
}

# Usage
usage() {
	cat <<EOF
SecuBox Streamlit Platform Controller

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

Commands:
  install       Download Alpine rootfs and setup LXC container
  uninstall     Remove container (preserves apps)
  update        Update Streamlit package in container
  status        Show service status (JSON format)
  logs          Show container logs
  shell         Open shell in container

  app list      List deployed apps
  app add <name> <path>  Deploy new app
  app remove <name>      Remove app
  app run <name>         Switch active app

  service-run   Start service (used by init)
  service-stop  Stop service (used by init)

Configuration:
  /etc/config/streamlit

Data directory:
  /srv/streamlit

EOF
}

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

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

	log_info "Creating Alpine rootfs for Streamlit..."

	ensure_dir "$rootfs"

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

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

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

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

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

	# Create startup script
	cat > "$rootfs/opt/start-streamlit.sh" << 'STARTUP'
#!/bin/sh
set -e

# Install Python and Streamlit on first run
if [ ! -f /opt/.installed ]; then
    echo "Installing Python 3.12 and dependencies..."
    apk update
    apk add --no-cache python3 py3-pip

    echo "Installing Streamlit..."
    pip3 install --break-system-packages streamlit 2>/dev/null || \
    pip3 install streamlit 2>/dev/null

    touch /opt/.installed
    echo "Installation complete!"
fi

# Find active app
ACTIVE_APP="${STREAMLIT_APP:-hello.py}"
APP_PATH="/srv/apps/${ACTIVE_APP}"

# Fallback to hello.py if app not found
if [ ! -f "$APP_PATH" ]; then
    if [ -f "/srv/apps/hello.py" ]; then
        APP_PATH="/srv/apps/hello.py"
    else
        echo "No app found, creating default..."
        mkdir -p /srv/apps
        cat > /srv/apps/hello.py << 'HELLO'
import streamlit as st
st.set_page_config(page_title="SecuBox Streamlit", page_icon="⚡", layout="wide")
st.title("⚡ SECUBOX STREAMLIT ⚡")
st.markdown("### Neural Data App Platform")
col1, col2, col3 = st.columns(3)
with col1:
    st.metric("Status", "ONLINE", delta="Active")
with col2:
    st.metric("Apps", "1", delta="+1")
with col3:
    st.metric("Platform", "SecuBox")
st.info("Deploy your Streamlit apps via LuCI dashboard")
HELLO
        APP_PATH="/srv/apps/hello.py"
    fi
fi

echo "Starting Streamlit with app: $APP_PATH"
cd /srv/apps

exec streamlit run "$APP_PATH" \
    --server.address="${STREAMLIT_HOST:-0.0.0.0}" \
    --server.port="${STREAMLIT_PORT:-8501}" \
    --server.headless="${STREAMLIT_HEADLESS:-true}" \
    --browser.gatherUsageStats="${STREAMLIT_STATS:-false}" \
    --theme.base="${STREAMLIT_THEME_BASE:-dark}" \
    --theme.primaryColor="${STREAMLIT_THEME_PRIMARY:-#0ff}"
STARTUP
	chmod +x "$rootfs/opt/start-streamlit.sh"

	log_info "Rootfs created successfully"
	return 0
}

# Create LXC config
lxc_create_config() {
	load_config

	ensure_dir "$(dirname "$LXC_CONFIG")"

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

	# Determine active app file
	local app_file
	if [ -f "$APPS_PATH/${active_app}.py" ]; then
		app_file="${active_app}.py"
	elif [ -f "$APPS_PATH/${active_app}" ]; then
		app_file="${active_app}"
	else
		app_file="hello.py"
	fi

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

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

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

# Environment
lxc.environment = STREAMLIT_HOST=$http_host
lxc.environment = STREAMLIT_PORT=$http_port
lxc.environment = STREAMLIT_APP=$app_file
lxc.environment = STREAMLIT_HEADLESS=$headless
lxc.environment = STREAMLIT_STATS=$gather_stats
lxc.environment = STREAMLIT_THEME_BASE=$theme_base
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary

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

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

# Init command
lxc.init.cmd = /opt/start-streamlit.sh
EOF

	log_info "LXC config created"
}

# Container control
lxc_running() {
	lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"
}

lxc_exists() {
	[ -f "$LXC_CONFIG" ] && [ -d "$LXC_ROOTFS" ]
}

lxc_stop() {
	if lxc_running; then
		log_info "Stopping Streamlit container..."
		lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true
		sleep 2
	fi
}

lxc_run() {
	load_config
	lxc_stop

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

	# Regenerate config in case settings changed
	lxc_create_config

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

# App management
cmd_app_list() {
	load_config

	echo "{"
	echo '  "apps": ['

	local first=1
	if [ -d "$APPS_PATH" ]; then
		for app in "$APPS_PATH"/*.py; do
			[ -f "$app" ] || continue
			local name=$(basename "$app" .py)
			local size=$(ls -la "$app" 2>/dev/null | awk '{print $5}')
			local mtime=$(ls -la "$app" 2>/dev/null | awk '{print $6, $7, $8}')

			# Check if this is the active app
			local is_active="false"
			if [ "$name" = "$active_app" ] || [ "${name}.py" = "$active_app" ]; then
				is_active="true"
			fi

			[ $first -eq 0 ] && echo ","
			first=0
			printf '    {"name": "%s", "path": "%s", "size": "%s", "modified": "%s", "active": %s}' \
				"$name" "$app" "$size" "$mtime" "$is_active"
		done
	fi

	echo ""
	echo "  ],"
	echo "  \"active_app\": \"$active_app\","
	echo "  \"apps_path\": \"$APPS_PATH\""
	echo "}"
}

cmd_app_add() {
	local name="$1"
	local path="$2"

	if [ -z "$name" ] || [ -z "$path" ]; then
		log_error "Usage: streamlitctl app add <name> <path>"
		return 1
	fi

	load_config

	if [ ! -f "$path" ]; then
		log_error "Source file not found: $path"
		return 1
	fi

	# Validate it looks like a Python file
	if ! echo "$path" | grep -q '\.py$'; then
		log_error "Source file must be a .py file"
		return 1
	fi

	ensure_dir "$APPS_PATH"
	local dest="$APPS_PATH/${name}.py"

	cp "$path" "$dest" || {
		log_error "Failed to copy app to $dest"
		return 1
	}

	# Register in UCI
	uci set "${CONFIG}.${name}=app"
	uci set "${CONFIG}.${name}.name=$name"
	uci set "${CONFIG}.${name}.path=${name}.py"
	uci set "${CONFIG}.${name}.enabled=1"
	uci commit "$CONFIG"

	log_info "App '$name' added successfully"
	echo '{"success": true, "message": "App added", "name": "'"$name"'"}'
}

cmd_app_remove() {
	local name="$1"

	if [ -z "$name" ]; then
		log_error "Usage: streamlitctl app remove <name>"
		return 1
	fi

	if [ "$name" = "hello" ]; then
		log_error "Cannot remove the default hello app"
		return 1
	fi

	load_config

	local app_file="$APPS_PATH/${name}.py"
	if [ -f "$app_file" ]; then
		rm -f "$app_file"
	fi

	# Remove from UCI
	uci -q delete "${CONFIG}.${name}" 2>/dev/null || true
	uci commit "$CONFIG"

	# If this was the active app, switch to hello
	if [ "$active_app" = "$name" ]; then
		uci_set main.active_app "hello"
	fi

	log_info "App '$name' removed"
	echo '{"success": true, "message": "App removed", "name": "'"$name"'"}'
}

cmd_app_run() {
	local name="$1"

	if [ -z "$name" ]; then
		log_error "Usage: streamlitctl app run <name>"
		return 1
	fi

	load_config

	local app_file="$APPS_PATH/${name}.py"
	if [ ! -f "$app_file" ]; then
		log_error "App not found: $name"
		return 1
	fi

	uci_set main.active_app "$name"

	# Restart if running
	if lxc_running; then
		log_info "Switching to app: $name (restarting container)"
		/etc/init.d/streamlit restart
	else
		log_info "Active app set to: $name"
	fi

	echo '{"success": true, "message": "Active app changed", "active_app": "'"$name"'"}'
}

# Commands
cmd_install() {
	require_root
	load_config

	log_info "Installing Streamlit Platform..."

	lxc_check_prereqs || exit 1

	# Create container
	if ! lxc_exists; then
		lxc_create_rootfs || exit 1
	fi

	lxc_create_config || exit 1

	# Setup default app
	ensure_dir "$APPS_PATH"
	if [ -f "$DEFAULT_APP" ] && [ ! -f "$APPS_PATH/hello.py" ]; then
		cp "$DEFAULT_APP" "$APPS_PATH/hello.py"
		log_info "Default hello app installed"
	fi

	# Enable service
	uci_set main.enabled '1'
	/etc/init.d/streamlit enable 2>/dev/null || true

	log_info "Installation complete!"
	log_info ""
	log_info "Start with: /etc/init.d/streamlit start"
	log_info "Web interface: http://<router-ip>:$http_port"
}

cmd_uninstall() {
	require_root

	log_info "Uninstalling Streamlit Platform..."

	# Stop service
	/etc/init.d/streamlit stop 2>/dev/null || true
	/etc/init.d/streamlit disable 2>/dev/null || true

	lxc_stop

	# Remove container (keep apps)
	if [ -d "$LXC_PATH/$LXC_NAME" ]; then
		rm -rf "$LXC_PATH/$LXC_NAME"
		log_info "Container removed"
	fi

	uci_set main.enabled '0'

	log_info "Streamlit Platform uninstalled"
	log_info "Apps preserved in: $(uci_get main.data_path)/apps"
}

cmd_update() {
	require_root
	load_config

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

	log_info "Updating Streamlit in container..."

	# Remove installed marker to force reinstall
	rm -f "$LXC_ROOTFS/opt/.installed"

	# Restart to trigger update
	if [ "$(uci_get main.enabled)" = "1" ]; then
		/etc/init.d/streamlit restart
	fi

	log_info "Update will apply on next start"
}

cmd_status() {
	load_config

	local enabled="$(uci_get main.enabled)"
	local running="false"
	local installed="false"
	local uptime=""

	if lxc_exists; then
		installed="true"
	fi

	if lxc_running; then
		running="true"
		uptime=$(lxc-info -n "$LXC_NAME" 2>/dev/null | grep -i "cpu use" | head -1 | awk '{print $3}')
	fi

	# Get LAN IP for URL
	local lan_ip
	lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1")

	# Count apps
	local app_count=0
	if [ -d "$APPS_PATH" ]; then
		app_count=$(ls -1 "$APPS_PATH"/*.py 2>/dev/null | wc -l)
	fi

	cat << EOF
{
  "enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
  "running": $running,
  "installed": $installed,
  "uptime": "$uptime",
  "http_port": $http_port,
  "data_path": "$data_path",
  "memory_limit": "$memory_limit",
  "active_app": "$active_app",
  "app_count": $app_count,
  "web_url": "http://${lan_ip}:${http_port}",
  "container_name": "$LXC_NAME"
}
EOF
}

cmd_status_text() {
	load_config

	local enabled="$(uci_get main.enabled)"
	local running="false"
	local uptime=""

	if lxc_running; then
		running="true"
		uptime=$(lxc-info -n "$LXC_NAME" 2>/dev/null | grep -i "cpu use" | head -1)
	fi

	cat << EOF
Streamlit Platform Status
==========================
Enabled:     $([ "$enabled" = "1" ] && echo "yes" || echo "no")
Running:     $([ "$running" = "true" ] && echo "yes" || echo "no")
HTTP Port:   $http_port
Data Path:   $data_path
Memory:      $memory_limit
Active App:  $active_app

Container:   $LXC_NAME
Rootfs:      $LXC_ROOTFS
Config:      $LXC_CONFIG

EOF

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

cmd_logs() {
	load_config

	local lines="${1:-100}"

	if [ -d "$data_path/logs" ]; then
		if [ -n "$(ls -A "$data_path/logs" 2>/dev/null)" ]; then
			tail -n "$lines" "$data_path/logs"/*.log 2>/dev/null || \
			cat "$data_path/logs"/*.log 2>/dev/null || \
			echo "No logs found"
		else
			echo "No logs yet"
		fi
	else
		echo "Log directory not found"
	fi

	# Also check install logs
	for logfile in /var/log/streamlit-install.log /var/log/streamlit-update.log; do
		if [ -f "$logfile" ]; then
			echo ""
			echo "=== $logfile ==="
			tail -n 50 "$logfile"
		fi
	done
}

cmd_shell() {
	require_root

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

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

cmd_service_run() {
	require_root
	load_config

	lxc_check_prereqs || exit 1
	lxc_run
}

cmd_service_stop() {
	require_root
	lxc_stop
}

# Main
case "${1:-}" in
	install)    shift; cmd_install "$@" ;;
	uninstall)  shift; cmd_uninstall "$@" ;;
	update)     shift; cmd_update "$@" ;;
	status)
		shift
		if [ "${1:-}" = "--json" ] || [ -t 0 ]; then
			cmd_status "$@"
		else
			cmd_status_text "$@"
		fi
		;;
	logs)       shift; cmd_logs "$@" ;;
	shell)      shift; cmd_shell "$@" ;;
	app)
		shift
		case "${1:-}" in
			list)   shift; cmd_app_list "$@" ;;
			add)    shift; cmd_app_add "$@" ;;
			remove) shift; cmd_app_remove "$@" ;;
			run)    shift; cmd_app_run "$@" ;;
			*)      echo "Usage: streamlitctl app {list|add|remove|run}"; exit 1 ;;
		esac
		;;
	service-run)  shift; cmd_service_run "$@" ;;
	service-stop) shift; cmd_service_stop "$@" ;;
	*)          usage ;;
esac
