#!/bin/bash
CONFIG="/etc/sing-box/config.json"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

# 1. 自动检查并安装依赖
MISSING_PKGS=""
for pkg in qrencode jq openssl curl python3 python3-cryptography base64; do
  command -v $pkg &>/dev/null || dpkg -s $pkg &>/dev/null || MISSING_PKGS="$MISSING_PKGS $pkg"
done
if [ -n "$MISSING_PKGS" ]; then
  echo -e "${YELLOW}正在安装缺失的依赖: $MISSING_PKGS${NC}"
  apt-get update -qq && apt-get install -y $MISSING_PKGS coreutils &>/dev/null
fi

[ -f "$CONFIG" ] || { echo -e "${RED}错误：找不到配置文件 $CONFIG${NC}"; exit 1; }

# 2. 清理 JSON 单行注释（逐字符扫描，避开双引号字符串内的 //，如 https://）
CLEAN_JSON=$(python3 - "$CONFIG" << 'PYEOF_INNER'
import sys
text = open(sys.argv[1], encoding='utf-8').read()
out = []
i, n = 0, len(text)
in_str = False
while i < n:
    c = text[i]
    if in_str:
        out.append(c)
        if c == '\\' and i + 1 < n:
            out.append(text[i + 1])
            i += 2
            continue
        elif c == '"':
            in_str = False
    else:
        if c == '"':
            in_str = True
            out.append(c)
        elif c == '/' and i + 1 < n and text[i + 1] == '/':
            while i < n and text[i] != '\n':
                i += 1
            continue
        else:
            out.append(c)
    i += 1
print(''.join(out))
PYEOF_INNER
)

# 3. 获取服务器 IP
SERVER_IP=$(curl -s4 --max-time 5 ifconfig.me || curl -s4 --max-time 5 ip.sb)

echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}     Sing-box 终极节点自动导出工具      ${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""

# X25519 公钥推导函数 (REALITY 必备)
derive_pubkey() {
  python3 - "$1" << 'PYEOF'
import sys, base64
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
try:
    priv_b64 = sys.argv[1].replace('-', '+').replace('_', '/')
    priv_b64 += '=' * (-len(priv_b64) % 4)
    priv_key = X25519PrivateKey.from_private_bytes(base64.b64decode(priv_b64))
    pub_bytes = priv_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
    print(base64.urlsafe_b64encode(pub_bytes).rstrip(b'=').decode())
except Exception:
    pass
PYEOF
}

# 核心安全组件：urlencode 函数（修复密码含 @ # ? 等符号破坏 URI 的问题）
urlencode() {
  local LC_ALL=C
  for (( i=0; i<${#1}; i++ )); do
    local c="${1:i:1}"
    case "$c" in
      [a-zA-Z0-9.~_-]) printf "$c" ;;
      *) printf '%%%02X' "'$c" ;;
    esac
  done
}

# 遍历所有入站节点。users 里有多个人的入站会被拆成「一人一条」：
#   2 个用户 → 输出 2 条记录，tag 后面各自补上 name，users 只留他自己那一个
#   只有 1 个用户的入站原样通过，tag 不变（和以前的行为完全一致）
echo "$CLEAN_JSON" | jq -c '.inbounds[]?
  | if (.users? | type) == "array" and (.users | length) > 1
    then (.users | to_entries[]) as $e
         | (.users = [$e.value]
            | .tag = "\(.tag)-\($e.value.name // "user\($e.key + 1)")")
    else .
    end' | while read -r node; do
  TYPE=$(echo "$node" | jq -r '.type // empty')
  TAG=$(echo "$node" | jq -r '.tag // "Unknown-Node"')
  PORT=$(echo "$node" | jq -r '.listen_port // empty')
  
  [ -z "$TYPE" ] && continue

  # ================= 1. VLESS =================
  if [ "$TYPE" == "vless" ]; then
    UUID=$(echo "$node" | jq -r '.users[0].uuid // empty')
    ENC_UUID=$(urlencode "$UUID")
    FLOW=$(echo "$node" | jq -r '.users[0].flow // empty')
    TRANSPORT=$(echo "$node" | jq -r '.transport.type // "tcp"')
    HAS_TLS=$(echo "$node" | jq -r '.tls.enabled // "false"')
    HAS_REALITY=$(echo "$node" | jq -r '.tls.reality.enabled // "false"')
    SERVER_NAME=$(echo "$node" | jq -r '.tls.server_name // empty')
    FP=$(echo "$node" | jq -r '.tls.utls.fingerprint // "chrome"')

    QUERY="type=${TRANSPORT}&encryption=none"

    if [ "$HAS_REALITY" == "true" ]; then
      PRIVKEY=$(echo "$node" | jq -r '.tls.reality.private_key // empty')
      PBK=$(derive_pubkey "$PRIVKEY")
      SNI=$(echo "$node" | jq -r '.tls.reality.handshake.server // empty')
      SID=$(echo "$node" | jq -r '.tls.reality.short_id[0] // empty')
      
      [ -z "$PBK" ] && { echo -e "${RED}[跳过] $TAG (VLESS)：public key 推导失败${NC}"; continue; }
      QUERY="${QUERY}&security=reality&pbk=${PBK}&sni=${SNI}&sid=${SID}&fp=${FP}"
      [ "$FLOW" != "null" ] && [ -n "$FLOW" ] && QUERY="${QUERY}&flow=${FLOW}"
      HOST_ADDR=$SERVER_IP

    elif [ "$HAS_TLS" == "true" ]; then
      QUERY="${QUERY}&security=tls&sni=${SERVER_NAME}&fp=${FP}"
      [ "$FLOW" != "null" ] && [ -n "$FLOW" ] && QUERY="${QUERY}&flow=${FLOW}"
      HOST_ADDR=${SERVER_NAME:-$SERVER_IP}
    else
      QUERY="${QUERY}&security=none"
      HOST_ADDR=$SERVER_IP
    fi

    if [ "$TRANSPORT" == "ws" ]; then
      WS_PATH=$(echo "$node" | jq -r '.transport.path // "/"')
      WS_HOST=$(echo "$node" | jq -r '.transport.headers.Host // empty')
      QUERY="${QUERY}&path=$(urlencode "$WS_PATH")"
      [ "$WS_HOST" != "null" ] && [ -n "$WS_HOST" ] && QUERY="${QUERY}&host=${WS_HOST}"
    elif [ "$TRANSPORT" == "grpc" ]; then
      SVC_NAME=$(echo "$node" | jq -r '.transport.service_name // empty')
      QUERY="${QUERY}&serviceName=$(urlencode "$SVC_NAME")"
    fi

    LINK="vless://${ENC_UUID}@${HOST_ADDR}:${PORT}?${QUERY}#$(urlencode "$TAG")"
    echo -e "${GREEN}[VLESS] $TAG${NC} (传输: $TRANSPORT)"

  # ================= 2. Hysteria2 =================
  elif [ "$TYPE" == "hysteria2" ]; then
    PASS=$(echo "$node" | jq -r '.users[0].password // empty')
    ENC_PASS=$(urlencode "$PASS")
    UP=$(echo "$node" | jq -r '.up_mbps // empty')
    DOWN=$(echo "$node" | jq -r '.down_mbps // empty')
    
    OBFS_TYPE=$(echo "$node" | jq -r '.obfs?.type // empty')
    OBFS_PASS=$(echo "$node" | jq -r '.obfs?.password // empty')
    ENC_OBFS_PASS=$(urlencode "$OBFS_PASS")
    
    NODE_SNI=$(echo "$node" | jq -r '.tls.server_name // empty')
    if [ -z "$NODE_SNI" ] || [ "$NODE_SNI" == "null" ]; then
      CERT=$(echo "$node" | jq -r '.tls.certificate_path // empty')
      if [ -n "$CERT" ] && [ "$CERT" != "null" ] && [ -f "$CERT" ]; then
        NODE_SNI=$(openssl x509 -in "$CERT" -noout -subject 2>/dev/null | sed 's/.*CN\s*=\s*//' | tr -d ' ')
      fi
    fi

    QUERY=""
    [ -n "$NODE_SNI" ] && [ "$NODE_SNI" != "null" ] && QUERY="sni=${NODE_SNI}&insecure=0" || QUERY="insecure=1"
    [ -n "$UP" ] && [ "$UP" != "null" ] && [ "$UP" != "0" ] && QUERY="${QUERY}&up=${UP}"
    [ -n "$DOWN" ] && [ "$DOWN" != "null" ] && [ "$DOWN" != "0" ] && QUERY="${QUERY}&down=${DOWN}"
    
    if [ -n "$OBFS_TYPE" ] && [ "$OBFS_TYPE" != "null" ]; then
      QUERY="${QUERY}&obfs=${OBFS_TYPE}&obfs-password=${ENC_OBFS_PASS}"
    fi

    HOST_ADDR=${NODE_SNI:-$SERVER_IP}
    [ -z "$NODE_SNI" ] || [ "$NODE_SNI" == "null" ] && HOST_ADDR=$SERVER_IP

    LINK="hy2://${ENC_PASS}@${HOST_ADDR}:${PORT}?${QUERY}#$(urlencode "$TAG")"
    echo -e "${GREEN}[Hysteria2] $TAG${NC}"
    [ -n "$OBFS_TYPE" ] && [ "$OBFS_TYPE" != "null" ] && echo -e "  包含混淆: $OBFS_TYPE"

  # ================= 3. Trojan =================
  elif [ "$TYPE" == "trojan" ]; then
    PASS=$(echo "$node" | jq -r '.users[0].password // empty')
    ENC_PASS=$(urlencode "$PASS")
    SNI=$(echo "$node" | jq -r '.tls.server_name // empty')
    [ -z "$SNI" ] || [ "$SNI" == "null" ] && SNI="$SERVER_IP"
    TRANSPORT=$(echo "$node" | jq -r '.transport.type // "tcp"')

    QUERY="sni=${SNI}"
    
    if [ "$TRANSPORT" == "ws" ]; then
       WS_PATH=$(echo "$node" | jq -r '.transport.path // "/"')
       WS_HOST=$(echo "$node" | jq -r '.transport.headers.Host // empty')
       QUERY="${QUERY}&type=ws&path=$(urlencode "$WS_PATH")"
       [ "$WS_HOST" != "null" ] && [ -n "$WS_HOST" ] && QUERY="${QUERY}&host=${WS_HOST}"
    elif [ "$TRANSPORT" == "grpc" ]; then
       SVC_NAME=$(echo "$node" | jq -r '.transport.service_name // empty')
       QUERY="${QUERY}&type=grpc&serviceName=$(urlencode "$SVC_NAME")"
    fi

    LINK="trojan://${ENC_PASS}@${SNI}:${PORT}?${QUERY}#$(urlencode "$TAG")"
    echo -e "${GREEN}[Trojan] $TAG${NC} (传输: $TRANSPORT)"

  # ================= 4. Shadowsocks =================
  elif [ "$TYPE" == "shadowsocks" ]; then
    METHOD=$(echo "$node" | jq -r '.method // empty')
    # 2022 系列多用户时，客户端密码是「服务端 PSK:用户 PSK」两段拼起来
    PASS=$(echo "$node" | jq -r 'if (.users? | type) == "array" and (.users | length) > 0
      then "\(.password):\(.users[0].password)" else .password end // empty')
    
    B64_CRED=$(echo -n "${METHOD}:${PASS}" | base64 | tr -d '\n\r' | tr '+/' '-_' | tr -d '=')
    LINK="ss://${B64_CRED}@${SERVER_IP}:${PORT}#$(urlencode "$TAG")"
    echo -e "${GREEN}[Shadowsocks] $TAG${NC}"

  # ================= 5. TUIC v5 =================
  elif [ "$TYPE" == "tuic" ]; then
    UUID=$(echo "$node" | jq -r '.users[0].uuid // empty')
    PASS=$(echo "$node" | jq -r '.users[0].password // empty')
    ENC_UUID=$(urlencode "$UUID")
    ENC_PASS=$(urlencode "$PASS")
    CC=$(echo "$node" | jq -r '.congestion_control // "bbr"')
    ALPN=$(echo "$node" | jq -r 'if (.tls.alpn|type)=="array" then (.tls.alpn|join(",")) else (.tls.alpn // "h3") end')
    NODE_SNI=$(echo "$node" | jq -r '.tls.server_name // empty')
    if [ -z "$NODE_SNI" ] || [ "$NODE_SNI" == "null" ]; then
      CERT=$(echo "$node" | jq -r '.tls.certificate_path // empty')
      if [ -n "$CERT" ] && [ "$CERT" != "null" ] && [ -f "$CERT" ]; then
        NODE_SNI=$(openssl x509 -in "$CERT" -noout -subject 2>/dev/null | sed 's/.*CN\s*=\s*//' | tr -d ' ')
      fi
    fi
    if [ -n "$NODE_SNI" ] && [ "$NODE_SNI" != "null" ]; then
      QUERY="congestion_control=${CC}&alpn=$(urlencode "$ALPN")&sni=${NODE_SNI}&udp_relay_mode=native"
      HOST_ADDR=$NODE_SNI
    else
      QUERY="congestion_control=${CC}&alpn=$(urlencode "$ALPN")&allow_insecure=1&udp_relay_mode=native"
      HOST_ADDR=$SERVER_IP
    fi
    LINK="tuic://${ENC_UUID}:${ENC_PASS}@${HOST_ADDR}:${PORT}?${QUERY}#$(urlencode "$TAG")"
    echo -e "${GREEN}[TUIC v5] $TAG${NC}"

  # ================= 6. AnyTLS =================
  elif [ "$TYPE" == "anytls" ]; then
    PASS=$(echo "$node" | jq -r '.users[0].password // empty')
    ENC_PASS=$(urlencode "$PASS")
    SNI=$(echo "$node" | jq -r '.tls.server_name // empty')
    FP=$(echo "$node" | jq -r '.tls.utls.fingerprint // "chrome"')
    HOST_ADDR=${SNI:-$SERVER_IP}
    if [ -n "$SNI" ] && [ "$SNI" != "null" ]; then
      QUERY="sni=${SNI}&insecure=0&fp=${FP}"
    else
      QUERY="insecure=1&fp=${FP}"
    fi
    LINK="anytls://${ENC_PASS}@${HOST_ADDR}:${PORT}?${QUERY}#$(urlencode "$TAG")"
    echo -e "${GREEN}[AnyTLS] $TAG${NC}"

  else
    echo -e "${YELLOW}[忽略] $TAG：暂不支持 '$TYPE' 协议生成分享链接。${NC}"
    echo ""
    continue
  fi

  # 输出链接与二维码
  echo "$LINK"
  echo -e "${CYAN}扫码:${NC}"
  qrencode -t ansiutf8 "$LINK"
  echo ""

done

echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}            节点导出完毕                ${NC}"
echo -e "${CYAN}========================================${NC}"