#!/bin/sh
# proxy-run — 中央代理启动器
# 所有需要走代理的 app 都走它，加新 app 不用每个手写脚本。
#
# 用法：
#   proxy-run <程序> [参数...]          # 经代理启动，如：proxy-run /usr/bin/harbour-fintube %U
#   proxy-run --show                    # 显示当前代理配置和环境
#   proxy-run --check                   # 测试代理是否连通
#   proxy-run --sync                    # 按 dconf 名单生成全部代理图标（到 ~/.local/share/applications）
#   proxy-run --from-desktop <原.desktop> [新名字] [--user]  # 克隆一个 *-proxy 图标
#   proxy-run --help
#
# 代理地址来源（优先级从高到低）：
#   1. 环境变量 PROXY_URL
#   2. dconf /apps/proxy-run/proxy_url（设置 > 连接 > 代理应用 里改，即时生效）
#   3. $HOME/.config/proxy-run.conf  (写法：PROXY_URL=http://127.0.0.1:7890，或直接一行 URL)
#   4. /etc/proxy-run.conf           (同上)
#   5. 默认 http://127.0.0.1:7890
#
# 原理：
#   - 通用 Qt/yt-dlp/curl/Python：export http_proxy/https_proxy 等即可。
#   - QtWebEngine/Chromium（如 RootTitanium）：还要补 --proxy-server 到 QTWEBENGINE_CHROMIUM_FLAGS。
#   - Gecko（sailfish-browser）：不支持 env 代理，用独立 profile 的 prefs.js 写 network.proxy.*。
#   - 本机回环一律直连（no_proxy），否则 FinTube 内部 127.0.0.1 媒体代理会被送进外部代理而卡死。

# 诊断：每次启动都记一行（定位“点图标闪退”时看有没有执行到这里）
{ echo "proxy-run launch $(date '+%F %T') pid=$$ ppid=$PPID args: $0 $*"; } >>/tmp/proxy-run.log 2>&1 || true

USER_CONF="$HOME/.config/proxy-run.conf"
SYS_CONF="/etc/proxy-run.conf"
DEFAULT_PROXY="http://127.0.0.1:7890"

_pick_from_file() {
    # $1=文件路径，输出 URL（找不到输出空）
    [ -f "$1" ] || return 0
    # 优先找 PROXY_URL=xxx / PROXY=xxx
    v=$(grep -E -m1 '^[[:space:]]*(PROXY_URL|PROXY)[[:space:]]*=' "$1" 2>/dev/null | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]"'"'"']*$//; s/^[[:space:]"'"'"']*//')
    if [ -n "$v" ]; then echo "$v"; return 0; fi
    # 否则找第一行像 URL 的非注释行
    grep -E -m1 -v '^[[:space:]]*(#|$)' "$1" 2>/dev/null | sed -E 's/[[:space:]"'"'"']+//g'
}

_dconf_str() {
    # $1=dconf 键，输出去引号后的字符串（无 dconf 或键不存在则输出空）
    command -v dconf >/dev/null 2>&1 || return 0
    dconf read "$1" 2>/dev/null | sed -e "s/^'//" -e "s/'\$//"
}

if [ -n "$PROXY_URL" ]; then
    PROXY="$PROXY_URL"
else
    PROXY="$(_dconf_str /apps/proxy-run/proxy_url)"
    [ -z "$PROXY" ] && PROXY="$( _pick_from_file "$USER_CONF" )"
    [ -z "$PROXY" ] && PROXY="$( _pick_from_file "$SYS_CONF" )"
    [ -z "$PROXY" ] && PROXY="$DEFAULT_PROXY"
fi

# 从 PROXY 拆出 host/port（给 Gecko prefs.js 用）
PROXY_HOSTPORT=$(echo "$PROXY" | sed -E 's|^[a-zA-Z]+://||; s|/.*$||')
PROXY_HOST=$(echo "$PROXY_HOSTPORT" | cut -d: -f1)
PROXY_PORT=$(echo "$PROXY_HOSTPORT" | cut -s -d: -f2)
[ -z "$PROXY_PORT" ] && PROXY_PORT="8080"

usage() {
    sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'
}

cmd_show() {
    echo "PROXY=$PROXY"
    echo "host=$PROXY_HOST port=$PROXY_PORT"
    echo "dconf=/apps/proxy-run/proxy_url $(command -v dconf >/dev/null 2>&1 && echo "($(_dconf_str /apps/proxy-run/proxy_url))" || echo '(无 dconf)') "
    echo "user_conf=$USER_CONF $([ -f "$USER_CONF" ] && echo '(存在)' || echo '(无)')"
    echo "sys_conf=$SYS_CONF $([ -f "$SYS_CONF" ] && echo '(存在)' || echo '(无)')"
    echo "apps=$( _dconf_str /apps/proxy-run/apps)"
}

cmd_check() {
    echo "测试代理 $PROXY ..."
    if command -v curl >/dev/null 2>&1; then
        curl -x "$PROXY" -I https://www.youtube.com/ --max-time 10 2>&1 | head -n 10
        echo "curl exit: $?"
    else
        echo "无 curl，跳过连通测试"
    fi
}

# 生成单个代理图标。$1=原.desktop $2=新名字(可空) $3=目标目录 $4=sys|user
# 注意：原 Exec 整句保留（很多原图标是 /usr/bin/invoker 带参启动，只取第一个词会生成空壳）
gen_desktop() {
    SRC="$1"; NEWNAME="$2"; DESTDIR="$3"; MODE="$4"
    base=$(basename "$SRC" .desktop)
    DST="$DESTDIR/${base}-proxy.desktop"
    FULL_EXEC=$(grep -m1 '^Exec=' "$SRC" | cut -d= -f2-)
    # 去掉 desktop field codes（%U %u %F %f %c %k %i），proxy-run 自己在末尾加 %U
    PREFIX=$(echo "$FULL_EXEC" | sed -e 's/ *%[UuFfck] */ /g' -e 's/^ *//;s/ *$//')
    [ -z "$PREFIX" ] && { echo "空 Exec，跳过: $SRC" >&2; return 1; }
    ICON=$(grep -m1 '^Icon=' "$SRC" | cut -d= -f2-)
    ORIG_NAME=$(grep -m1 '^Name=' "$SRC" | cut -d= -f2-)
    [ -z "$NEWNAME" ] && NEWNAME="${ORIG_NAME} Proxy"
    [ -z "$ICON" ] && ICON="$base"
    [ -z "$ORIG_NAME" ] && ORIG_NAME="$base"
    # 启动类型：lipstick 没这行就不调起。no-invoker 的原样保留，其余统一 generic
    #（generic+脚本经 invoker 起，RooTitanium 已验证可行；silica-qt5 不直接沿用，
    # 避免 Qt booster 预加载进 bash 脚本）
    XTYPE=$(grep -m1 '^X-Nemo-Application-Type=' "$SRC" | cut -d= -f2-)
    case "$XTYPE" in
        no-invoker) XNEMO_LINE="X-Nemo-Application-Type=no-invoker" ;;
        *) XNEMO_LINE="X-Nemo-Application-Type=generic" ;;
    esac
    if [ "$MODE" = sys ]; then
        sudo tee "$DST" > /dev/null <<DESK
[Desktop Entry]
Type=Application
$XNEMO_LINE
Name=$NEWNAME
Comment=$ORIG_NAME 复制版，默认代理 $PROXY（经 proxy-run 启动）
Icon=$ICON
Exec=/usr/bin/invoker --type=generic --id=${base}-proxy /usr/bin/proxy-run $PREFIX %U
# 经 proxy-run 统一走代理。故意不写 MimeType 和 X-Maemo-Service，避免和原版抢默认打开方式。
# 代理地址改一处即可：设置 > 连接 > 代理应用，或 ~/.config/proxy-run.conf

[X-Sailjail]
Sandboxing=Disabled
DESK
        sudo chmod 644 "$DST"
    else
        cat > "$DST" <<DESK
[Desktop Entry]
Type=Application
$XNEMO_LINE
Name=$NEWNAME
Comment=$ORIG_NAME 复制版，默认代理 $PROXY（经 proxy-run 启动）
Icon=$ICON
Exec=/usr/bin/invoker --type=generic --id=${base}-proxy /usr/bin/proxy-run $PREFIX %U
# 经 proxy-run 统一走代理。故意不写 MimeType 和 X-Maemo-Service，避免和原版抢默认打开方式。
# 由 proxy-run --sync 按 dconf 名单生成；代理地址在 设置 > 连接 > 代理应用 里改。

[X-Sailjail]
Sandboxing=Disabled
DESK
        chmod 644 "$DST"
    fi
    echo "已生成 $DST"
}

# 克隆图标：proxy-run --from-desktop /usr/share/applications/harbour-xxx.desktop ["新名字"] [--user]
cmd_from_desktop() {
    MODE=sys; SRC=""; NEWNAME=""
    for a in "$@"; do
        case "$a" in
            --user) MODE=user ;;
            *) if [ -z "$SRC" ]; then SRC="$a"; else NEWNAME="$a"; fi ;;
        esac
    done
    [ -z "$SRC" ] && { echo "用法: proxy-run --from-desktop <原.desktop> [新名字] [--user]" >&2; return 1; }
    [ -f "$SRC" ] || { echo "找不到 $SRC" >&2; return 1; }
    if [ "$MODE" = user ]; then
        DESTDIR="$HOME/.local/share/applications"
        mkdir -p "$DESTDIR"
    else
        DESTDIR="/usr/share/applications"
    fi
    echo "原图标: $SRC"
    gen_desktop "$SRC" "$NEWNAME" "$DESTDIR" "$MODE"
    if command -v update-desktop-database >/dev/null 2>&1; then
        if [ "$MODE" = user ]; then
            update-desktop-database "$DESTDIR" 2>&1 || true
        else
            sudo update-desktop-database "$DESTDIR" 2>&1 || true
        fi
    fi
}

# 按 dconf 名单批量生成用户级代理图标
cmd_sync() {
    APPS_RAW="$(_dconf_str /apps/proxy-run/apps)"
    if [ -z "$APPS_RAW" ]; then
        echo "dconf /apps/proxy-run/apps 为空：先去 设置 > 连接 > 代理应用 里添加。" >&2
        return 1
    fi
    USER_APPDIR="$HOME/.local/share/applications"
    mkdir -p "$USER_APPDIR"
    n=0
    _rest="$APPS_RAW;"
    while [ -n "$_rest" ]; do
        src="${_rest%%;*}"
        _rest="${_rest#*;}"
        src="$(echo "$src" | sed 's/^ *//;s/ *$//')"
        [ -z "$src" ] && continue
        if [ ! -f "$src" ]; then echo "跳过（找不到）: $src" >&2; continue; fi
        if gen_desktop "$src" "" "$USER_APPDIR" user; then n=$((n + 1)); fi
    done
    echo "已同步 $n 个代理图标到 $USER_APPDIR"
    if command -v update-desktop-database >/dev/null 2>&1; then
        update-desktop-database "$USER_APPDIR" 2>&1 || true
    fi
}

# 通用 env 导出（所有引擎都要）
apply_env() {
    export http_proxy="$PROXY"
    export https_proxy="$PROXY"
    export HTTP_PROXY="$PROXY"
    export HTTPS_PROXY="$PROXY"
    export all_proxy="$PROXY"
    export ALL_PROXY="$PROXY"
    # 本机直连：FinTube/Youfish 内部 127.0.0.1 媒体代理、GStreamer 回环都不能进外部代理
    # 保留用户已有的 no_proxy，幂等追加
    _extra="localhost,127.0.0.1"
    case ",$no_proxy," in
        *,127.0.0.1,*) ;;
        *) no_proxy="${no_proxy:+$no_proxy,}$_extra" ;;
    esac
    case ",$NO_PROXY," in
        *,127.0.0.1,*) ;;
        *) NO_PROXY="${NO_PROXY:+$NO_PROXY,}$_extra" ;;
    esac
    export no_proxy NO_PROXY
    # 供子脚本/调试用
    export PROXY_URL="$PROXY"
}

# Chromium/QtWebEngine：补 --proxy-server（幂等）
apply_chromium_flags() {
    case "$BASE_FLAGS" in
        *"--proxy-server"*) ;;
        *) BASE_FLAGS="$BASE_FLAGS --proxy-server=$PROXY --proxy-bypass-list=localhost,127.0.0.1" ;;
    esac
    export QTWEBENGINE_CHROMIUM_FLAGS="$BASE_FLAGS"
}

# 已知 Chromium app 的内置默认值（反编译/实测确认）。未知 app 则用环境已有值或最小值。
chromium_defaults_for() {
    case "$1" in
        *harbour-rootitanium*)
            echo "--no-sandbox --disable-gpu-sandbox --use-gl=egl --disable-seccomp-filter-sandbox --touch-events=enabled --blink-settings=availablePointerTypes=2,availableHoverTypes=1,primaryPointerType=2,primaryHoverType=1 --force-device-scale-factor=2.6214 --touch-slop-distance=28 --enable-viewport --disable-features=WebBluetooth,WebUSB,WebNFC,IdleDetection,FedCm,WebOTP --force-webrtc-ip-handling-policy=default_public_interface_only --enable-features=BlockInsecurePrivateNetworkRequests"
            ;;
        *) echo "" ;;
    esac
}

# Gecko：sailfish-browser 独立 profile 写 prefs.js
run_sailfish_browser() {
    PROFILE_BASE="$HOME/.local/share/org.sailfishos/browser/proxy"
    MOZDIR="$PROFILE_BASE/.mozilla"
    PREFS="$MOZDIR/prefs.js"
    DEFAULT_PREFS="$HOME/.local/share/org.sailfishos/browser/.mozilla/prefs.js"
    SYSTEM_PREFS="/usr/share/sailfish-browser/data/prefs.js"
    mkdir -p "$MOZDIR"
    if [ ! -f "$PREFS" ]; then
        if [ -f "$DEFAULT_PREFS" ]; then cp "$DEFAULT_PREFS" "$PREFS"
        elif [ -f "$SYSTEM_PREFS" ]; then cp "$SYSTEM_PREFS" "$PREFS"
        else : > "$PREFS"; fi
    fi
    if [ -f "$PREFS" ]; then
        sed -i '/"network\.proxy\./d' "$PREFS"
        cat >> "$PREFS" <<PREFEOF
user_pref("network.proxy.http", "$PROXY_HOST");
user_pref("network.proxy.http_port", $PROXY_PORT);
user_pref("network.proxy.ssl", "$PROXY_HOST");
user_pref("network.proxy.ssl_port", $PROXY_PORT);
user_pref("network.proxy.share_proxy_settings", true);
user_pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1");
user_pref("network.proxy.type", 1);
user_pref("network.proxy.proxy_over_tls", false);
PREFEOF
    fi
    if pgrep -x sailfish-browser >/dev/null 2>&1; then
        echo "proxy-run: 检测到浏览器正在运行，请先关闭原版再开代理版，否则会跳回原版（直连）。" >&2
    fi
    exec /usr/bin/sailfish-browser -profile "$PROFILE_BASE" "$@"
}

case "$1" in
    --help|-h|"") usage; exit 0 ;;
    --show) cmd_show; exit 0 ;;
    --check) cmd_check; exit 0 ;;
    --sync) cmd_sync; exit $? ;;
    --from-desktop) shift; cmd_from_desktop "$@"; exit $? ;;
esac

BIN="$1"; shift
[ -z "$BIN" ] && { usage >&2; exit 1; }
# 允许短名：proxy-run harbour-fintube
case "$BIN" in
    */*) ;;
    *) command -v "$BIN" >/dev/null 2>&1 && BIN=$(command -v "$BIN") || BIN="/usr/bin/$BIN" ;;
esac

# 拆 invoker 包装：很多原图标 Exec 形如
#   /usr/bin/invoker --type=silica-qt5 --id=xxx /usr/bin/realapp [args...]
# gen_desktop 会整句包进来，这里拆出 invoker 参数 / 真正程序 / 应用参数。
# 非 invoker 启动时：WRAPPER 为空，REALBIN=$BIN，"$@" 不变。
WRAPPER=""
INV_OPTS=""
REALBIN="$BIN"
if [ "$(basename "$BIN")" = "invoker" ]; then
    WRAPPER="$BIN"
    REALBIN=""
    _needval=0
    _rest=""
    for _a in "$@"; do
        if [ $_needval -eq 1 ]; then INV_OPTS="$INV_OPTS '$_a'"; _needval=0; continue; fi
        case "$_a" in
            --type|--id|-t|-i) INV_OPTS="$INV_OPTS '$_a'"; _needval=1 ;;
            -*) INV_OPTS="$INV_OPTS '$_a'" ;;
            *) if [ -z "$REALBIN" ]; then REALBIN="$_a"; else _rest="$_rest '$_a'"; fi ;;
        esac
    done
    if [ -z "$REALBIN" ]; then echo "proxy-run: invoker 后面没找到程序：$*" >&2; exit 1; fi
    eval "set -- $_rest"
fi

apply_env

# 经包装器起（保留原 invoker 参数，环境变量透传进去）；直起就直接 exec
launch_wrapped() {
    if [ -n "$WRAPPER" ]; then
        eval "exec \"$WRAPPER\" $INV_OPTS \"$REALBIN\" \"\$@\""
    else
        exec "$REALBIN" "$@"
    fi
}

case "$REALBIN" in
    *sailfish-browser*)
        # 浏览器一律直起（去 invoker 包装，原 browser-proxy 就是这么跑的），
        # 把 -profile 开关吃掉（调用方可能带了），统一用代理 profile
        _args=""
        _skip=0
        for a in "$@"; do
            if [ $_skip -eq 1 ]; then _skip=0; continue; fi
            case "$a" in
                -profile) _skip=1; continue ;;
                *) _args="$_args '$a'" ;;
            esac
        done
        # 用 eval 保留原参数空格情况的简单处理；参数多为 URL/%U，无空格，够用
        eval "set -- $_args"
        run_sailfish_browser "$@"
        ;;
    *rootitanium*|*qtwebengine*|*chromium*)
        if [ -n "$QTWEBENGINE_CHROMIUM_FLAGS" ]; then
            BASE_FLAGS="$QTWEBENGINE_CHROMIUM_FLAGS"
        else
            BASE_FLAGS=$(chromium_defaults_for "$REALBIN")
        fi
        apply_chromium_flags
        launch_wrapped "$@"
        ;;
    *)
        # 通用：再顺手照顾一下“已经导出了 QTWEBENGINE 变量的 QtWebEngine 程序”
        if [ -n "$QTWEBENGINE_CHROMIUM_FLAGS" ]; then
            BASE_FLAGS="$QTWEBENGINE_CHROMIUM_FLAGS"
            apply_chromium_flags
        fi
        launch_wrapped "$@"
        ;;
esac
