#!/bin/sh
# rProxy — Менеджер обратного прокси для роутеров Keenetic
# Публикация локальных сервисов через SSH-туннели + nginx на VPS
# http://5.104.75.50:3000/Petro1990/rProxy

VERSION="1.0.0"
CONF_DIR="/opt/etc/rproxy"
CONF_FILE="$CONF_DIR/rproxy.conf"
SERVICES_DIR="$CONF_DIR/services"
PID_DIR="/opt/var/run/rproxy"
SSH_KEY="$CONF_DIR/id_rsa"
REMOTE_NGINX_DIR="/etc/nginx/sites-enabled"
REMOTE_NGINX_AVAILABLE="/etc/nginx/sites-available"
BASE_TUNNEL_PORT=10000

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

msg()     { printf "${GREEN}▸${NC} %s\n" "$*"; }
warn()    { printf "${YELLOW}⚠${NC} %s\n" "$*"; }
err()     { printf "${RED}✖${NC} %s\n" "$*" >&2; }
header()  { printf "\n${CYAN}${BOLD}%s${NC}\n" "$*"; }

# ─── Helpers ──────────────────────────────────────────────────────────
check_conf() {
    if [ ! -f "$CONF_FILE" ]; then
        err "Конфигурация не найдена. Запустите: rproxy setup"
        exit 1
    fi
    . "$CONF_FILE"
}

load_service() {
    local svc_file="$SERVICES_DIR/$1.conf"
    if [ ! -f "$svc_file" ]; then
        err "Сервис '$1' не найден"
        exit 1
    fi
    . "$svc_file"
}

ssh_cmd() {
    if [ "$VPS_AUTH" = "password" ]; then
        sshpass -p "$VPS_PASS" ssh -o StrictHostKeyChecking=no -p "$VPS_PORT" "$VPS_USER@$VPS_HOST" "$@"
    else
        ssh -o StrictHostKeyChecking=no -i "$SSH_KEY" -p "$VPS_PORT" "$VPS_USER@$VPS_HOST" "$@"
    fi
}

scp_cmd() {
    if [ "$VPS_AUTH" = "password" ]; then
        sshpass -p "$VPS_PASS" scp -o StrictHostKeyChecking=no -P "$VPS_PORT" "$@"
    else
        scp -o StrictHostKeyChecking=no -i "$SSH_KEY" -P "$VPS_PORT" "$@"
    fi
}

next_free_port() {
    local port=$BASE_TUNNEL_PORT
    while true; do
        local used=0
        for f in "$SERVICES_DIR"/*.conf 2>/dev/null; do
            [ -f "$f" ] || continue
            grep -q "TUNNEL_PORT=$port" "$f" && used=1 && break
        done
        [ "$used" -eq 0 ] && echo "$port" && return
        port=$((port + 1))
    done
}

get_pid_file() {
    echo "$PID_DIR/$1.pid"
}

is_running() {
    local pf
    pf=$(get_pid_file "$1")
    [ -f "$pf" ] && kill -0 "$(cat "$pf")" 2>/dev/null
}

# ─── SETUP ────────────────────────────────────────────────────────────
do_setup() {
    header "rProxy — Настройка подключения к VPS"

    printf "IP-адрес VPS: "
    read -r vps_host
    printf "SSH порт [22]: "
    read -r vps_port
    vps_port=${vps_port:-22}
    printf "SSH пользователь [root]: "
    read -r vps_user
    vps_user=${vps_user:-root}

    printf "\nМетод авторизации:\n"
    printf "  1) SSH-ключ (рекомендуется)\n"
    printf "  2) Пароль\n"
    printf "Выберите [1]: "
    read -r auth_choice
    auth_choice=${auth_choice:-1}

    local vps_auth="key"
    local vps_pass=""

    if [ "$auth_choice" = "2" ]; then
        vps_auth="password"
        printf "SSH пароль: "
        stty -echo 2>/dev/null
        read -r vps_pass
        stty echo 2>/dev/null
        printf "\n"

        # Check sshpass
        if ! command -v sshpass >/dev/null 2>&1; then
            msg "Устанавливаю sshpass..."
            opkg install sshpass 2>/dev/null || {
                err "Не удалось установить sshpass. Установите вручную: opkg install sshpass"
                exit 1
            }
        fi
    else
        # Generate SSH key if not exists
        if [ ! -f "$SSH_KEY" ]; then
            msg "Генерирую SSH-ключ..."
            mkdir -p "$CONF_DIR"
            ssh-keygen -t rsa -b 4096 -f "$SSH_KEY" -N "" -q
            msg "Ключ создан: $SSH_KEY"
        fi
    fi

    # Test connection
    msg "Проверяю подключение к $vps_host..."
    if [ "$vps_auth" = "password" ]; then
        if ! sshpass -p "$vps_pass" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
            -p "$vps_port" "$vps_user@$vps_host" "echo ok" >/dev/null 2>&1; then
            err "Не удалось подключиться к VPS. Проверьте параметры."
            exit 1
        fi
    else
        # Copy key to VPS
        msg "Копирую SSH-ключ на VPS..."
        printf "Введите пароль VPS для копирования ключа:\n"
        ssh-copy-id -i "$SSH_KEY.pub" -p "$vps_port" "$vps_user@$vps_host" 2>/dev/null || {
            # Fallback: manual copy
            cat "$SSH_KEY.pub" | ssh -p "$vps_port" "$vps_user@$vps_host" \
                "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
        }

        if ! ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
            -i "$SSH_KEY" -p "$vps_port" "$vps_user@$vps_host" "echo ok" >/dev/null 2>&1; then
            err "Не удалось подключиться к VPS по ключу."
            exit 1
        fi
    fi
    msg "Подключение успешно!"

    # Setup nginx on VPS
    msg "Настраиваю nginx на VPS..."
    ssh_cmd_raw() {
        if [ "$vps_auth" = "password" ]; then
            sshpass -p "$vps_pass" ssh -o StrictHostKeyChecking=no -p "$vps_port" "$vps_user@$vps_host" "$@"
        else
            ssh -o StrictHostKeyChecking=no -i "$SSH_KEY" -p "$vps_port" "$vps_user@$vps_host" "$@"
        fi
    }

    ssh_cmd_raw "
        if ! command -v nginx >/dev/null 2>&1; then
            echo 'Устанавливаю nginx...'
            apt-get update -qq && apt-get install -y -qq nginx >/dev/null 2>&1 || \
            yum install -y nginx >/dev/null 2>&1 || {
                echo 'FAIL: не удалось установить nginx'
                exit 1
            }
        fi
        mkdir -p $REMOTE_NGINX_DIR
        mkdir -p /etc/nginx/rproxy
        # Ensure sites-enabled is included
        if ! grep -q 'include.*sites-enabled' /etc/nginx/nginx.conf 2>/dev/null; then
            sed -i '/http {/a\\    include /etc/nginx/sites-enabled/*.conf;' /etc/nginx/nginx.conf 2>/dev/null
        fi
        # Enable and start nginx
        systemctl enable nginx 2>/dev/null
        systemctl start nginx 2>/dev/null
        echo 'OK'
    " || {
        warn "Не удалось автоматически настроить nginx. Убедитесь, что nginx установлен на VPS."
    }

    # Save config
    mkdir -p "$CONF_DIR" "$SERVICES_DIR" "$PID_DIR"
    cat > "$CONF_FILE" <<EOF
# rProxy VPS configuration
VPS_HOST="$vps_host"
VPS_PORT="$vps_port"
VPS_USER="$vps_user"
VPS_AUTH="$vps_auth"
VPS_PASS="$vps_pass"
EOF
    chmod 600 "$CONF_FILE"

    msg "Конфигурация сохранена: $CONF_FILE"
    header "Готово! Добавьте первый сервис: rproxy add <имя> <хост:порт>"
}

# ─── ADD ──────────────────────────────────────────────────────────────
do_add() {
    local name="$1"
    local target="$2"
    shift 2

    if [ -z "$name" ] || [ -z "$target" ]; then
        err "Использование: rproxy add <имя> <хост:порт> [--domain example.com] [--port 8080]"
        exit 1
    fi

    check_conf

    if [ -f "$SERVICES_DIR/$name.conf" ]; then
        err "Сервис '$name' уже существует. Удалите его сначала: rproxy remove $name"
        exit 1
    fi

    local domain=""
    local ext_port=""

    while [ $# -gt 0 ]; do
        case "$1" in
            --domain|-d)
                domain="$2"
                shift 2
                ;;
            --port|-p)
                ext_port="$2"
                shift 2
                ;;
            *)
                shift
                ;;
        esac
    done

    local tunnel_port
    tunnel_port=$(next_free_port)

    # Determine external port
    if [ -n "$domain" ]; then
        ext_port=80
    elif [ -z "$ext_port" ]; then
        ext_port=$tunnel_port
    fi

    local target_host="${target%:*}"
    local target_port="${target#*:}"

    # Validate target
    if [ "$target_host" = "$target_port" ]; then
        # No colon — assume localhost
        target_port="$target_host"
        target_host="127.0.0.1"
    fi

    msg "Добавляю сервис '$name'..."
    msg "  Цель: $target_host:$target_port"
    msg "  Туннель порт: $tunnel_port"
    [ -n "$domain" ] && msg "  Домен: $domain"
    msg "  Внешний порт: $ext_port"

    # Generate nginx config
    local nginx_conf=""
    if [ -n "$domain" ]; then
        nginx_conf="# rProxy: $name
server {
    listen 80;
    server_name $domain;

    location / {
        proxy_pass http://127.0.0.1:$tunnel_port;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \"upgrade\";
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}
"
    else
        nginx_conf="# rProxy: $name
server {
    listen $ext_port;

    location / {
        proxy_pass http://127.0.0.1:$tunnel_port;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \"upgrade\";
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}
"
    fi

    # Deploy nginx config to VPS
    local tmp_file="/tmp/rproxy_nginx_$name.conf"
    printf '%s' "$nginx_conf" > "$tmp_file"
    scp_cmd "$tmp_file" "$VPS_USER@$VPS_HOST:$REMOTE_NGINX_DIR/rproxy_$name.conf" || {
        err "Не удалось загрузить конфиг nginx на VPS"
        rm -f "$tmp_file"
        exit 1
    }
    rm -f "$tmp_file"

    # Reload nginx
    ssh_cmd "nginx -t && systemctl reload nginx" || {
        err "Ошибка конфигурации nginx на VPS"
        ssh_cmd "rm -f $REMOTE_NGINX_DIR/rproxy_$name.conf"
        exit 1
    }

    # Save service config
    cat > "$SERVICES_DIR/$name.conf" <<EOF
# rProxy service: $name
SVC_NAME="$name"
SVC_TARGET_HOST="$target_host"
SVC_TARGET_PORT="$target_port"
SVC_TUNNEL_PORT="$tunnel_port"
SVC_EXT_PORT="$ext_port"
SVC_DOMAIN="$domain"
SVC_ENABLED="yes"
EOF

    msg "Сервис '$name' добавлен!"

    # Auto-start the tunnel
    do_start "$name"
}

# ─── REMOVE ───────────────────────────────────────────────────────────
do_remove() {
    local name="$1"
    [ -z "$name" ] && { err "Использование: rproxy remove <имя>"; exit 1; }

    check_conf
    load_service "$name"

    # Stop tunnel if running
    is_running "$name" && do_stop "$name"

    # Remove nginx config from VPS
    msg "Удаляю конфиг nginx на VPS..."
    ssh_cmd "rm -f $REMOTE_NGINX_DIR/rproxy_$name.conf && nginx -t && systemctl reload nginx" || {
        warn "Не удалось удалить конфиг nginx на VPS"
    }

    # Remove local config
    rm -f "$SERVICES_DIR/$name.conf"
    rm -f "$(get_pid_file "$name")"

    msg "Сервис '$name' удалён"
}

# ─── START ────────────────────────────────────────────────────────────
do_start() {
    local name="$1"

    check_conf

    if [ -z "$name" ]; then
        # Start all enabled services
        local count=0
        for f in "$SERVICES_DIR"/*.conf; do
            [ -f "$f" ] || continue
            local svc_name
            svc_name=$(basename "$f" .conf)
            . "$f"
            if [ "$SVC_ENABLED" = "yes" ]; then
                do_start "$svc_name"
                count=$((count + 1))
            fi
        done
        [ "$count" -eq 0 ] && warn "Нет сервисов для запуска"
        return
    fi

    load_service "$name"

    if is_running "$name"; then
        warn "Сервис '$name' уже запущен"
        return
    fi

    msg "Запускаю туннель '$name' ($SVC_TARGET_HOST:$SVC_TARGET_PORT → VPS:$SVC_TUNNEL_PORT)..."

    mkdir -p "$PID_DIR"
    local pid_file
    pid_file=$(get_pid_file "$name")

    local ssh_opts="-o StrictHostKeyChecking=no -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes"

    if [ "$VPS_AUTH" = "password" ]; then
        # With sshpass
        AUTOSSH_GATETIME=0 sshpass -p "$VPS_PASS" autossh -M 0 -f -N \
            $ssh_opts \
            -p "$VPS_PORT" \
            -R "0.0.0.0:$SVC_TUNNEL_PORT:$SVC_TARGET_HOST:$SVC_TARGET_PORT" \
            "$VPS_USER@$VPS_HOST" &
    else
        # With key
        AUTOSSH_GATETIME=0 autossh -M 0 -f -N \
            $ssh_opts \
            -i "$SSH_KEY" \
            -p "$VPS_PORT" \
            -R "0.0.0.0:$SVC_TUNNEL_PORT:$SVC_TARGET_HOST:$SVC_TARGET_PORT" \
            "$VPS_USER@$VPS_HOST" &
    fi

    local bg_pid=$!
    sleep 2

    # Find the actual autossh/ssh process
    local real_pid
    real_pid=$(pgrep -f "autossh.*$SVC_TUNNEL_PORT:$SVC_TARGET_HOST:$SVC_TARGET_PORT" 2>/dev/null | head -1)
    if [ -z "$real_pid" ]; then
        real_pid=$(pgrep -f "ssh.*$SVC_TUNNEL_PORT:$SVC_TARGET_HOST:$SVC_TARGET_PORT" 2>/dev/null | head -1)
    fi

    if [ -n "$real_pid" ]; then
        echo "$real_pid" > "$pid_file"
        msg "Туннель '$name' запущен (PID: $real_pid)"
    else
        echo "$bg_pid" > "$pid_file"
        msg "Туннель '$name' запущен (PID: $bg_pid)"
    fi
}

# ─── STOP ─────────────────────────────────────────────────────────────
do_stop() {
    local name="$1"

    check_conf

    if [ -z "$name" ]; then
        # Stop all
        for f in "$SERVICES_DIR"/*.conf; do
            [ -f "$f" ] || continue
            do_stop "$(basename "$f" .conf)"
        done
        return
    fi

    local pid_file
    pid_file=$(get_pid_file "$name")

    if [ ! -f "$pid_file" ]; then
        warn "Сервис '$name' не запущен"
        return
    fi

    local pid
    pid=$(cat "$pid_file")

    # Kill autossh and child ssh processes
    kill "$pid" 2>/dev/null
    # Also kill any related ssh tunnels
    pkill -f "ssh.*$SVC_TUNNEL_PORT:.*:$SVC_TARGET_PORT" 2>/dev/null

    rm -f "$pid_file"
    msg "Туннель '$name' остановлен"
}

# ─── RESTART ──────────────────────────────────────────────────────────
do_restart() {
    local name="$1"
    check_conf

    if [ -z "$name" ]; then
        for f in "$SERVICES_DIR"/*.conf; do
            [ -f "$f" ] || continue
            do_restart "$(basename "$f" .conf)"
        done
        return
    fi

    load_service "$name"
    do_stop "$name"
    sleep 1
    do_start "$name"
}

# ─── STATUS ───────────────────────────────────────────────────────────
do_status() {
    check_conf

    header "rProxy v$VERSION — Статус сервисов"
    printf "\n"
    printf "${BOLD}%-15s %-25s %-8s %-10s %-20s${NC}\n" "ИМЯ" "ЦЕЛЬ" "ПОРТ" "СТАТУС" "ДОМЕН"
    printf "%-15s %-25s %-8s %-10s %-20s\n" "───────────────" "─────────────────────────" "────────" "──────────" "────────────────────"

    local has_services=0
    for f in "$SERVICES_DIR"/*.conf; do
        [ -f "$f" ] || continue
        has_services=1
        . "$f"

        local status_text
        if is_running "$SVC_NAME"; then
            status_text="${GREEN}▲ онлайн${NC}"
        else
            status_text="${RED}▼ офлайн${NC}"
        fi

        local domain_text="${SVC_DOMAIN:-—}"

        printf "%-15s %-25s %-8s ${status_text}   %-20s\n" \
            "$SVC_NAME" "$SVC_TARGET_HOST:$SVC_TARGET_PORT" "$SVC_EXT_PORT" "$domain_text"
    done

    if [ "$has_services" -eq 0 ]; then
        printf "\n  ${YELLOW}Нет добавленных сервисов.${NC}\n"
        printf "  Добавьте сервис: ${CYAN}rproxy add <имя> <хост:порт>${NC}\n"
    fi

    printf "\n${BOLD}VPS:${NC} $VPS_HOST:$VPS_PORT (${VPS_AUTH})\n\n"
}

# ─── LIST ─────────────────────────────────────────────────────────────
do_list() {
    check_conf

    for f in "$SERVICES_DIR"/*.conf; do
        [ -f "$f" ] || continue
        . "$f"
        local state="остановлен"
        is_running "$SVC_NAME" && state="работает"

        local info="$SVC_NAME  $SVC_TARGET_HOST:$SVC_TARGET_PORT → :$SVC_EXT_PORT"
        [ -n "$SVC_DOMAIN" ] && info="$info ($SVC_DOMAIN)"
        info="$info [$state]"
        echo "$info"
    done
}

# ─── ENABLE / DISABLE ────────────────────────────────────────────────
do_enable() {
    local name="$1"
    [ -z "$name" ] && { err "Использование: rproxy enable <имя>"; exit 1; }

    check_conf
    load_service "$name"

    sed -i "s/SVC_ENABLED=.*/SVC_ENABLED=\"yes\"/" "$SERVICES_DIR/$name.conf"
    msg "Сервис '$name' добавлен в автозапуск"
}

do_disable() {
    local name="$1"
    [ -z "$name" ] && { err "Использование: rproxy disable <имя>"; exit 1; }

    check_conf
    load_service "$name"

    sed -i "s/SVC_ENABLED=.*/SVC_ENABLED=\"no\"/" "$SERVICES_DIR/$name.conf"
    msg "Сервис '$name' убран из автозапуска"
}

# ─── LOGS ─────────────────────────────────────────────────────────────
do_logs() {
    local name="$1"
    [ -z "$name" ] && { err "Использование: rproxy logs <имя>"; exit 1; }

    check_conf

    msg "Логи nginx на VPS для '$name':"
    ssh_cmd "grep -i 'rproxy_$name\\|$name' /var/log/nginx/access.log /var/log/nginx/error.log 2>/dev/null | tail -50"
}

# ─── USAGE ────────────────────────────────────────────────────────────
usage() {
    cat <<EOF

${CYAN}${BOLD}rProxy v$VERSION${NC} — Менеджер обратного прокси для Keenetic

${BOLD}Использование:${NC}
  rproxy <команда> [аргументы]

${BOLD}Команды:${NC}
  ${GREEN}setup${NC}                              Настроить подключение к VPS
  ${GREEN}add${NC} <имя> <хост:порт> [опции]      Добавить сервис
  ${GREEN}remove${NC} <имя>                        Удалить сервис
  ${GREEN}start${NC} [имя]                         Запустить туннель (все/один)
  ${GREEN}stop${NC} [имя]                          Остановить туннель (все/один)
  ${GREEN}restart${NC} [имя]                       Перезапустить туннель
  ${GREEN}status${NC}                              Показать статус сервисов
  ${GREEN}list${NC}                                Список сервисов (краткий)
  ${GREEN}enable${NC} <имя>                        Включить автозапуск
  ${GREEN}disable${NC} <имя>                       Выключить автозапуск
  ${GREEN}logs${NC} <имя>                          Показать логи nginx

${BOLD}Опции для add:${NC}
  --domain, -d <домен>       Привязать к домену (порт 80)
  --port, -p <порт>         Внешний порт (без домена)

${BOLD}Примеры:${NC}
  rproxy setup
  rproxy add nas 192.168.1.100:5000 --domain nas.example.com
  rproxy add camera 192.168.1.50:8080 --port 9090
  rproxy add homeassistant 192.168.1.10:8123
  rproxy remove nas
  rproxy status

EOF
}

# ─── MAIN ─────────────────────────────────────────────────────────────
mkdir -p "$CONF_DIR" "$SERVICES_DIR" "$PID_DIR" 2>/dev/null

case "${1:-}" in
    setup)          do_setup ;;
    add)            shift; do_add "$@" ;;
    remove|rm|del)  shift; do_remove "$@" ;;
    start)          shift; do_start "$@" ;;
    stop)           shift; do_stop "$@" ;;
    restart)        shift; do_restart "$@" ;;
    status|st)      do_status ;;
    list|ls)        do_list ;;
    enable)         shift; do_enable "$@" ;;
    disable)        shift; do_disable "$@" ;;
    logs|log)       shift; do_logs "$@" ;;
    -v|--version)   echo "rProxy v$VERSION" ;;
    -h|--help|"")   usage ;;
    *)              err "Неизвестная команда: $1"; usage; exit 1 ;;
esac
