#!/bin/bash
# streamlitctl - Streamlit Platform Manager for SecuBox Debian
# Control script for LXC-based Streamlit application hosting
# Three-fold architecture: Components, Status, Access
set -e

VERSION="1.1.0"

LXC_NAME="streamlit"
LXC_PATH="/var/lib/lxc/$LXC_NAME"
DATA_PATH="/srv/streamlit"
APPS_PATH="$DATA_PATH/apps"
CONF_PATH="/etc/secubox/streamlit.toml"
LOG_FILE="/var/log/secubox/streamlit.log"

# Logging
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" 2>/dev/null || echo "$*"; }
error() { log "ERROR: $*" >&2; }

# Load config
load_config() {
    if [ -f "$CONF_PATH" ]; then
        DATA_PATH=$(grep -E '^data_path\s*=' "$CONF_PATH" | cut -d'"' -f2 || echo "$DATA_PATH")
        APPS_PATH="$DATA_PATH/apps"
    fi
}

# LXC helpers
lxc_running() { lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; }
lxc_exists() { [ -d "$LXC_PATH/rootfs" ]; }

# ─────────────────────────────────────────────────────────────────────
# IDLE-TRACKING HELPERS (#331)
# ─────────────────────────────────────────────────────────────────────

IDLE_STATE_DIR="/var/lib/secubox/streamlit/idle"

# Read [idle] settings from /etc/secubox/streamlit.toml with safe defaults.
# Note: `grep | head | cut | tr` always exits 0 (tr's status) even when grep
# matched nothing, so a trailing `|| echo $default` never fires and missing
# keys silently returned empty. Capture explicitly and fall back via
# `${v:-$default}` for the missing-or-blank case.
_idle_config() {
    local key="$1" default="$2" v
    v=$(grep -E "^\s*${key}\s*=" "$CONF_PATH" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d ' "')
    echo "${v:-$default}"
}

# Returns 0 if the app is listening on its port INSIDE the LXC, else 1.
_app_running() {
    local name="$1"
    local port; port=$(grep -E "^port\s*=" "$APPS_PATH/$name/.streamlit.toml" 2>/dev/null | cut -d'=' -f2 | tr -d ' "')
    [ -z "$port" ] && return 1
    lxc_running || return 1
    lxc-attach -n "$LXC_NAME" -- ss -tln "sport = :$port" 2>/dev/null \
        | awk -v p="$port" 'NR>1 && $4 ~ ":"p"$" {found=1} END {exit found?0:1}'
}

# Returns the count of ESTABLISHED connections to the app's port (inside LXC).
_app_active_conns() {
    local name="$1"
    local port; port=$(grep -E "^port\s*=" "$APPS_PATH/$name/.streamlit.toml" 2>/dev/null | cut -d'=' -f2 | tr -d ' "')
    [ -z "$port" ] && { echo 0; return; }
    lxc_running || { echo 0; return; }
    lxc-attach -n "$LXC_NAME" -- ss -tn state established "sport = :$port" 2>/dev/null \
        | awk 'NR>1' | wc -l
}

# Last-activity epoch for the app (file mtime). Touched by cmd_app_idle_check
# when ESTABLISHED>0; created at start time by cmd_app_start.
_app_last_active() {
    local name="$1"
    local f="$IDLE_STATE_DIR/$name.state"
    [ -f "$f" ] || { echo 0; return; }
    stat -c %Y "$f"
}

_app_touch_active() {
    local name="$1"
    mkdir -p "$IDLE_STATE_DIR"
    touch "$IDLE_STATE_DIR/$name.state"
}

# ─────────────────────────────────────────────────────────────────────
# CONTAINER MANAGEMENT
# ─────────────────────────────────────────────────────────────────────

cmd_install() {
    if lxc_exists; then
        log "Container already exists. Use 'update' to upgrade."
        return 1
    fi

    log "Installing Streamlit LXC container..."
    mkdir -p "$DATA_PATH" "$APPS_PATH" "$(dirname $LOG_FILE)"

    # Create Alpine-based LXC container
    lxc-create -n "$LXC_NAME" -t download -- \
        -d alpine -r 3.19 -a arm64 || {
        error "Failed to create LXC container"
        return 1
    }

    # Configure container
    cat >> "$LXC_PATH/config" << EOF

# Streamlit bind mounts
lxc.mount.entry = $APPS_PATH srv/apps none bind,create=dir 0 0
lxc.mount.entry = $DATA_PATH/logs var/log/streamlit none bind,create=dir 0 0

# Network
lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up
lxc.net.0.name = eth0

# Resource limits
lxc.cgroup2.memory.max = 1G
lxc.cgroup2.cpu.max = 100000 100000
EOF

    # Start container and install packages
    lxc-start -n "$LXC_NAME"
    sleep 5

    log "Installing packages inside container..."
    lxc-attach -n "$LXC_NAME" -- sh -c '
        apk update
        apk add python3 py3-pip py3-virtualenv git bash curl
        pip3 install --break-system-packages streamlit pandas numpy plotly altair pillow
        mkdir -p /srv/apps /var/log/streamlit
    '

    # Create hello world app
    cat > "$APPS_PATH/hello/app.py" << 'PYEOF'
import streamlit as st

st.set_page_config(page_title="SecuBox Streamlit", page_icon=":rocket:")
st.title("Welcome to SecuBox Streamlit Platform")
st.markdown("""
This is your Streamlit application hosting environment.

### Getting Started
1. Upload a ZIP file containing your app
2. The app should have an `app.py` or `main.py` entrypoint
3. Include `requirements.txt` for dependencies

### Features
- Isolated LXC container execution
- Automatic dependency installation
- Domain/vhost publishing
- Gitea integration for version control
""")
PYEOF

    log "Container installation complete"
    return 0
}

cmd_uninstall() {
    log "Uninstalling Streamlit container..."

    if lxc_running; then
        lxc-stop -n "$LXC_NAME" || true
        sleep 2
    fi

    if lxc_exists; then
        lxc-destroy -n "$LXC_NAME" || true
    fi

    log "Container removed (data preserved in $DATA_PATH)"
}

cmd_update() {
    if ! lxc_exists; then
        error "Container not installed. Run install first."
        return 1
    fi

    local was_running=false
    if lxc_running; then
        was_running=true
        log "Stopping container for update..."
        lxc-stop -n "$LXC_NAME"
        sleep 2
    fi

    log "Updating packages inside container..."
    lxc-start -n "$LXC_NAME"
    sleep 3

    lxc-attach -n "$LXC_NAME" -- sh -c '
        apk update && apk upgrade
        pip3 install --break-system-packages --upgrade streamlit pandas numpy
    '

    if ! $was_running; then
        lxc-stop -n "$LXC_NAME"
    fi

    log "Update complete"
}

cmd_start() {
    if ! lxc_exists; then
        error "Container not installed"
        return 1
    fi

    if lxc_running; then
        log "Container already running"
        return 0
    fi

    log "Starting Streamlit container..."
    lxc-start -n "$LXC_NAME"
    sleep 2

    # Start any autostart instances
    cmd_autostart

    log "Container started"
}

cmd_stop() {
    if ! lxc_running; then
        log "Container not running"
        return 0
    fi

    log "Stopping Streamlit container..."
    lxc-stop -n "$LXC_NAME"
    log "Container stopped"
}

cmd_status() {
    local running="false"
    local installed="false"

    lxc_exists && installed="true"
    lxc_running && running="true"

    local app_count=0
    [ -d "$APPS_PATH" ] && app_count=$(find "$APPS_PATH" -maxdepth 1 -type d | wc -l)
    app_count=$((app_count - 1))
    [ $app_count -lt 0 ] && app_count=0

    cat << EOF
{
  "installed": $installed,
  "running": $running,
  "container": "$LXC_NAME",
  "data_path": "$DATA_PATH",
  "app_count": $app_count
}
EOF
}

# ─────────────────────────────────────────────────────────────────────
# THREE-FOLD ARCHITECTURE: Components (What)
# ─────────────────────────────────────────────────────────────────────

cmd_components() {
    local installed=false
    local running=false
    lxc_exists && installed=true
    lxc_running && running=true

    local app_count=0
    [ -d "$APPS_PATH" ] && app_count=$(find "$APPS_PATH" -maxdepth 1 -type d 2>/dev/null | wc -l)
    app_count=$((app_count - 1))
    [ $app_count -lt 0 ] && app_count=0

    cat <<EOF
{
  "components": [
    {
      "name": "Streamlit Container",
      "type": "lxc",
      "container": "$LXC_NAME",
      "description": "Python Streamlit application runtime",
      "installed": $installed,
      "running": $running
    },
    {
      "name": "Applications",
      "type": "apps",
      "path": "$APPS_PATH",
      "description": "Deployed Streamlit applications",
      "count": $app_count
    },
    {
      "name": "Python Runtime",
      "type": "runtime",
      "description": "Python 3 with Streamlit packages",
      "installed": $installed
    }
  ]
}
EOF
}

# ─────────────────────────────────────────────────────────────────────
# THREE-FOLD ARCHITECTURE: Access (How to connect)
# ─────────────────────────────────────────────────────────────────────

cmd_access() {
    local apps_json="["
    local first=true

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

        local port=$(grep -E "^port\s*=" "$app_dir/.streamlit.toml" 2>/dev/null | cut -d'=' -f2 | tr -d ' ' || echo "")
        local running=false
        [ -n "$port" ] && ss -tln "sport = :$port" 2>/dev/null | grep -q "$port" && running=true

        $first || apps_json+=","
        first=false
        apps_json+="{\"name\":\"$name\",\"port\":${port:-0},\"running\":$running,\"url\":\"http://localhost:${port:-8501}\"}"
    done
    apps_json+="]"

    cat <<EOF
{
  "data_path": "$DATA_PATH",
  "apps_path": "$APPS_PATH",
  "apps": $apps_json,
  "admin_panel": "/streamlit/",
  "default_port_range": "8501-8600"
}
EOF
}

# ─────────────────────────────────────────────────────────────────────
# APP MANAGEMENT
# ─────────────────────────────────────────────────────────────────────

cmd_app_list() {
    echo '{"apps":['
    local first=true

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

        # Find main py file
        local main_py=""
        for candidate in app.py main.py streamlit_app.py; do
            [ -f "$app_dir/$candidate" ] && main_py="$candidate" && break
        done

        # Check if running (by checking port)
        local running="false"
        local port=$(grep -E "^port\s*=" "$app_dir/.streamlit.toml" 2>/dev/null | cut -d'=' -f2 | tr -d ' ' || echo "")
        [ -n "$port" ] && ss -tln "sport = :$port" 2>/dev/null | grep -q "$port" && running="true"

        $first || echo ","
        first=false

        cat << EOF
  {"name": "$name", "main": "$main_py", "running": $running, "port": ${port:-0}}
EOF
    done

    echo ']}'
}

# app info <name> — print metadata + runtime state from manifest + pid file (#182)
cmd_app_info() {
    local name="$1"
    [ -z "$name" ] && { error "Usage: streamlitctl app info <name>"; return 1; }
    local d="$APPS_PATH/$name"
    [ -d "$d" ] || { error "app not found: $name"; return 1; }
    # Entry point detection (same logic as cmd_app_start)
    local entry=""
    for c in app.py main.py streamlit_app.py; do
        [ -f "$d/$c" ] && entry="$c" && break
    done
    local port=""
    [ -f "$d/.streamlit.toml" ] && port=$(grep -E "^port" "$d/.streamlit.toml" 2>/dev/null | cut -d= -f2 | tr -d ' ')
    local pidf="/var/run/streamlit-${name}.pid"
    local pid="" alive="no"
    if lxc_running; then
        pid=$(lxc-attach -n "$LXC_NAME" -- cat "$pidf" 2>/dev/null || true)
        if [ -n "$pid" ]; then
            lxc-attach -n "$LXC_NAME" -- kill -0 "$pid" >/dev/null 2>&1 && alive="yes"
        fi
    fi
    cat <<EOF
name:        $name
path:        $d
entrypoint:  ${entry:-(none)}
port:        ${port:-(unset)}
pid_file:    $pidf
pid:         ${pid:-(none)}
running:     $alive
EOF
}

# app restart <name> — stop + start (#182)
cmd_app_restart() {
    local name="$1"
    [ -z "$name" ] && { error "Usage: streamlitctl app restart <name>"; return 1; }
    cmd_app_stop "$name" || true
    sleep 1
    # Recover the previous port from .streamlit.toml so restart preserves it
    local port=""
    [ -f "$APPS_PATH/$name/.streamlit.toml" ] && \
        port=$(grep -E "^port" "$APPS_PATH/$name/.streamlit.toml" 2>/dev/null | cut -d= -f2 | tr -d ' ')
    cmd_app_start "$name" "${port:-8501}"
}

cmd_app_start() {
    local name="$1"
    local port="${2:-8501}"

    [ -z "$name" ] && { error "Usage: streamlitctl app start <name> [port]"; return 1; }
    [ -d "$APPS_PATH/$name" ] || { error "App not found: $name"; return 1; }

    if ! lxc_running; then
        error "Container not running. Start it first."
        return 1
    fi

    # Find entrypoint
    local entrypoint=""
    for candidate in app.py main.py streamlit_app.py; do
        [ -f "$APPS_PATH/$name/$candidate" ] && entrypoint="$candidate" && break
    done

    [ -z "$entrypoint" ] && { error "No entrypoint found in $name"; return 1; }

    # Save port config
    mkdir -p "$APPS_PATH/$name"
    echo "port = $port" > "$APPS_PATH/$name/.streamlit.toml"

    log "Starting app $name on port $port..."

    lxc-attach -n "$LXC_NAME" -- sh -c "
        cd /srv/apps/$name
        nohup streamlit run $entrypoint \
            --server.port=$port \
            --server.headless=true \
            --server.address=0.0.0.0 \
            > /var/log/streamlit/$name.log 2>&1 &
        echo \$! > /var/run/streamlit-$name.pid
    "

    log "App $name started"
    _app_touch_active "$name"
}

cmd_app_stop() {
    local name="$1"
    [ -z "$name" ] && { error "Usage: streamlitctl app stop <name>"; return 1; }

    if ! lxc_running; then
        return 0
    fi

    log "Stopping app $name..."

    lxc-attach -n "$LXC_NAME" -- sh -c "
        if [ -f /var/run/streamlit-$name.pid ]; then
            kill \$(cat /var/run/streamlit-$name.pid) 2>/dev/null || true
            rm -f /var/run/streamlit-$name.pid
        fi
        pkill -f \"streamlit.*$name\" 2>/dev/null || true
    "

    log "App $name stopped"
}

cmd_app_idle_check() {
    local enabled; enabled=$(_idle_config "enabled" "true")
    [ "$enabled" = "true" ] || { log "idle-check disabled in $CONF_PATH"; return 0; }

    local timeout_min; timeout_min=$(_idle_config "timeout_minutes" "30")
    local timeout_sec=$(( timeout_min * 60 ))
    local now; now=$(date +%s)

    local stopped=0 active=0 idle=0
    for app_dir in "$APPS_PATH"/*/; do
        [ -d "$app_dir" ] || continue
        local name; name=$(basename "$app_dir")
        [ "$name" = "*" ] && continue

        _app_running "$name" || continue

        local conns; conns=$(_app_active_conns "$name")
        if [ "$conns" -gt 0 ]; then
            _app_touch_active "$name"
            active=$(( active + 1 ))
            continue
        fi

        local last; last=$(_app_last_active "$name")
        [ "$last" -eq 0 ] && { _app_touch_active "$name"; continue; }
        local idle_sec=$(( now - last ))

        if [ "$idle_sec" -ge "$timeout_sec" ]; then
            log "idle-stop: $name (idle ${idle_sec}s >= ${timeout_sec}s)"
            cmd_app_stop "$name" >/dev/null 2>&1 || true
            stopped=$(( stopped + 1 ))
        else
            idle=$(( idle + 1 ))
        fi
    done

    log "idle-check: active=$active idle=$idle stopped=$stopped"
}

cmd_app_wake() {
    local name="$1"
    local wait_sec="${2:-30}"
    [ -z "$name" ] && { error "Usage: streamlitctl app wake <name> [wait_seconds]"; return 2; }
    [ -d "$APPS_PATH/$name" ] || { error "App not found: $name"; return 2; }

    if _app_running "$name"; then
        _app_touch_active "$name"
        log "wake: $name already running"
        return 0
    fi

    log "wake: starting $name (will wait up to ${wait_sec}s)"
    cmd_app_start "$name" >/dev/null 2>&1 || { error "wake: failed to start $name"; return 1; }

    local elapsed=0
    while [ "$elapsed" -lt "$wait_sec" ]; do
        if _app_running "$name"; then
            _app_touch_active "$name"
            log "wake: $name listening after ${elapsed}s"
            return 0
        fi
        sleep 1
        elapsed=$(( elapsed + 1 ))
    done

    error "wake: $name did not come up within ${wait_sec}s"
    return 1
}

cmd_app_deploy() {
    local zip_file="$1"
    local name="$2"

    [ -z "$zip_file" ] || [ ! -f "$zip_file" ] && {
        error "Usage: streamlitctl app deploy <zipfile> [name]"
        return 1
    }

    # Auto-name from filename if not specified
    [ -z "$name" ] && name=$(basename "$zip_file" .zip | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g')

    local app_dir="$APPS_PATH/$name"
    log "Deploying $name from $zip_file..."

    # Remove existing
    [ -d "$app_dir" ] && rm -rf "$app_dir"
    mkdir -p "$app_dir"

    # Extract with flattening
    local tmpdir="/tmp/streamlit_extract_$$"
    mkdir -p "$tmpdir"
    unzip -q "$zip_file" -d "$tmpdir"

    # Check for single root directory
    local root_count=$(ls -1 "$tmpdir" | wc -l)
    if [ "$root_count" = "1" ]; then
        local single_root="$tmpdir/$(ls -1 $tmpdir | head -1)"
        if [ -d "$single_root" ]; then
            mv "$single_root"/* "$app_dir/" 2>/dev/null || true
            mv "$single_root"/.[!.]* "$app_dir/" 2>/dev/null || true
        else
            mv "$single_root" "$app_dir/"
        fi
    else
        mv "$tmpdir"/* "$app_dir/" 2>/dev/null || true
    fi
    rm -rf "$tmpdir"

    # Install requirements if present and container is running
    if [ -f "$app_dir/requirements.txt" ] && lxc_running; then
        log "Installing requirements..."
        lxc-attach -n "$LXC_NAME" -- pip3 install --break-system-packages -r "/srv/apps/$name/requirements.txt" || true
    fi

    log "App $name deployed to $app_dir"
    echo '{"success": true, "name": "'"$name"'", "path": "'"$app_dir"'"}'
}

cmd_app_remove() {
    local name="$1"
    [ -z "$name" ] && { error "Usage: streamlitctl app remove <name>"; return 1; }

    # Stop if running
    cmd_app_stop "$name" 2>/dev/null || true

    # Remove directory
    if [ -d "$APPS_PATH/$name" ]; then
        rm -rf "$APPS_PATH/$name"
        log "App $name removed"
    else
        error "App not found: $name"
        return 1
    fi
}

cmd_app_logs() {
    local name="$1"
    local lines="${2:-100}"

    [ -z "$name" ] && { error "Usage: streamlitctl app logs <name> [lines]"; return 1; }

    local log_file="$DATA_PATH/logs/$name.log"
    if [ -f "$log_file" ]; then
        tail -n "$lines" "$log_file"
    else
        echo "No logs found for $name"
    fi
}

cmd_autostart() {
    # Start apps marked for autostart in config
    if [ -f "$CONF_PATH" ]; then
        local apps=$(grep -E '^\[apps\.' "$CONF_PATH" | sed 's/\[apps\.//;s/\]//')
        for name in $apps; do
            local autostart=$(grep -A5 "^\[apps\.$name\]" "$CONF_PATH" | grep -E '^autostart\s*=' | grep -q 'true' && echo "true" || echo "false")
            local port=$(grep -A5 "^\[apps\.$name\]" "$CONF_PATH" | grep -E '^port\s*=' | cut -d'=' -f2 | tr -d ' ')
            if [ "$autostart" = "true" ] && [ -n "$port" ]; then
                cmd_app_start "$name" "$port" 2>/dev/null || true
            fi
        done
    fi
}

# ─────────────────────────────────────────────────────────────────────
# INSTANCE MANAGEMENT (multiple instances per app)
# ─────────────────────────────────────────────────────────────────────

cmd_instance_list() {
    echo '{"instances":['
    local first=true

    if [ -f "$CONF_PATH" ]; then
        grep -E '^\[instances\.' "$CONF_PATH" | while read -r line; do
            local id=$(echo "$line" | sed 's/\[instances\.//;s/\]//')

            # Parse instance config
            local section=$(sed -n "/^\[instances\.$id\]/,/^\[/p" "$CONF_PATH" | head -n -1)
            local app=$(echo "$section" | grep -E '^app\s*=' | cut -d'"' -f2)
            local port=$(echo "$section" | grep -E '^port\s*=' | cut -d'=' -f2 | tr -d ' ')
            local enabled=$(echo "$section" | grep -E '^enabled\s*=' | grep -q 'true' && echo "true" || echo "false")

            local running="false"
            [ -n "$port" ] && ss -tln "sport = :$port" 2>/dev/null | grep -q "$port" && running="true"

            $first || echo ","
            first=false

            echo "  {\"id\": \"$id\", \"app\": \"$app\", \"port\": ${port:-0}, \"enabled\": $enabled, \"running\": $running}"
        done
    fi

    echo ']}'
}

cmd_instance_start() {
    local id="$1"
    [ -z "$id" ] && { error "Usage: streamlitctl instance start <id>"; return 1; }

    # Read instance config
    if [ -f "$CONF_PATH" ]; then
        local section=$(sed -n "/^\[instances\.$id\]/,/^\[/p" "$CONF_PATH" | head -n -1)
        local app=$(echo "$section" | grep -E '^app\s*=' | cut -d'"' -f2)
        local port=$(echo "$section" | grep -E '^port\s*=' | cut -d'=' -f2 | tr -d ' ')

        [ -z "$app" ] || [ -z "$port" ] && { error "Instance $id not configured"; return 1; }

        cmd_app_start "$app" "$port"
    else
        error "No config file found"
        return 1
    fi
}

cmd_instance_stop() {
    local id="$1"
    [ -z "$id" ] && { error "Usage: streamlitctl instance stop <id>"; return 1; }

    if [ -f "$CONF_PATH" ]; then
        local section=$(sed -n "/^\[instances\.$id\]/,/^\[/p" "$CONF_PATH" | head -n -1)
        local app=$(echo "$section" | grep -E '^app\s*=' | cut -d'"' -f2)

        [ -n "$app" ] && cmd_app_stop "$app"
    fi
}

# ─────────────────────────────────────────────────────────────────────
# MIGRATION
# ─────────────────────────────────────────────────────────────────────

cmd_migrate() {
    local source="${1:-192.168.255.1}"

    log "Migrating Streamlit data from $source..."
    mkdir -p "$DATA_PATH" "$APPS_PATH"

    # Sync apps directory
    log "Syncing applications..."
    rsync -avz --progress "root@$source:/srv/streamlit/apps/" "$APPS_PATH/" 2>/dev/null || {
        # Try alternative path
        rsync -avz --progress "root@$source:/overlay/upper/srv/streamlit/apps/" "$APPS_PATH/" 2>/dev/null || true
    }

    # Convert UCI config to TOML
    log "Converting UCI configuration to TOML..."
    ssh "root@$source" "uci show streamlit 2>/dev/null" | while read -r line; do
        case "$line" in
            streamlit.main.*)
                key=$(echo "$line" | cut -d'.' -f3 | cut -d'=' -f1)
                val=$(echo "$line" | cut -d'=' -f2- | tr -d "'")
                echo "$key = \"$val\""
                ;;
        esac
    done > "$CONF_PATH.tmp"

    # Add apps sections
    for app_dir in "$APPS_PATH"/*/; do
        [ -d "$app_dir" ] || continue
        local name=$(basename "$app_dir")
        [ "$name" = "*" ] && continue

        # Get port from UCI if available
        local port=$(ssh "root@$source" "uci -q get streamlit.$name.port 2>/dev/null" || echo "")
        local enabled=$(ssh "root@$source" "uci -q get streamlit.$name.enabled 2>/dev/null" || echo "1")

        cat >> "$CONF_PATH.tmp" << EOF

[apps.$name]
enabled = $([ "$enabled" = "1" ] && echo "true" || echo "false")
port = ${port:-0}
EOF
    done

    # Finalize config
    [ -s "$CONF_PATH.tmp" ] && mv "$CONF_PATH.tmp" "$CONF_PATH"

    # Count migrated apps
    local count=$(find "$APPS_PATH" -maxdepth 1 -type d | wc -l)
    count=$((count - 1))

    log "Migration complete: $count apps imported"
    echo "{\"success\": true, \"apps_migrated\": $count}"
}

# ─────────────────────────────────────────────────────────────────────
# GITEA INTEGRATION
# ─────────────────────────────────────────────────────────────────────

cmd_gitea_push() {
    local name="$1"
    [ -z "$name" ] && { error "Usage: streamlitctl gitea push <app>"; return 1; }
    [ -d "$APPS_PATH/$name" ] || { error "App not found: $name"; return 1; }

    # Read Gitea config
    if [ ! -f "$CONF_PATH" ]; then
        error "No config file. Configure Gitea first."
        return 1
    fi

    local gitea_url=$(grep -E '^url\s*=' "$CONF_PATH" | head -1 | cut -d'"' -f2)
    local gitea_user=$(grep -E '^user\s*=' "$CONF_PATH" | head -1 | cut -d'"' -f2)
    local gitea_token=$(grep -E '^token\s*=' "$CONF_PATH" | head -1 | cut -d'"' -f2)

    [ -z "$gitea_url" ] && { error "Gitea URL not configured"; return 1; }

    cd "$APPS_PATH/$name"

    # Initialize git if needed
    if [ ! -d .git ]; then
        git init
        git add -A
        git commit -m "Initial commit from SecuBox Streamlit"
    fi

    # Create repo on Gitea if it doesn't exist
    if [ -n "$gitea_token" ]; then
        curl -s -X POST "$gitea_url/api/v1/user/repos" \
            -H "Authorization: token $gitea_token" \
            -H "Content-Type: application/json" \
            -d "{\"name\": \"$name\", \"private\": true}" 2>/dev/null || true
    fi

    # Set remote and push
    local remote_url="$gitea_url/${gitea_user}/${name}.git"
    git remote remove origin 2>/dev/null || true
    git remote add origin "$remote_url"

    if [ -n "$gitea_token" ]; then
        # Use token in URL
        remote_url=$(echo "$remote_url" | sed "s|://|://$gitea_token@|")
        git push -u "$remote_url" main 2>/dev/null || git push -u "$remote_url" master 2>/dev/null || true
    fi

    log "Pushed $name to Gitea"
}

cmd_gitea_clone() {
    local name="$1"
    local repo="$2"

    [ -z "$name" ] || [ -z "$repo" ] && {
        error "Usage: streamlitctl gitea clone <name> <repo_url>"
        return 1
    }

    local app_dir="$APPS_PATH/$name"
    [ -d "$app_dir" ] && { error "App already exists: $name"; return 1; }

    log "Cloning $repo to $name..."
    git clone "$repo" "$app_dir"

    # Install requirements if present
    if [ -f "$app_dir/requirements.txt" ] && lxc_running; then
        lxc-attach -n "$LXC_NAME" -- pip3 install --break-system-packages -r "/srv/apps/$name/requirements.txt" || true
    fi

    log "Cloned $name from Gitea"
}

# ─────────────────────────────────────────────────────────────────────
# MAIN
# ─────────────────────────────────────────────────────────────────────

load_config

case "${1:-}" in
    # Three-fold architecture
    components) cmd_components ;;
    access)     cmd_access ;;
    status)     cmd_status ;;
    # Container control
    install)    cmd_install ;;
    uninstall)  cmd_uninstall ;;
    update)     cmd_update ;;
    start)      cmd_start ;;
    stop)       cmd_stop ;;
    restart)    cmd_stop; sleep 1; cmd_start ;;

    app)
        case "${2:-}" in
            list)       cmd_app_list ;;
            idle-check) cmd_app_idle_check ;;
            wake)       cmd_app_wake "$3" "$4" ;;
            start)  cmd_app_start "$3" "$4" ;;
            stop)   cmd_app_stop "$3" ;;
            deploy) cmd_app_deploy "$3" "$4" ;;
            remove) cmd_app_remove "$3" ;;
            logs)   cmd_app_logs "$3" "$4" ;;
            info)   cmd_app_info "$3" ;;
            restart) cmd_app_restart "$3" ;;
            *)      echo "Usage: streamlitctl app {list|start|stop|restart|deploy|remove|logs|info|idle-check|wake} [args]" ;;
        esac
        ;;

    instance)
        case "${2:-}" in
            list)   cmd_instance_list ;;
            start)  cmd_instance_start "$3" ;;
            stop)   cmd_instance_stop "$3" ;;
            *)      echo "Usage: streamlitctl instance {list|start|stop} [id]" ;;
        esac
        ;;

    migrate)    cmd_migrate "$2" ;;
    autostart)  cmd_autostart ;;

    gitea)
        case "${2:-}" in
            push)   cmd_gitea_push "$3" ;;
            clone)  cmd_gitea_clone "$3" "$4" ;;
            *)      echo "Usage: streamlitctl gitea {push|clone} [args]" ;;
        esac
        ;;

    *)
        cat << EOF
streamlitctl v$VERSION - Streamlit Platform Manager
Three-fold architecture: Components, Status, Access

Information (Three-fold):
  components    List system components (JSON)
  status        Show container status (JSON)
  access        Show apps and access info (JSON)

Container:
  install       Install LXC container with Streamlit
  uninstall     Remove container (preserves data)
  update        Update packages in container
  start         Start container
  stop          Stop container
  restart       Restart container

Apps:
  app list              List all apps
  app start <name> [port]   Start an app
  app stop <name>           Stop an app
  app deploy <zip> [name]   Deploy from ZIP
  app remove <name>         Remove an app
  app logs <name> [lines]   View app logs

Instances:
  instance list         List configured instances
  instance start <id>   Start instance
  instance stop <id>    Stop instance

Integration:
  migrate [source]      Migrate from OpenWrt (default: 192.168.255.1)
  gitea push <app>      Push app to Gitea
  gitea clone <name> <url>  Clone from Gitea

Examples:
  streamlitctl components        # JSON list of components
  streamlitctl access            # JSON list of apps with URLs

EOF
        ;;
esac
