#!/bin/sh
#
# SecuBox Service Exposure Manager
# Unified tool for port management, Tor hidden services, and HAProxy SSL
#

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

CONFIG_NAME="secubox-exposure"
HAPROXY_CONFIG=""
HAPROXY_CERTS=""
TOR_HIDDEN_DIR=""
TOR_CONFIG=""

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_err() { echo -e "${RED}[ERROR]${NC} $1"; }

load_config() {
    config_load "$CONFIG_NAME"
    config_get HAPROXY_CONFIG main haproxy_config "/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg"
    config_get HAPROXY_CERTS main haproxy_certs "/srv/lxc/haproxy/rootfs/etc/haproxy/certs"
    config_get TOR_HIDDEN_DIR main tor_hidden_dir "/var/lib/tor/hidden_services"
    config_get TOR_CONFIG main tor_config "/etc/tor/torrc"
    config_get APP_PORT_START ranges app_start "8100"
    config_get APP_PORT_END ranges app_end "8199"
}

# ============================================================================
# PORT SCANNING & CONFLICT DETECTION
# ============================================================================

get_listening_ports() {
    # Returns: port address process
    netstat -tlnp 2>/dev/null | grep LISTEN | awk '{
        split($4, a, ":")
        port = a[length(a)]
        if (!seen[port]++) {
            split($7, p, "/")
            proc = p[2]
            if (proc == "") proc = "unknown"
            print port, $4, proc
        }
    }' | sort -n
}

cmd_scan() {
    log_info "Scanning listening services..."
    echo ""
    printf "%-6s %-20s %-15s %-10s\n" "PORT" "ADDRESS" "PROCESS" "STATUS"
    printf "%-6s %-20s %-15s %-10s\n" "------" "--------------------" "---------------" "----------"

    get_listening_ports | while read port addr proc; do
        # Determine if external
        case "$addr" in
            *0.0.0.0*|*::*) status="${GREEN}external${NC}" ;;
            *127.0.0.1*|*::1*) status="${YELLOW}local${NC}" ;;
            *) status="${CYAN}bound${NC}" ;;
        esac
        printf "%-6s %-20s %-15s " "$port" "$addr" "$proc"
        echo -e "$status"
    done
    echo ""
}

cmd_conflicts() {
    log_info "Checking for port conflicts..."
    echo ""

    local conflicts=0
    local TMP_PORTS="/tmp/ports_$$"

    # Get all configured ports from UCI
    > "$TMP_PORTS"

    # Check known services
    check_known_service() {
        local section="$1"
        local default_port config_path
        config_get default_port "$section" default_port
        config_get config_path "$section" config_path

        if [ -n "$config_path" ]; then
            # Extract UCI config and option
            local uci_config=$(echo "$config_path" | cut -d'.' -f1)
            local uci_option=$(echo "$config_path" | cut -d'.' -f2-)
            local actual_port=$(uci -q get "$config_path" 2>/dev/null)
            [ -z "$actual_port" ] && actual_port="$default_port"
            echo "$actual_port $section" >> "$TMP_PORTS"
        fi
    }
    config_foreach check_known_service known

    # Find duplicates
    sort "$TMP_PORTS" | uniq -d -w5 | while read port svc; do
        log_warn "Port $port is configured for multiple services!"
        grep "^$port " "$TMP_PORTS" | while read p s; do
            echo "  - $s"
        done
        conflicts=$((conflicts + 1))
    done

    # Check against actually listening ports
    get_listening_ports | while read port addr proc; do
        if grep -q "^$port " "$TMP_PORTS"; then
            local configured_svc=$(grep "^$port " "$TMP_PORTS" | head -1 | cut -d' ' -f2)
            # Check if process matches expected
            case "$configured_svc" in
                gitea) [ "$proc" != "gitea" ] && log_warn "Port $port: expected gitea, found $proc" ;;
                streamlit) echo "$proc" | grep -qv "python\|streamlit" && log_warn "Port $port: expected streamlit, found $proc" ;;
            esac
        fi
    done

    rm -f "$TMP_PORTS"

    if [ "$conflicts" -eq 0 ]; then
        log_ok "No port conflicts detected"
    fi
}

find_free_port() {
    local start="$1"
    local end="$2"
    local port="$start"

    while [ "$port" -le "$end" ]; do
        if ! netstat -tlnp 2>/dev/null | grep -q ":$port "; then
            echo "$port"
            return 0
        fi
        port=$((port + 1))
    done
    return 1
}

cmd_fix_port() {
    local service="$1"
    local new_port="$2"

    if [ -z "$service" ]; then
        log_err "Usage: secubox-exposure fix-port <service> [new_port]"
        return 1
    fi

    load_config

    # Get service config
    local config_path default_port
    config_get config_path "$service" config_path
    config_get default_port "$service" default_port

    if [ -z "$config_path" ]; then
        log_err "Unknown service: $service"
        return 1
    fi

    # Find free port if not specified
    if [ -z "$new_port" ]; then
        new_port=$(find_free_port "$APP_PORT_START" "$APP_PORT_END")
        if [ -z "$new_port" ]; then
            log_err "No free ports available in range $APP_PORT_START-$APP_PORT_END"
            return 1
        fi
    fi

    # Check if new port is free
    if netstat -tlnp 2>/dev/null | grep -q ":$new_port "; then
        log_err "Port $new_port is already in use"
        return 1
    fi

    log_info "Changing $service port to $new_port"

    # Update UCI
    if uci set "$config_path=$new_port" && uci commit; then
        log_ok "UCI config updated"

        # Restart service if it has an init script
        if [ -x "/etc/init.d/$service" ]; then
            log_info "Restarting $service..."
            /etc/init.d/"$service" restart
        fi

        log_ok "$service now listening on port $new_port"
    else
        log_err "Failed to update UCI config"
        return 1
    fi
}

# ============================================================================
# TOR HIDDEN SERVICES
# ============================================================================

cmd_tor_add() {
    local service="$1"
    local local_port="$2"
    local onion_port="${3:-80}"

    if [ -z "$service" ]; then
        log_err "Usage: secubox-exposure tor add <service> [local_port] [onion_port]"
        return 1
    fi

    load_config

    # Get local port from config if not specified
    if [ -z "$local_port" ]; then
        config_get local_port "$service" default_port
        if [ -z "$local_port" ]; then
            log_err "Cannot determine local port for $service"
            return 1
        fi
    fi

    local hidden_dir="$TOR_HIDDEN_DIR/$service"

    # Create hidden service directory
    mkdir -p "$hidden_dir"
    chmod 700 "$hidden_dir"
    chown tor:tor "$hidden_dir" 2>/dev/null || chown debian-tor:debian-tor "$hidden_dir" 2>/dev/null

    # Check if already configured in torrc
    if grep -q "HiddenServiceDir $hidden_dir" "$TOR_CONFIG" 2>/dev/null; then
        log_warn "Hidden service for $service already exists"
        local onion=$(cat "$hidden_dir/hostname" 2>/dev/null)
        [ -n "$onion" ] && log_info "Onion address: $onion"
        return 0
    fi

    # Add to torrc
    log_info "Adding hidden service for $service (127.0.0.1:$local_port -> :$onion_port)"

    cat >> "$TOR_CONFIG" << EOF

# Hidden service for $service (added by secubox-exposure)
HiddenServiceDir $hidden_dir
HiddenServicePort $onion_port 127.0.0.1:$local_port
EOF

    # Restart Tor
    log_info "Restarting Tor..."
    /etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null

    # Wait for onion address
    log_info "Waiting for onion address generation..."
    local tries=0
    while [ ! -f "$hidden_dir/hostname" ] && [ "$tries" -lt 30 ]; do
        sleep 1
        tries=$((tries + 1))
    done

    if [ -f "$hidden_dir/hostname" ]; then
        local onion=$(cat "$hidden_dir/hostname")
        log_ok "Hidden service created!"
        echo ""
        echo -e "  ${CYAN}Service:${NC} $service"
        echo -e "  ${CYAN}Onion:${NC}   $onion"
        echo -e "  ${CYAN}Port:${NC}    $onion_port -> 127.0.0.1:$local_port"
        echo ""

        # Save to exposure UCI
        uci set "${CONFIG_NAME}.${service}=service"
        uci set "${CONFIG_NAME}.${service}.port=$local_port"
        uci set "${CONFIG_NAME}.${service}.tor=1"
        uci set "${CONFIG_NAME}.${service}.tor_onion=$onion"
        uci set "${CONFIG_NAME}.${service}.tor_port=$onion_port"
        uci commit "$CONFIG_NAME"

        # Sync to Tor Shield UCI
        local hs_name="hs_${service}"
        uci set "tor-shield.${hs_name}=hidden_service"
        uci set "tor-shield.${hs_name}.name=${service}"
        uci set "tor-shield.${hs_name}.enabled=1"
        uci set "tor-shield.${hs_name}.local_port=${local_port}"
        uci set "tor-shield.${hs_name}.onion_port=${onion_port}"
        uci set "tor-shield.${hs_name}.onion_address=${onion}"
        uci commit tor-shield
        log_ok "Synced to Tor Shield"
    else
        log_err "Failed to generate onion address"
        return 1
    fi
}

cmd_tor_list() {
    load_config

    log_info "Tor Hidden Services:"
    echo ""
    printf "%-15s %-62s %-10s\n" "SERVICE" "ONION ADDRESS" "PORT"
    printf "%-15s %-62s %-10s\n" "---------------" "--------------------------------------------------------------" "----------"

    # List from filesystem
    if [ -d "$TOR_HIDDEN_DIR" ]; then
        for dir in "$TOR_HIDDEN_DIR"/*/; do
            [ -d "$dir" ] || continue
            local svc=$(basename "$dir")
            local onion=""
            [ -f "$dir/hostname" ] && onion=$(cat "$dir/hostname")

            # Get port from torrc
            local port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{print $2}')

            if [ -n "$onion" ]; then
                printf "%-15s %-62s %-10s\n" "$svc" "$onion" "${port:-80}"
            fi
        done
    fi
    echo ""
}

cmd_tor_remove() {
    local service="$1"

    if [ -z "$service" ]; then
        log_err "Usage: secubox-exposure tor remove <service>"
        return 1
    fi

    load_config

    local hidden_dir="$TOR_HIDDEN_DIR/$service"

    if [ ! -d "$hidden_dir" ]; then
        log_err "No hidden service found for $service"
        return 1
    fi

    log_info "Removing hidden service for $service"

    # Remove from torrc (remove the block)
    sed -i "/# Hidden service for $service/,/HiddenServicePort/d" "$TOR_CONFIG"

    # Remove directory
    rm -rf "$hidden_dir"

    # Update exposure UCI
    uci delete "${CONFIG_NAME}.${service}.tor" 2>/dev/null
    uci delete "${CONFIG_NAME}.${service}.tor_onion" 2>/dev/null
    uci delete "${CONFIG_NAME}.${service}.tor_port" 2>/dev/null
    uci commit "$CONFIG_NAME"

    # Remove from Tor Shield UCI
    local hs_name="hs_${service}"
    if uci -q get "tor-shield.${hs_name}" >/dev/null 2>&1; then
        uci delete "tor-shield.${hs_name}"
        uci commit tor-shield
        log_ok "Removed from Tor Shield"
    fi

    # Restart Tor
    /etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null

    log_ok "Hidden service removed"
}

cmd_tor_sync() {
    load_config

    log_info "Syncing hidden services to Tor Shield..."
    local synced=0

    # List from filesystem and sync to Tor Shield
    if [ -d "$TOR_HIDDEN_DIR" ]; then
        for dir in "$TOR_HIDDEN_DIR"/*/; do
            [ -d "$dir" ] || continue
            local svc=$(basename "$dir")
            local onion=""
            [ -f "$dir/hostname" ] && onion=$(cat "$dir/hostname")

            # Get port from torrc
            local port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{print $2}')
            local local_port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{split($3,a,":"); print a[2]}')

            if [ -n "$onion" ]; then
                local hs_name="hs_${svc}"
                if ! uci -q get "tor-shield.${hs_name}" >/dev/null 2>&1; then
                    log_info "Adding $svc to Tor Shield"
                    uci set "tor-shield.${hs_name}=hidden_service"
                    uci set "tor-shield.${hs_name}.name=${svc}"
                    uci set "tor-shield.${hs_name}.enabled=1"
                    uci set "tor-shield.${hs_name}.local_port=${local_port:-80}"
                    uci set "tor-shield.${hs_name}.onion_port=${port:-80}"
                    uci set "tor-shield.${hs_name}.onion_address=${onion}"
                    synced=$((synced + 1))
                fi
            fi
        done
    fi

    if [ "$synced" -gt 0 ]; then
        uci commit tor-shield
        log_ok "Synced $synced hidden service(s) to Tor Shield"
    else
        log_info "All hidden services already synced"
    fi
}

# ============================================================================
# HAPROXY SSL BACKENDS (UCI-based integration with haproxyctl)
# ============================================================================

# Sanitize name for UCI section (replace dots/hyphens with underscores)
sanitize_uci_name() {
    echo "$1" | sed 's/[.-]/_/g'
}

cmd_ssl_add() {
    local service="$1"
    local domain="$2"
    local local_port="$3"

    if [ -z "$service" ] || [ -z "$domain" ]; then
        log_err "Usage: secubox-exposure ssl add <service> <domain> [local_port]"
        return 1
    fi

    load_config

    # Get local port from config if not specified
    if [ -z "$local_port" ]; then
        config_get local_port "$service" default_port
        # Try to get from service UCI
        local config_path
        config_get config_path "$service" config_path
        if [ -n "$config_path" ]; then
            local configured_port=$(uci -q get "$config_path")
            [ -n "$configured_port" ] && local_port="$configured_port"
        fi
        if [ -z "$local_port" ]; then
            log_err "Cannot determine local port for $service. Specify it manually."
            return 1
        fi
    fi

    # Check if haproxyctl exists
    if [ ! -x "/usr/sbin/haproxyctl" ]; then
        log_err "haproxyctl not found. Is secubox-app-haproxy installed?"
        return 1
    fi

    # Sanitize names for UCI
    local backend_name="$service"
    local vhost_name=$(sanitize_uci_name "$domain")

    # Check if backend already exists in UCI
    if uci -q get "haproxy.${backend_name}" >/dev/null 2>&1; then
        log_warn "Backend '$backend_name' already exists in HAProxy UCI config"
    else
        # Create backend in HAProxy UCI config
        log_info "Adding backend '$backend_name' (127.0.0.1:$local_port)"
        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"
        uci add_list "haproxy.${backend_name}.server=${service} 127.0.0.1:${local_port} check"
    fi

    # Check if vhost already exists
    if uci -q get "haproxy.${vhost_name}" >/dev/null 2>&1; then
        log_warn "Vhost for '$domain' already exists"
    else
        # Create vhost in HAProxy UCI config
        log_info "Adding vhost '$domain' -> backend '$backend_name'"
        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}.enabled=1"
    fi

    # Commit HAProxy UCI changes
    uci commit haproxy

    # Also save to exposure UCI for tracking
    uci set "${CONFIG_NAME}.${service}=service"
    uci set "${CONFIG_NAME}.${service}.port=$local_port"
    uci set "${CONFIG_NAME}.${service}.ssl=1"
    uci set "${CONFIG_NAME}.${service}.ssl_domain=$domain"
    uci commit "$CONFIG_NAME"

    log_ok "HAProxy UCI config updated"
    log_info "Domain: $domain -> 127.0.0.1:$local_port"

    # Regenerate and reload HAProxy
    log_info "Regenerating HAProxy config..."
    /usr/sbin/haproxyctl generate

    log_info "Reloading HAProxy..."
    /usr/sbin/haproxyctl reload

    log_ok "SSL backend configured"
    log_warn "Note: Ensure SSL certificate exists for $domain"
}

cmd_ssl_list() {
    load_config

    log_info "HAProxy SSL Backends:"
    echo ""
    printf "%-15s %-30s %-20s\n" "SERVICE" "DOMAIN" "BACKEND"
    printf "%-15s %-30s %-20s\n" "---------------" "------------------------------" "--------------------"

    # Read from HAProxy UCI config (vhosts with their backends)
    local found=0
    for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
        local domain=$(uci -q get "haproxy.${vhost}.domain")
        local backend=$(uci -q get "haproxy.${vhost}.backend")
        local enabled=$(uci -q get "haproxy.${vhost}.enabled")

        [ "$enabled" != "1" ] && continue
        [ -z "$domain" ] && continue

        # Get server from backend
        local server=""
        if [ -n "$backend" ]; then
            server=$(uci -q get "haproxy.${backend}.server" | head -1 | awk '{print $2}')
        fi

        printf "%-15s %-30s %-20s\n" "${backend:-N/A}" "$domain" "${server:-N/A}"
        found=1
    done

    [ "$found" = "0" ] && echo "  No SSL backends configured"
    echo ""
}

cmd_ssl_remove() {
    local service="$1"

    if [ -z "$service" ]; then
        log_err "Usage: secubox-exposure ssl remove <service>"
        return 1
    fi

    load_config

    # Check if haproxyctl exists
    if [ ! -x "/usr/sbin/haproxyctl" ]; then
        log_err "haproxyctl not found"
        return 1
    fi

    local backend_name="$service"
    local removed=0

    # Find and remove vhosts pointing to this backend
    for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
        local vhost_backend=$(uci -q get "haproxy.${vhost}.backend")
        if [ "$vhost_backend" = "$backend_name" ]; then
            log_info "Removing vhost '$vhost'"
            uci delete "haproxy.${vhost}"
            removed=1
        fi
    done

    # Remove backend if it exists
    if uci -q get "haproxy.${backend_name}" >/dev/null 2>&1; then
        log_info "Removing backend '$backend_name'"
        uci delete "haproxy.${backend_name}"
        removed=1
    fi

    if [ "$removed" = "0" ]; then
        log_err "No backend or vhost found for '$service'"
        return 1
    fi

    # Commit HAProxy UCI changes
    uci commit haproxy

    # Update exposure UCI
    uci delete "${CONFIG_NAME}.${service}.ssl" 2>/dev/null
    uci delete "${CONFIG_NAME}.${service}.ssl_domain" 2>/dev/null
    uci commit "$CONFIG_NAME"

    # Regenerate and reload HAProxy
    log_info "Regenerating HAProxy config..."
    /usr/sbin/haproxyctl generate

    log_info "Reloading HAProxy..."
    /usr/sbin/haproxyctl reload

    log_ok "SSL backend removed"
}

# ============================================================================
# EMANCIPATE - Unified Multi-Channel Exposure
# ============================================================================

cmd_emancipate() {
    local service="$1"
    local port="$2"
    local domain="$3"
    shift 3 2>/dev/null || true

    if [ -z "$service" ] || [ -z "$port" ]; then
        log_err "Usage: secubox-exposure emancipate <service> <port> <domain> [--tor] [--dns] [--mesh] [--all]"
        return 1
    fi

    local enable_tor=0 enable_dns=0 enable_mesh=0

    # Parse flags
    while [ $# -gt 0 ]; do
        case "$1" in
            --tor) enable_tor=1 ;;
            --dns) enable_dns=1 ;;
            --mesh) enable_mesh=1 ;;
            --all) enable_tor=1; enable_dns=1; enable_mesh=1 ;;
        esac
        shift
    done

    # Default to --all if no flags specified
    if [ "$enable_tor" = "0" ] && [ "$enable_dns" = "0" ] && [ "$enable_mesh" = "0" ]; then
        enable_tor=1
        enable_dns=1
        enable_mesh=1
    fi

    load_config

    # Validate: check if port is listening
    local listen_info=$(netstat -tlnp 2>/dev/null | grep ":$port ")
    if [ -z "$listen_info" ]; then
        log_warn "Port $port is not currently listening (service may start later)"
    else
        # Check if localhost-only
        local listen_addr=$(echo "$listen_info" | awk '{print $4}' | head -1)
        case "$listen_addr" in
            127.0.0.1:*|::1:*)
                log_err "Cannot expose localhost-only service (listening on $listen_addr)"
                log_err "Service must bind to 0.0.0.0 or a specific LAN IP"
                return 1
                ;;
        esac
    fi

    echo ""
    log_info "Emancipating ${CYAN}$service${NC} on port $port"
    echo ""

    local success=0
    local failed=0

    # Channel 1: Tor Hidden Service
    if [ "$enable_tor" = "1" ]; then
        log_info "Channel 1: Tor Hidden Service..."
        if cmd_tor_add "$service" "$port" 80 2>/dev/null; then
            local onion=$(cat "$TOR_HIDDEN_DIR/$service/hostname" 2>/dev/null)
            if [ -n "$onion" ]; then
                echo -e "  ${GREEN}✓${NC} Tor: http://$onion"
                success=$((success + 1))
            else
                echo -e "  ${YELLOW}⏳${NC} Tor: Generating onion address..."
                success=$((success + 1))
            fi
        else
            echo -e "  ${RED}✗${NC} Tor: Failed to create hidden service"
            failed=$((failed + 1))
        fi
    fi

    # Channel 2: DNS/SSL via HAProxy
    if [ "$enable_dns" = "1" ]; then
        if [ -z "$domain" ]; then
            echo -e "  ${YELLOW}⚠${NC} DNS: Skipped (no domain specified)"
        else
            log_info "Channel 2: DNS/SSL via HAProxy..."

            # Check for dnsctl
            if [ -x "/usr/sbin/dnsctl" ]; then
                # Get public IP
                local public_ip=$(curl -s --connect-timeout 5 ifconfig.me 2>/dev/null || \
                                  curl -s --connect-timeout 5 icanhazip.com 2>/dev/null || \
                                  echo "")

                if [ -n "$public_ip" ]; then
                    # Extract subdomain from FQDN (e.g., gitea.example.com -> gitea)
                    local subdomain="${domain%%.*}"

                    # Create DNS A record
                    if /usr/sbin/dnsctl add A "$subdomain" "$public_ip" 2>/dev/null; then
                        log_ok "DNS A record created: $subdomain -> $public_ip"
                    else
                        log_warn "DNS A record creation failed (may already exist)"
                    fi
                else
                    log_warn "Could not determine public IP for DNS record"
                fi
            else
                log_warn "dnsctl not found - skipping DNS record creation"
            fi

            # Create HAProxy vhost + backend
            if cmd_ssl_add "$service" "$domain" "$port" 2>/dev/null; then
                echo -e "  ${GREEN}✓${NC} DNS: https://$domain"
                success=$((success + 1))

                # Request certificate if haproxyctl supports it
                if [ -x "/usr/sbin/haproxyctl" ]; then
                    log_info "Requesting SSL certificate for $domain..."
                    /usr/sbin/haproxyctl cert add "$domain" 2>/dev/null || \
                        log_warn "Certificate request may have failed (check haproxyctl logs)"
                fi
            else
                echo -e "  ${RED}✗${NC} DNS: Failed to create HAProxy backend"
                failed=$((failed + 1))
            fi
        fi
    fi

    # Channel 3: Mesh P2P
    if [ "$enable_mesh" = "1" ]; then
        log_info "Channel 3: Mesh P2P..."
        if [ -x "/usr/sbin/secubox-p2p" ]; then
            if /usr/sbin/secubox-p2p publish "$service" "$port" "$service" 2>/dev/null; then
                echo -e "  ${GREEN}✓${NC} Mesh: http://${service}.mesh.local"
                success=$((success + 1))

                # Update mesh DNS
                /usr/sbin/secubox-p2p dns-update 2>/dev/null || true
            else
                echo -e "  ${RED}✗${NC} Mesh: Failed to publish service"
                failed=$((failed + 1))
            fi
        else
            echo -e "  ${YELLOW}⚠${NC} Mesh: secubox-p2p not installed"
        fi
    fi

    # Store emancipation state in UCI
    uci set "${CONFIG_NAME}.${service}=service"
    uci set "${CONFIG_NAME}.${service}.port=$port"
    [ -n "$domain" ] && uci set "${CONFIG_NAME}.${service}.domain=$domain"
    uci set "${CONFIG_NAME}.${service}.tor=$enable_tor"
    uci set "${CONFIG_NAME}.${service}.dns=$enable_dns"
    uci set "${CONFIG_NAME}.${service}.mesh=$enable_mesh"
    uci set "${CONFIG_NAME}.${service}.emancipated=1"
    uci commit "$CONFIG_NAME"

    echo ""
    if [ "$failed" -eq 0 ]; then
        log_ok "Emancipation complete: $success channel(s) active"
    else
        log_warn "Emancipation partial: $success succeeded, $failed failed"
    fi
    echo ""
}

cmd_revoke() {
    local service="$1"
    shift 2>/dev/null || true

    if [ -z "$service" ]; then
        log_err "Usage: secubox-exposure revoke <service> [--tor] [--dns] [--mesh] [--all]"
        return 1
    fi

    local enable_tor=0 enable_dns=0 enable_mesh=0

    # Parse flags
    while [ $# -gt 0 ]; do
        case "$1" in
            --tor) enable_tor=1 ;;
            --dns) enable_dns=1 ;;
            --mesh) enable_mesh=1 ;;
            --all) enable_tor=1; enable_dns=1; enable_mesh=1 ;;
        esac
        shift
    done

    # Default to --all if no flags specified
    if [ "$enable_tor" = "0" ] && [ "$enable_dns" = "0" ] && [ "$enable_mesh" = "0" ]; then
        enable_tor=1
        enable_dns=1
        enable_mesh=1
    fi

    load_config

    echo ""
    log_info "Revoking exposure for ${CYAN}$service${NC}"
    echo ""

    local success=0

    # Channel 1: Remove Tor Hidden Service
    if [ "$enable_tor" = "1" ]; then
        if [ -d "$TOR_HIDDEN_DIR/$service" ]; then
            log_info "Removing Tor hidden service..."
            cmd_tor_remove "$service" 2>/dev/null
            echo -e "  ${GREEN}✓${NC} Tor: Hidden service removed"
            success=$((success + 1))
        else
            echo -e "  ${YELLOW}−${NC} Tor: No hidden service found"
        fi
    fi

    # Channel 2: Remove DNS/SSL
    if [ "$enable_dns" = "1" ]; then
        local domain=$(uci -q get "${CONFIG_NAME}.${service}.domain")

        if [ -n "$domain" ]; then
            log_info "Removing DNS/SSL..."

            # Remove DNS record
            if [ -x "/usr/sbin/dnsctl" ]; then
                local subdomain="${domain%%.*}"
                /usr/sbin/dnsctl rm A "$subdomain" 2>/dev/null || true
            fi

            # Remove HAProxy vhost/backend
            cmd_ssl_remove "$service" 2>/dev/null
            echo -e "  ${GREEN}✓${NC} DNS: HAProxy vhost removed"
            success=$((success + 1))

            # Remove certificate
            if [ -x "/usr/sbin/haproxyctl" ]; then
                /usr/sbin/haproxyctl cert remove "$domain" 2>/dev/null || true
            fi
        else
            echo -e "  ${YELLOW}−${NC} DNS: No domain configured"
        fi
    fi

    # Channel 3: Remove from Mesh
    if [ "$enable_mesh" = "1" ]; then
        if [ -x "/usr/sbin/secubox-p2p" ]; then
            log_info "Removing from mesh..."
            /usr/sbin/secubox-p2p unpublish "$service" 2>/dev/null || true
            /usr/sbin/secubox-p2p dns-update 2>/dev/null || true
            echo -e "  ${GREEN}✓${NC} Mesh: Service unpublished"
            success=$((success + 1))
        else
            echo -e "  ${YELLOW}−${NC} Mesh: secubox-p2p not installed"
        fi
    fi

    # Update UCI
    if [ "$enable_tor" = "1" ] && [ "$enable_dns" = "1" ] && [ "$enable_mesh" = "1" ]; then
        # Full revoke - delete the entire section
        uci delete "${CONFIG_NAME}.${service}" 2>/dev/null
    else
        # Partial revoke - just update flags
        [ "$enable_tor" = "1" ] && uci set "${CONFIG_NAME}.${service}.tor=0"
        [ "$enable_dns" = "1" ] && uci set "${CONFIG_NAME}.${service}.dns=0"
        [ "$enable_mesh" = "1" ] && uci set "${CONFIG_NAME}.${service}.mesh=0"

        # Check if all channels are now disabled
        local tor_enabled=$(uci -q get "${CONFIG_NAME}.${service}.tor" || echo "0")
        local dns_enabled=$(uci -q get "${CONFIG_NAME}.${service}.dns" || echo "0")
        local mesh_enabled=$(uci -q get "${CONFIG_NAME}.${service}.mesh" || echo "0")

        if [ "$tor_enabled" = "0" ] && [ "$dns_enabled" = "0" ] && [ "$mesh_enabled" = "0" ]; then
            uci set "${CONFIG_NAME}.${service}.emancipated=0"
        fi
    fi
    uci commit "$CONFIG_NAME"

    echo ""
    log_ok "Revocation complete: $success channel(s) removed"
    echo ""
}

# ============================================================================
# STATUS & HELP
# ============================================================================

cmd_status() {
    load_config

    echo ""
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
    echo -e "${CYAN}            SecuBox Service Exposure Status${NC}"
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
    echo ""

    # Count services
    local total_services=$(get_listening_ports | wc -l)
    local external_services=$(get_listening_ports | grep -E "0\.0\.0\.0|::" | wc -l)

    echo -e "${BLUE}Services:${NC}"
    echo "  Total listening: $total_services"
    echo "  External (0.0.0.0): $external_services"
    echo ""

    # Tor status
    local tor_services=0
    [ -d "$TOR_HIDDEN_DIR" ] && tor_services=$(ls -1 "$TOR_HIDDEN_DIR" 2>/dev/null | wc -l)
    echo -e "${BLUE}Tor Hidden Services:${NC} $tor_services"
    if [ "$tor_services" -gt 0 ]; then
        for dir in "$TOR_HIDDEN_DIR"/*/; do
            [ -d "$dir" ] || continue
            local svc=$(basename "$dir")
            local onion=$(cat "$dir/hostname" 2>/dev/null)
            [ -n "$onion" ] && echo "  - $svc: ${onion:0:16}..."
        done
    fi
    echo ""

    # HAProxy backends (from UCI)
    local ssl_backends=0
    echo -e "${BLUE}HAProxy SSL Backends:${NC}"
    for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
        local domain=$(uci -q get "haproxy.${vhost}.domain")
        local backend=$(uci -q get "haproxy.${vhost}.backend")
        local enabled=$(uci -q get "haproxy.${vhost}.enabled")
        [ "$enabled" != "1" ] && continue
        [ -z "$domain" ] && continue
        echo "  - ${backend}: ${domain}"
        ssl_backends=$((ssl_backends + 1))
    done
    [ "$ssl_backends" = "0" ] && echo "  (none configured)"
    echo ""

    # Emancipated services
    echo -e "${BLUE}Emancipated Services:${NC}"
    local emancipated=0
    for svc in $(uci show "$CONFIG_NAME" 2>/dev/null | grep "=service$" | cut -d'.' -f2 | cut -d'=' -f1); do
        local is_emancipated=$(uci -q get "${CONFIG_NAME}.${svc}.emancipated")
        [ "$is_emancipated" != "1" ] && continue

        local port=$(uci -q get "${CONFIG_NAME}.${svc}.port")
        local domain=$(uci -q get "${CONFIG_NAME}.${svc}.domain")
        local tor=$(uci -q get "${CONFIG_NAME}.${svc}.tor")
        local dns=$(uci -q get "${CONFIG_NAME}.${svc}.dns")
        local mesh=$(uci -q get "${CONFIG_NAME}.${svc}.mesh")

        local channels=""
        [ "$tor" = "1" ] && channels="${channels}Tor "
        [ "$dns" = "1" ] && channels="${channels}DNS "
        [ "$mesh" = "1" ] && channels="${channels}Mesh"

        echo "  - ${svc} (:${port}) [${channels}]"
        [ -n "$domain" ] && echo "    Domain: ${domain}"
        emancipated=$((emancipated + 1))
    done
    [ "$emancipated" = "0" ] && echo "  (none)"
    echo ""
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
}

cmd_help() {
    cat << EOF
SecuBox Service Exposure Manager

Usage: secubox-exposure <command> [options]

COMMANDS:
  scan                    Scan all listening services
  conflicts               Detect port conflicts
  fix-port <svc> [port]   Change service port (auto-assigns if no port given)
  status                  Show exposure status summary

  emancipate <svc> <port> <domain> [flags]
                          Expose service through multiple channels
                          Flags: --tor, --dns, --mesh, --all (default)

  revoke <svc> [flags]    Remove service exposure
                          Flags: --tor, --dns, --mesh, --all (default)

  tor add <svc> [port]    Create Tor hidden service
  tor list                List hidden services
  tor remove <svc>        Remove hidden service
  tor sync                Sync hidden services to Tor Shield

  ssl add <svc> <domain>  Add HAProxy SSL backend
  ssl list                List SSL backends
  ssl remove <svc>        Remove SSL backend

EXAMPLES:
  # Full emancipation (Tor + DNS + Mesh)
  secubox-exposure emancipate gitea 3000 gitea.example.com --all

  # Tor-only exposure
  secubox-exposure emancipate streamlit 8501 "" --tor

  # DNS/SSL only
  secubox-exposure emancipate ollama 11434 ai.example.com --dns

  # Revoke all channels
  secubox-exposure revoke gitea --all

  # Revoke only Tor
  secubox-exposure revoke gitea --tor

  # Legacy commands
  secubox-exposure scan
  secubox-exposure tor add gitea
  secubox-exposure ssl add gitea git.example.com

EOF
}

# ============================================================================
# MAIN
# ============================================================================

case "$1" in
    scan)
        cmd_scan
        ;;
    conflicts)
        load_config
        cmd_conflicts
        ;;
    fix-port)
        cmd_fix_port "$2" "$3"
        ;;
    status)
        cmd_status
        ;;
    emancipate)
        shift
        cmd_emancipate "$@"
        ;;
    revoke)
        shift
        cmd_revoke "$@"
        ;;
    tor)
        case "$2" in
            add) cmd_tor_add "$3" "$4" "$5" ;;
            list) cmd_tor_list ;;
            remove) cmd_tor_remove "$3" ;;
            sync) cmd_tor_sync ;;
            *) log_err "Usage: secubox-exposure tor {add|list|remove|sync}"; exit 1 ;;
        esac
        ;;
    ssl)
        case "$2" in
            add) cmd_ssl_add "$3" "$4" "$5" ;;
            list) cmd_ssl_list ;;
            remove) cmd_ssl_remove "$3" ;;
            *) log_err "Usage: secubox-exposure ssl {add|list|remove}"; exit 1 ;;
        esac
        ;;
    help|--help|-h)
        cmd_help
        ;;
    *)
        cmd_help
        exit 1
        ;;
esac
