#!/bin/sh
# SecuBox Streamlit Platform Controller
# Copyright (C) 2025 CyberMind.fr
#
# Manages Streamlit in LXC container
# Supports multi-instance with folder-based apps and Gitea integration

# Source OpenWrt UCI functions
. /lib/functions.sh

CONFIG="streamlit"
LXC_NAME="streamlit"

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

# Logging
log_info() { echo "[INFO] $*"; logger -t streamlit "$*"; }
log_warn() { echo "[WARN] $*" >&2; logger -t streamlit -p warning "$*"; }
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; }
has_git() { command -v git >/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"

	# Gitea config
	gitea_enabled="$(uci_get gitea.enabled)" || gitea_enabled="0"
	gitea_url="$(uci_get gitea.url)" || gitea_url="http://192.168.255.1:3000"
	gitea_user="$(uci_get gitea.user)" || gitea_user="admin"
	gitea_token="$(uci_get gitea.token)" || gitea_token=""

	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 (Multi-Instance)

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

Container Commands:
  install           Download and setup LXC container
  uninstall         Remove container (preserves apps)
  update            Update Streamlit in container
  status            Show service status
  logs [app]        Show logs

App Management (folder-based):
  app list                    List all apps
  app create <name>           Create new app folder
  app delete <name>           Delete app folder
  app deploy <name> <path>    Deploy app from path/archive

Instance Management:
  instance list               List configured instances
  instance add <app> <port>   Add instance for app on port
  instance remove <name>      Remove instance
  instance start <name>       Start single instance
  instance stop <name>        Stop single instance

Gitea Integration:
  gitea setup                 Configure git credentials
  gitea clone <name> <repo>   Clone app from Gitea repo
  gitea pull <name>           Pull latest from Gitea

Exposure:
  emancipate <name> [domain]  KISS ULTIME MODE - Full exposure workflow:
                              1. DNS A record (Gandi/OVH)
                              2. Vortex DNS mesh publication
                              3. HAProxy vhost with SSL
                              4. ACME certificate
                              5. Zero-downtime reload

Service Commands:
  service-run       Start all instances (for init)
  service-stop      Stop all instances

Utility:
  shell             Open shell in container
  exec <cmd>        Execute command in container

App Folder Structure:
  /srv/streamlit/apps/<appname>/
    app.py              Main Streamlit app (or <appname>.py)
    requirements.txt    Python dependencies
    .streamlit/         Streamlit config (optional)
    ...                 Other files

Examples:
  # Create local app
  streamlitctl app create myapp
  streamlitctl instance add myapp 8502
  /etc/init.d/streamlit restart

  # Deploy from Gitea (complete workflow)
  uci set streamlit.gitea.enabled=1
  uci set streamlit.gitea.url='http://192.168.255.1:3000'
  uci set streamlit.gitea.user='admin'
  uci set streamlit.gitea.token='your-token'
  uci commit streamlit
  streamlitctl gitea setup
  streamlitctl gitea clone yijing CyberMood/yijing-oracle
  streamlitctl instance add yijing 8505
  /etc/init.d/streamlit restart
  # Access at: http://<device-ip>:8505

  # Update app from Gitea
  streamlitctl gitea pull yijing
  /etc/init.d/streamlit restart

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"

	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"

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

	log_info "Rootfs created successfully"
	return 0
}

# Create startup script for multi-instance folder-based apps
create_startup_script() {
	load_config

	local start_script="$LXC_ROOTFS/opt/start-streamlit.sh"
	cat > "$start_script" << 'STARTUP'
#!/bin/sh
export PATH=/usr/local/bin:/usr/bin:/bin:$PATH
export HOME=/root

APPS_BASE="/srv/apps"
PIDS_DIR="/var/run/streamlit"
LOG_DIR="/var/log/streamlit"
INSTANCES_CONF="/srv/apps/instances.conf"

mkdir -p "$PIDS_DIR" "$LOG_DIR"

# 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 git procps

    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

# Create default hello app if missing
if [ ! -d "$APPS_BASE/hello" ]; then
    mkdir -p "$APPS_BASE/hello"
    cat > "$APPS_BASE/hello/app.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", "Ready", delta="+1")
with col3:
    st.metric("Platform", "SecuBox")
st.info("Deploy your Streamlit apps via LuCI dashboard or CLI")
HELLO
fi

# Function to find main app file in folder
find_app_file() {
    local app_dir="$1"
    local app_name=$(basename "$app_dir")

    # Priority: app.py > main.py > <appname>.py > first .py file
    for candidate in "$app_dir/app.py" "$app_dir/main.py" "$app_dir/${app_name}.py"; do
        [ -f "$candidate" ] && { echo "$candidate"; return 0; }
    done

    # Fallback to first .py file
    local first_py=$(ls -1 "$app_dir"/*.py 2>/dev/null | head -1)
    [ -n "$first_py" ] && { echo "$first_py"; return 0; }

    return 1
}

# Function to install requirements for an app
install_requirements() {
    local app_dir="$1"
    local app_name=$(basename "$app_dir")

    # Find requirements file: requirements.txt first, then any requirements*.txt
    local req_file=""
    if [ -f "$app_dir/requirements.txt" ]; then
        req_file="$app_dir/requirements.txt"
    else
        req_file=$(ls -1 "$app_dir"/requirements*.txt 2>/dev/null | head -1)
    fi

    if [ -n "$req_file" ] && [ -f "$req_file" ]; then
        REQ_HASH=$(md5sum "$req_file" 2>/dev/null | cut -d' ' -f1)
        REQ_MARKER="/opt/.req_${app_name}_${REQ_HASH}"
        if [ ! -f "$REQ_MARKER" ]; then
            echo "Installing requirements from $(basename $req_file) for $app_name..."
            pip3 install --break-system-packages -r "$req_file" 2>/dev/null || \
            pip3 install -r "$req_file" 2>/dev/null || true
            touch "$REQ_MARKER"
        fi
    fi
}

# Function to start a single instance
start_instance() {
    local app_name="$1"
    local port="$2"
    local app_dir="$APPS_BASE/$app_name"
    local app_file=""
    local work_dir=""

    if [ -d "$app_dir" ]; then
        # Folder-based app (ZIP upload, Gitea clone, or created via CLI)
        app_file=$(find_app_file "$app_dir")
        if [ -z "$app_file" ]; then
            echo "No Python app file found in $app_dir"
            return 1
        fi
        install_requirements "$app_dir"
        work_dir="$app_dir"
    elif [ -f "$APPS_BASE/${app_name}.py" ]; then
        # Top-level single .py file (direct upload)
        app_file="$APPS_BASE/${app_name}.py"
        work_dir="$APPS_BASE"
    else
        echo "App not found: $app_name (no folder or .py file)"
        return 1
    fi

    echo "Starting instance: $app_name on port $port (file: $(basename $app_file))"

    # Change to app/work directory so relative imports work
    cd "$work_dir"

    nohup streamlit run "$(basename $app_file)" \
        --server.address="0.0.0.0" \
        --server.port="$port" \
        --server.headless=true \
        --browser.gatherUsageStats=false \
        --theme.base="${STREAMLIT_THEME_BASE:-dark}" \
        --theme.primaryColor="${STREAMLIT_THEME_PRIMARY:-#0ff}" \
        > "$LOG_DIR/${app_name}.log" 2>&1 &

    echo $! > "$PIDS_DIR/${app_name}.pid"
    echo "Instance '$app_name' started (PID: $!)"
}

# Function to stop an instance
stop_instance() {
    local app_name="$1"
    local pidfile="$PIDS_DIR/${app_name}.pid"

    if [ -f "$pidfile" ]; then
        local pid=$(cat "$pidfile")
        if kill -0 "$pid" 2>/dev/null; then
            kill "$pid"
            echo "Stopped instance '$app_name' (PID: $pid)"
        fi
        rm -f "$pidfile"
    fi
}

# Read instances from config file (format: appname:port)
if [ -f "$INSTANCES_CONF" ]; then
    echo "Loading instances from config..."
    while IFS=: read -r app_name port; do
        [ -n "$app_name" ] && [ -n "$port" ] && start_instance "$app_name" "$port"
    done < "$INSTANCES_CONF"
else
    # Fallback: single instance mode
    if [ -n "$STREAMLIT_APP" ] && [ -n "$STREAMLIT_PORT" ]; then
        start_instance "$STREAMLIT_APP" "$STREAMLIT_PORT"
    else
        # Default: start hello on 8501
        start_instance "hello" "8501"
    fi
fi

# Keep container running
echo ""
echo "Streamlit multi-instance manager running."
echo "Instances:"
ls -1 "$PIDS_DIR"/*.pid 2>/dev/null | while read f; do
    name=$(basename "$f" .pid)
    pid=$(cat "$f")
    echo "  - $name (PID: $pid)"
done

# Monitor loop
while true; do
    sleep 30
    if ! pgrep -f "streamlit" >/dev/null; then
        echo "No streamlit processes running, exiting..."
        exit 1
    fi
done
STARTUP
	chmod +x "$start_script"
}

# Generate instances.conf from UCI
generate_instances_conf() {
	load_config

	local conf_file="$data_path/apps/instances.conf"
	> "$conf_file"

	# Iterate over instance sections
	_add_instance_to_conf() {
		local section="$1"
		local inst_enabled inst_app inst_port
		config_get inst_enabled "$section" enabled "0"
		config_get inst_app "$section" app ""
		config_get inst_port "$section" port ""

		if [ "$inst_enabled" = "1" ] && [ -n "$inst_app" ] && [ -n "$inst_port" ]; then
			echo "${inst_app}:${inst_port}" >> "$conf_file"
		fi
	}

	config_load "$CONFIG"
	config_foreach _add_instance_to_conf instance

	# If no instances, add default
	if [ ! -s "$conf_file" ]; then
		echo "hello:8501" > "$conf_file"
	fi

	log_debug "Generated instances.conf: $(cat $conf_file | tr '\n' ' ')"
}

# Create LXC config
lxc_create_config() {
	load_config

	ensure_dir "$(dirname "$LXC_CONFIG")"

	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

	cat > "$LXC_CONFIG" << EOF
# Streamlit Platform LXC Configuration (Multi-Instance)
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
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
lxc.mount.entry = /tmp/secubox tmp/secubox none bind,create=dir 0 0

# Environment
lxc.environment = STREAMLIT_THEME_BASE=$theme_base
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary

# Terminal
lxc.tty.max = 0
lxc.pty.max = 256

# Standard character devices
lxc.cgroup2.devices.allow = c 1:* rwm
lxc.cgroup2.devices.allow = c 5:* rwm
lxc.cgroup2.devices.allow = c 136:* rwm

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

# Resource limits
lxc.cgroup2.memory.max = $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

	lxc_create_config
	create_startup_script
	generate_instances_conf

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

lxc_exec() {
	if ! lxc_running; then
		log_error "Container not running. Start with: /etc/init.d/streamlit start"
		return 1
	fi
	lxc-attach -n "$LXC_NAME" -- env PATH=/usr/local/bin:/usr/bin:/bin "$@"
}

# App management commands
cmd_app_list() {
	load_config

	echo "Streamlit Apps:"
	echo "---------------"

	if [ -d "$APPS_PATH" ]; then
		for app_dir in "$APPS_PATH"/*/; do
			[ -d "$app_dir" ] || continue
			local name=$(basename "$app_dir")

			# Find main file
			local main_file=""
			for candidate in "$app_dir/app.py" "$app_dir/main.py" "$app_dir/${name}.py"; do
				[ -f "$candidate" ] && { main_file=$(basename "$candidate"); break; }
			done
			[ -z "$main_file" ] && main_file=$(ls -1 "$app_dir"/*.py 2>/dev/null | head -1 | xargs basename 2>/dev/null)

			# Check for requirements (requirements.txt or any requirements*.txt)
			local has_req="no"
			[ -f "$app_dir/requirements.txt" ] && has_req="yes"
			[ "$has_req" = "no" ] && ls -1 "$app_dir"/requirements*.txt >/dev/null 2>&1 && has_req="yes"

			# Check for git repo
			local has_git="no"
			[ -d "$app_dir/.git" ] && has_git="yes"

			printf "  %-20s main:%-15s req:%-3s git:%-3s\n" "$name" "${main_file:-none}" "$has_req" "$has_git"
		done
	fi

	# Also list legacy single-file apps
	for app_file in "$APPS_PATH"/*.py; do
		[ -f "$app_file" ] || continue
		local name=$(basename "$app_file" .py)
		# Skip if there's a folder with same name
		[ -d "$APPS_PATH/$name" ] && continue
		printf "  %-20s (legacy single-file)\n" "$name"
	done
}

cmd_app_create() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "App name required"; return 1; }

	# Validate name
	echo "$name" | grep -qE '^[a-z][a-z0-9_-]*$' || {
		log_error "Invalid app name. Use lowercase, numbers, underscore, hyphen."
		return 1
	}

	local app_dir="$APPS_PATH/$name"

	if [ -d "$app_dir" ]; then
		log_error "App '$name' already exists"
		return 1
	fi

	log_info "Creating app folder: $name"

	ensure_dir "$app_dir"
	ensure_dir "$app_dir/.streamlit"

	# Create template app.py
	cat > "$app_dir/app.py" << 'APPTEMPLATE'
import streamlit as st

st.set_page_config(
    page_title="My Streamlit App",
    page_icon="🚀",
    layout="wide"
)

st.title("🚀 My Streamlit App")
st.write("Edit this file to build your app!")

# Example widgets
name = st.text_input("Enter your name:")
if name:
    st.success(f"Hello, {name}!")
APPTEMPLATE

	# Create empty requirements.txt
	cat > "$app_dir/requirements.txt" << 'REQTEMPLATE'
# Add your Python dependencies here, one per line
# Example:
# pandas>=2.0.0
# numpy
# plotly
REQTEMPLATE

	# Create .streamlit/config.toml
	cat > "$app_dir/.streamlit/config.toml" << 'CONFIGTEMPLATE'
[theme]
base = "dark"
primaryColor = "#0ff"

[server]
headless = true
CONFIGTEMPLATE

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

	log_info "App '$name' created at $app_dir"
	log_info "Next steps:"
	log_info "  1. Edit $app_dir/app.py"
	log_info "  2. Add dependencies to $app_dir/requirements.txt"
	log_info "  3. Start with: streamlitctl instance add $name <port>"
}

cmd_app_delete() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "App name required"; return 1; }

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

	local app_dir="$APPS_PATH/$name"

	# Stop any running instance
	cmd_instance_stop "$name" 2>/dev/null

	# Remove directory
	if [ -d "$app_dir" ]; then
		rm -rf "$app_dir"
		log_info "App folder removed: $app_dir"
	fi

	# Remove legacy file
	if [ -f "$APPS_PATH/${name}.py" ]; then
		rm -f "$APPS_PATH/${name}.py"
	fi

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

	log_info "App '$name' deleted"
}

cmd_app_deploy() {
	require_root
	load_config

	local name="$1"
	local source="$2"

	[ -z "$name" ] || [ -z "$source" ] && {
		log_error "Usage: streamlitctl app deploy <name> <source_path>"
		return 1
	}

	local app_dir="$APPS_PATH/$name"
	ensure_dir "$app_dir"

	if [ -d "$source" ]; then
		# Copy directory contents
		log_info "Deploying from directory: $source"
		cp -r "$source"/* "$app_dir/"
	elif [ -f "$source" ]; then
		case "$source" in
			*.tar.gz|*.tgz)
				log_info "Extracting archive: $source"
				tar -xzf "$source" -C "$app_dir"
				;;
			*.zip)
				log_info "Extracting zip: $source"
				unzip -q "$source" -d "$app_dir"
				;;
			*.py)
				log_info "Deploying single file: $source"
				cp "$source" "$app_dir/app.py"
				;;
			*)
				log_error "Unsupported file type"
				return 1
				;;
		esac
	else
		log_error "Source not found: $source"
		return 1
	fi

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

	log_info "App '$name' deployed to $app_dir"

	# Auto-package for P2P distribution
	if [ -x /usr/sbin/secubox-content-pkg ]; then
		log_info "Packaging app for P2P distribution..."
		/usr/sbin/secubox-content-pkg streamlit "$name" 2>/dev/null && \
			log_info "App packaged for mesh distribution"
	fi
}

# Instance management
cmd_instance_list() {
	load_config

	echo "Streamlit Instances:"
	echo "--------------------"

	local found=0
	_print_instance() {
		local section="$1"
		local name app port enabled
		config_get name "$section" name "$section"
		config_get app "$section" app ""
		config_get port "$section" port ""
		config_get enabled "$section" enabled "0"

		[ -z "$app" ] || [ -z "$port" ] && return

		found=1
		local status="disabled"
		[ "$enabled" = "1" ] && status="enabled"

		local running=""
		if lxc_running && [ -f "$LXC_ROOTFS/var/run/streamlit/${app}.pid" ]; then
			running=" [RUNNING]"
		fi

		printf "  %-15s app:%-15s port:%-5s [%s]%s\n" "$section" "$app" "$port" "$status" "$running"
	}

	config_load "$CONFIG"
	config_foreach _print_instance instance

	[ "$found" = "0" ] && echo "  (no instances configured)"
}

cmd_instance_add() {
	require_root
	load_config

	local app="$1"
	local port="$2"

	[ -z "$app" ] || [ -z "$port" ] && {
		log_error "Usage: streamlitctl instance add <app_name> <port>"
		return 1
	}

	# Validate port
	echo "$port" | grep -qE '^[0-9]+$' || {
		log_error "Port must be a number"
		return 1
	}

	# Check app exists
	if [ ! -d "$APPS_PATH/$app" ] && [ ! -f "$APPS_PATH/${app}.py" ]; then
		log_error "App not found: $app"
		log_info "Create with: streamlitctl app create $app"
		return 1
	fi

	# Use app name as instance name
	local instance_name="$app"

	uci set "${CONFIG}.${instance_name}=instance"
	uci set "${CONFIG}.${instance_name}.name=$instance_name"
	uci set "${CONFIG}.${instance_name}.app=$app"
	uci set "${CONFIG}.${instance_name}.port=$port"
	uci set "${CONFIG}.${instance_name}.enabled=1"
	uci commit "$CONFIG"

	log_info "Instance added: $app on port $port"
	log_info "Restart service to apply: /etc/init.d/streamlit restart"
}

cmd_instance_remove() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "Instance name required"; return 1; }

	cmd_instance_stop "$name" 2>/dev/null

	uci -q delete "${CONFIG}.${name}" 2>/dev/null || {
		log_error "Instance not found: $name"
		return 1
	}
	uci commit "$CONFIG"

	# Regenerate instances.conf
	generate_instances_conf

	log_info "Instance '$name' removed"
}

cmd_instance_start() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "Instance name required"; return 1; }

	# Get instance config
	local app=$(uci_get "${name}.app")
	local port=$(uci_get "${name}.port")

	[ -z "$app" ] || [ -z "$port" ] && {
		log_error "Instance '$name' not found or incomplete config"
		return 1
	}

	if ! lxc_running; then
		log_error "Container not running. Start with: /etc/init.d/streamlit start"
		return 1
	fi

	log_info "Starting instance '$name' (app: $app, port: $port)..."

	lxc_exec sh -c "
		WORK_DIR=''
		APP_FILE=''

		if [ -d /srv/apps/$app ]; then
			WORK_DIR='/srv/apps/$app'
			cd \"\$WORK_DIR\"

			# Find main file in folder
			for f in app.py main.py ${app}.py; do
				[ -f \"\$f\" ] && { APP_FILE=\"\$f\"; break; }
			done
			[ -z \"\$APP_FILE\" ] && APP_FILE=\$(ls -1 *.py 2>/dev/null | head -1)

			# Install requirements (requirements.txt or any requirements*.txt)
			REQ_FILE=''
			[ -f requirements.txt ] && REQ_FILE='requirements.txt'
			[ -z \"\$REQ_FILE\" ] && REQ_FILE=\$(ls -1 requirements*.txt 2>/dev/null | head -1)
			[ -n \"\$REQ_FILE\" ] && pip3 install --break-system-packages -r \"\$REQ_FILE\" 2>/dev/null
		elif [ -f /srv/apps/${app}.py ]; then
			WORK_DIR='/srv/apps'
			APP_FILE='${app}.py'
			cd \"\$WORK_DIR\"
		fi

		[ -z \"\$APP_FILE\" ] && { echo 'No Python file found for $app'; exit 1; }

		# Kill existing
		[ -f /var/run/streamlit/${app}.pid ] && kill \$(cat /var/run/streamlit/${app}.pid) 2>/dev/null

		mkdir -p /var/run/streamlit /var/log/streamlit

		nohup streamlit run \"\$APP_FILE\" \
			--server.address=0.0.0.0 \
			--server.port=$port \
			--server.headless=true \
			> /var/log/streamlit/${app}.log 2>&1 &

		echo \$! > /var/run/streamlit/${app}.pid
		echo \"Started \$APP_FILE on port $port (PID: \$!)\"
	"
}

cmd_instance_stop() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "Instance name required"; return 1; }

	local app=$(uci_get "${name}.app")
	[ -z "$app" ] && app="$name"

	if ! lxc_running; then
		return 0
	fi

	log_info "Stopping instance '$name'..."

	lxc_exec sh -c "
		if [ -f /var/run/streamlit/${app}.pid ]; then
			kill \$(cat /var/run/streamlit/${app}.pid) 2>/dev/null
			rm -f /var/run/streamlit/${app}.pid
			echo 'Stopped'
		else
			echo 'Not running'
		fi
	"
}

# Gitea integration
cmd_gitea_setup() {
	require_root
	load_config

	if [ -z "$gitea_token" ]; then
		log_error "Gitea token not configured"
		log_info "Set with: uci set streamlit.gitea.token='your-token' && uci commit streamlit"
		return 1
	fi

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

	log_info "Configuring git credentials..."

	local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')

	lxc_exec sh -c "
		git config --global user.name '$gitea_user'
		git config --global user.email '${gitea_user}@localhost'
		git config --global credential.helper store

		rm -rf ~/.git-credentials
		cat > ~/.git-credentials << CRED
https://${gitea_user}:${gitea_token}@${gitea_host}
http://${gitea_user}:${gitea_token}@${gitea_host}
CRED
		chmod 600 ~/.git-credentials
	"

	log_info "Git credentials configured"
}

cmd_gitea_clone() {
	require_root
	load_config

	local name="$1"
	local repo="$2"

	[ -z "$name" ] || [ -z "$repo" ] && {
		log_error "Usage: streamlitctl gitea clone <app_name> <repo>"
		log_info "  repo can be: username/reponame or full URL"
		return 1
	}

	if [ "$gitea_enabled" != "1" ]; then
		log_error "Gitea integration not enabled"
		log_info "Enable with: uci set streamlit.gitea.enabled=1 && uci commit streamlit"
		return 1
	fi

	if [ -z "$gitea_token" ]; then
		log_error "Gitea token not configured"
		return 1
	fi

	local app_dir="$APPS_PATH/$name"

	# Build clone URL
	local clone_url
	if echo "$repo" | grep -q '^http'; then
		clone_url="$repo"
	else
		local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')
		clone_url="http://${gitea_user}:${gitea_token}@${gitea_host}/${repo}.git"
	fi

	if [ -d "$app_dir/.git" ]; then
		log_info "Repository already cloned, pulling latest..."
		cd "$app_dir" && git pull
	else
		log_info "Cloning $repo to $name..."
		ensure_dir "$(dirname "$app_dir")"
		rm -rf "$app_dir"

		git clone "$clone_url" "$app_dir" || {
			log_error "Failed to clone repository"
			return 1
		}
	fi

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

	log_info "App '$name' cloned from Gitea"
	log_info "Add instance with: streamlitctl instance add $name <port>"
}

cmd_gitea_pull() {
	require_root
	load_config

	local name="$1"
	[ -z "$name" ] && { log_error "App name required"; return 1; }

	local app_dir="$APPS_PATH/$name"

	if [ ! -d "$app_dir/.git" ]; then
		log_error "App '$name' is not a git repository"
		return 1
	fi

	log_info "Pulling latest for '$name'..."
	cd "$app_dir" && git pull

	log_info "Update complete"
}

# ===========================================
# KISS ULTIME MODE - Emancipate
# ===========================================

_emancipate_dns() {
	local name="$1"
	local domain="$2"
	local default_zone=$(uci -q get dns-provider.main.zone)
	local provider=$(uci -q get dns-provider.main.provider)
	local vortex_wildcard=$(uci -q get vortex-dns.master.wildcard_domain)

	# Check if dnsctl is available
	if ! command -v dnsctl >/dev/null 2>&1; then
		log_warn "[DNS] dnsctl not found, skipping external DNS"
		return 1
	fi

	# Get public IP
	local public_ip=$(curl -s --connect-timeout 5 https://ipv4.icanhazip.com 2>/dev/null | tr -d '\n')
	[ -z "$public_ip" ] && { log_warn "[DNS] Cannot detect public IP, skipping DNS"; return 1; }

	# Detect zone from domain suffix (try known zones)
	local zone=""
	local subdomain=""
	for z in "secubox.in" "maegia.tv" "cybermind.fr"; do
		if echo "$domain" | grep -q "\.${z}$"; then
			zone="$z"
			subdomain=$(echo "$domain" | sed "s/\.${z}$//")
			break
		elif [ "$domain" = "$z" ]; then
			zone="$z"
			subdomain="@"
			break
		fi
	done

	# Fallback to default zone if no match
	if [ -z "$zone" ]; then
		zone="$default_zone"
		subdomain=$(echo "$domain" | sed "s/\.${zone}$//")
	fi

	[ -z "$zone" ] && { log_warn "[DNS] No zone detected, skipping external DNS"; return 1; }

	log_info "[DNS] Registering $subdomain.$zone -> $public_ip via $provider"

	# Register on the published domain's zone
	dnsctl -z "$zone" add A "$subdomain" "$public_ip" 3600

	# Also register on vortex node subdomain (e.g., myapp.gk2.secubox.in)
	if [ -n "$vortex_wildcard" ]; then
		local vortex_zone=$(echo "$vortex_wildcard" | sed 's/^[^.]*\.//')
		local vortex_node=$(echo "$vortex_wildcard" | cut -d. -f1)
		local vortex_subdomain="${name}.${vortex_node}"
		log_info "[DNS] Registering $vortex_subdomain.$vortex_zone -> $public_ip (vortex node)"
		dnsctl -z "$vortex_zone" add A "$vortex_subdomain" "$public_ip" 3600
	fi

	log_info "[DNS] Verify with: dnsctl verify $domain"
}

_emancipate_vortex() {
	local name="$1"
	local domain="$2"

	# Check if vortexctl is available
	if ! command -v vortexctl >/dev/null 2>&1; then
		log_info "[VORTEX] vortexctl not found, skipping mesh publication"
		return 0
	fi

	# Check if vortex-dns is enabled
	local vortex_enabled=$(uci -q get vortex-dns.main.enabled)

	if [ "$vortex_enabled" = "1" ]; then
		log_info "[VORTEX] Publishing $name as $domain to mesh"
		vortexctl mesh publish "$name" "$domain" 2>/dev/null
	else
		log_info "[VORTEX] Vortex DNS disabled, skipping mesh publication"
	fi
}

_emancipate_haproxy() {
	local name="$1"
	local domain="$2"
	local port="$3"

	log_info "[HAPROXY] Creating vhost for $domain -> port $port"

	# Create backend
	local backend_name="streamlit_${name}"
	uci set haproxy.${backend_name}=backend
	uci set haproxy.${backend_name}.name="$backend_name"
	uci set haproxy.${backend_name}.mode="http"
	uci set haproxy.${backend_name}.balance="roundrobin"
	uci set haproxy.${backend_name}.enabled="1"

	# Create server
	local server_name="${backend_name}_srv"
	uci set haproxy.${server_name}=server
	uci set haproxy.${server_name}.backend="$backend_name"
	uci set haproxy.${server_name}.name="streamlit"
	uci set haproxy.${server_name}.address="192.168.255.1"
	uci set haproxy.${server_name}.port="$port"
	uci set haproxy.${server_name}.weight="100"
	uci set haproxy.${server_name}.check="1"
	uci set haproxy.${server_name}.enabled="1"

	# Create vhost with SSL
	local vhost_name=$(echo "$domain" | tr '.-' '_')
	uci set haproxy.${vhost_name}=vhost
	uci set haproxy.${vhost_name}.domain="$domain"
	uci set haproxy.${vhost_name}.backend="$backend_name"
	uci set haproxy.${vhost_name}.ssl="1"
	uci set haproxy.${vhost_name}.ssl_redirect="1"
	uci set haproxy.${vhost_name}.acme="1"
	uci set haproxy.${vhost_name}.enabled="1"

	uci commit haproxy

	# Generate HAProxy config
	if command -v haproxyctl >/dev/null 2>&1; then
		haproxyctl generate 2>/dev/null
	fi
}

_emancipate_ssl() {
	local domain="$1"

	log_info "[SSL] Requesting certificate for $domain"

	# Check if haproxyctl is available
	if ! command -v haproxyctl >/dev/null 2>&1; then
		log_warn "[SSL] haproxyctl not found, skipping SSL"
		return 1
	fi

	# haproxyctl cert add handles ACME webroot mode
	haproxyctl cert add "$domain" 2>&1 | while read line; do
		echo "  $line"
	done

	if [ -f "/srv/haproxy/certs/$domain.pem" ]; then
		log_info "[SSL] Certificate obtained successfully"
	else
		log_warn "[SSL] Certificate request may still be pending"
		log_warn "[SSL] Check with: haproxyctl cert verify $domain"
	fi
}

_emancipate_reload() {
	log_info "[RELOAD] Applying HAProxy configuration"
	# Generate fresh config
	haproxyctl generate 2>/dev/null
	# Restart for clean state with new vhosts/certs
	log_info "[RELOAD] Restarting HAProxy for clean state..."
	/etc/init.d/haproxy restart 2>/dev/null
	sleep 1
	# Verify HAProxy is running
	if pgrep haproxy >/dev/null 2>&1; then
		log_info "[RELOAD] HAProxy restarted successfully"
	else
		log_warn "[RELOAD] HAProxy may not have started properly"
	fi
}

cmd_emancipate() {
	require_root
	load_config

	local name="$1"
	local domain="$2"

	[ -z "$name" ] && { log_error "App name required"; usage; return 1; }

	# Check if app exists
	if [ ! -d "$APPS_PATH/$name" ] && [ ! -f "$APPS_PATH/${name}.py" ]; then
		log_error "App '$name' not found"
		log_error "Create first: streamlitctl app create $name"
		return 1
	fi

	# Get instance port from UCI
	local port=$(uci -q get ${CONFIG}.${name}.port)
	if [ -z "$port" ]; then
		log_error "No instance configured for app '$name'"
		log_error "Add instance first: streamlitctl instance add $name <port>"
		return 1
	fi

	# Domain is optional - can be auto-generated from vortex wildcard
	if [ -z "$domain" ]; then
		local vortex_wildcard=$(uci -q get vortex-dns.master.wildcard_domain)
		if [ -n "$vortex_wildcard" ]; then
			local vortex_zone=$(echo "$vortex_wildcard" | sed 's/^[^.]*\.//')
			local vortex_node=$(echo "$vortex_wildcard" | cut -d. -f1)
			domain="${name}.${vortex_node}.${vortex_zone}"
			log_info "Auto-generated domain: $domain"
		else
			log_error "Domain required: streamlitctl emancipate $name <domain>"
			return 1
		fi
	fi

	echo ""
	echo "=============================================="
	echo "  KISS ULTIME MODE: Emancipating $name"
	echo "=============================================="
	echo ""
	echo "  App:    $name"
	echo "  Domain: $domain"
	echo "  Port:   $port"
	echo ""

	# Step 1: DNS Registration (external provider)
	_emancipate_dns "$name" "$domain"

	# Step 2: Vortex DNS (mesh registration)
	_emancipate_vortex "$name" "$domain"

	# Step 3: HAProxy vhost + backend
	_emancipate_haproxy "$name" "$domain" "$port"

	# Step 4: SSL Certificate
	_emancipate_ssl "$domain"

	# Step 5: Reload HAProxy
	_emancipate_reload

	# Mark app as emancipated
	uci set ${CONFIG}.${name}.emancipated="1"
	uci set ${CONFIG}.${name}.emancipated_at="$(date -Iseconds)"
	uci set ${CONFIG}.${name}.domain="$domain"
	uci commit ${CONFIG}

	echo ""
	echo "=============================================="
	echo "  EMANCIPATION COMPLETE"
	echo "=============================================="
	echo ""
	echo "  Site:    https://$domain"
	echo "  Status:  Published and SSL-protected"
	echo "  Mesh:    $(uci -q get vortex-dns.main.enabled | grep -q 1 && echo 'Published' || echo 'Disabled')"
	echo ""
	echo "  Verify:"
	echo "    curl -v https://$domain"
	echo "    dnsctl verify $domain"
	echo "    haproxyctl cert verify $domain"
	echo ""
}

# Main commands
cmd_install() {
	require_root
	load_config

	log_info "Installing Streamlit Platform..."

	lxc_check_prereqs || exit 1

	if ! lxc_exists; then
		lxc_create_rootfs || exit 1
	fi

	lxc_create_config || exit 1
	create_startup_script

	# Setup default app
	ensure_dir "$APPS_PATH/hello"

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

	log_info "Installation complete!"
	log_info ""
	log_info "Next steps:"
	log_info "  1. Create app: streamlitctl app create myapp"
	log_info "  2. Add instance: streamlitctl instance add myapp 8501"
	log_info "  3. Start: /etc/init.d/streamlit start"
}

cmd_uninstall() {
	require_root

	log_info "Uninstalling Streamlit Platform..."

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

	lxc_stop

	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"
		return 1
	fi

	log_info "Updating Streamlit..."

	rm -f "$LXC_ROOTFS/opt/.installed"

	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"
	lxc_running && running="true"

	cat << EOF
Streamlit Platform Status
==========================
Enabled:     $([ "$enabled" = "1" ] && echo "yes" || echo "no")
Running:     $([ "$running" = "true" ] && echo "yes" || echo "no")
Data Path:   $data_path
Memory:      $memory_limit
Container:   $LXC_NAME

EOF

	echo "Apps:"
	cmd_app_list | tail -n +3

	echo ""
	echo "Instances:"
	cmd_instance_list | tail -n +3
}

cmd_logs() {
	load_config

	local app="${1:-}"

	if [ -n "$app" ]; then
		local log_file="$data_path/logs/${app}.log"
		if [ -f "$log_file" ]; then
			tail -100 "$log_file"
		else
			# Try inside container
			lxc_exec cat /var/log/streamlit/${app}.log 2>/dev/null || echo "No logs for '$app'"
		fi
	else
		# Show all logs
		for log_file in "$data_path/logs"/*.log; do
			[ -f "$log_file" ] || continue
			echo "=== $(basename "$log_file") ==="
			tail -50 "$log_file"
			echo ""
		done
	fi
}

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; cmd_status "$@" ;;
	logs)       shift; cmd_logs "$@" ;;
	shell)      shift; cmd_shell "$@" ;;

	app)
		shift
		case "${1:-}" in
			list)   shift; cmd_app_list "$@" ;;
			create) shift; cmd_app_create "$@" ;;
			delete) shift; cmd_app_delete "$@" ;;
			deploy) shift; cmd_app_deploy "$@" ;;
			*)      echo "Usage: streamlitctl app {list|create|delete|deploy}"; exit 1 ;;
		esac
		;;

	instance)
		shift
		case "${1:-}" in
			list)   shift; cmd_instance_list "$@" ;;
			add)    shift; cmd_instance_add "$@" ;;
			remove) shift; cmd_instance_remove "$@" ;;
			start)  shift; cmd_instance_start "$@" ;;
			stop)   shift; cmd_instance_stop "$@" ;;
			*)      echo "Usage: streamlitctl instance {list|add|remove|start|stop}"; exit 1 ;;
		esac
		;;

	gitea)
		shift
		case "${1:-}" in
			setup) shift; cmd_gitea_setup "$@" ;;
			clone) shift; cmd_gitea_clone "$@" ;;
			pull)  shift; cmd_gitea_pull "$@" ;;
			*)     echo "Usage: streamlitctl gitea {setup|clone|pull}"; exit 1 ;;
		esac
		;;

	service-run)  shift; cmd_service_run "$@" ;;
	service-stop) shift; cmd_service_stop "$@" ;;

	emancipate) shift; cmd_emancipate "$@" ;;

	*)          usage ;;
esac
