#!/bin/sh /etc/rc.common

. /usr/lib/hijpass/app-logger.sh

USE_PROCD=1
START=99
STOP=15

NAME=hijpass

LIB_PATH="/usr/lib/$NAME"
NFT_SCRIPT="$LIB_PATH/nft.sh"
ROUTE_SCRIPT="$LIB_PATH/route.sh"
RULE_UPDATE_SCRIPT="$LIB_PATH/rules.sh"
CHINANDS_NG_SCRIPT="$LIB_PATH/chinadns-ng.sh"
GEOVIEW_SCRIPT="$LIB_PATH/geoview.sh"

CONF_PATH="/etc/$NAME"
SHUNT_CONF="$CONF_PATH/shunt.json"

# Global
ENABLED=$(uci -q get hijpass.@hijpass[0].enabled || :)
DELAY_TIME=$(uci -q get hijpass.@hijpass[0].delay_time || :)
CLIENT_DIR=$(uci -q get hijpass.@hijpass[0].client_dir || :)

# RuleDTO
CRON_CONF=$(uci -q get hijpass.@rule[0].update_schedule || :)

# Log
REDIRECT_LOG=

# DNS
DNS_SERVICE=$(uci -q get hijpass.@dns[0].dns_service || :)
DNS_HOOK=$(uci -q get hijpass.@hijpass[0].dns_hook || :)
CHINADNS_NG_CONF="$CONF_PATH/chinadns-ng.conf"

start_service() {
  [ "$ENABLED" != "1" ] && return
  # 启动进程
  config_load $NAME
  log_info "===================== Starting service ======================="
  log_info "Running proxy nodes..."
  config_foreach run_regular_proxy proxy_node
  sleep 2
  log_info "Running load balancing proxy nodes..."
  config_foreach run_load_balance_proxy proxy_node
  sleep 1
  log_info "Running shunt..."
  config_foreach run_shunt shunt
  sleep 1
  # 配置防火墙
  log_info "Configuring firewall rules..."
  reset_fw_tproxy
  # 启动定时任务
  log_info "Resetting geo cron jobs..."
  reset_geo_cron
  # 启动DNS劫持
  log_info "DNS hijacking..."
  start_dns_service
  log_info "================ Service started successfully ================"
}

stop_service() {
  log_info "===================== Stopping service ======================="
  reset_geo_cron
  remove_fw_tproxy
  stop_dns_service
  log_info "================ Service stopped successfully ================"
}

service_triggers() {
  procd_add_reload_trigger "$NAME"
  procd_add_interface_trigger "interface.*.up" "*" /etc/init.d/$NAME restart
}

reload_service() {
  /etc/init.d/$NAME restart
}

boot() {
  [ "$DELAY_TIME" -gt 0 ] && sleep "$DELAY_TIME"
  restart
}

run_regular_proxy() {
  local type
  config_get type "$1" type
  [ "$type" = "load_balance" ] && return
  run_proxy "$1"
}

run_load_balance_proxy() {
  local type
  config_get type "$1" type
  [ "$type" != "load_balance" ] && return
  run_proxy "$1"
}

run_proxy() {
  local command log_path name enabled nest_proxy core
  local enable_std_log="1"
  config_get enabled "$1" enabled
  [ "$enabled" != "1" ] && return
  config_get command "$1" command
  config_get log_path "$1" log_path
  config_get name "$1" name
  config_get nest_proxy "$1" nest_proxy
  config_get core "$1" core

  local conf_path="$CLIENT_DIR/$name-$1.json"

  case "$core" in
    xray)    command="xray -c $conf_path" ;;
    sing-box) command="sing-box -c $conf_path run" ;;
    custom)  command=$(echo "$command" | sed "s|{conf_path}|$conf_path|g") ;;
    *)       log_warn "Node '$name' has unknown core='$core', skipping"; return ;;
  esac

  if [ -n "$log_path" ]; then
    : > "$log_path"
    command="$command >> $log_path 2>&1"
    enable_std_log="0"
    REDIRECT_LOG="1"
  else
    command="$command > /dev/null 2>&1"
  fi
  open_instance "$1" "$name" "$command" "$enable_std_log" "$nest_proxy"
}

run_shunt() {
  local type log_path enabled ruleset_convert
  config_get enabled "$1" enabled
  [ "$enabled" = "0" ] && log_info "Shunt disabled, skipping" && return
  config_get type "$1" type
  [ -z "$type" ] && log_error "Shunt type not set" && return
  config_get log_path "$1" log_path
  config_get ruleset_convert "$1" ruleset_convert

  local shunt_command="$type -c $SHUNT_CONF"
  [ "$type" = "sing-box" ] && shunt_command="$type run -c $SHUNT_CONF"
  [ "$type" = "sing-box" ] && [ "$ruleset_convert" = "1" ] && $GEOVIEW_SCRIPT -t

  local enable_std_log="0"
  if [ -n "$log_path" ]; then
    : > "$log_path"
    shunt_command="$shunt_command >> $log_path 2>&1"
    REDIRECT_LOG="1"
  else
    shunt_command="$shunt_command > /dev/null 2>&1"
  fi
  open_instance "$1" "shunt" "$shunt_command" "$enable_std_log"
}

open_instance() {
  log_debug "id: $1, name: $2, cmd: $3"
  local instance_name="$1-$2"
  local cmd="$3"
  local enable_std_log="$4"
  local nest_proxy="$5"
  procd_open_instance "$instance_name"
  procd_set_param command /bin/sh -c "$cmd"
  if [ "$2" = "shunt" ]; then
    procd_set_param limits core="unlimited"
    procd_set_param limits nofile="1000000 1000000"
    procd_set_param env "XRAY_LOCATION_ASSET=/usr/share/v2ray"
  fi
  config_list_foreach "$1" procd_env procd_set_env "$instance_name"
  if [ "$enable_std_log" = "1" ]; then
    procd_set_param stdout 1
    procd_set_param stderr 1
  fi
  procd_set_param pidfile "/var/run/$instance_name.pid"
  if [ "$nest_proxy" = "1" ]; then
    procd_set_param user "root"
  else
    procd_set_param user "hijpass"
  fi
  procd_set_param respawn 10 5 2
  procd_close_instance
}

procd_set_env() {
  [ -z "$1" ] && return
  log_debug "Setting $2 env: $1"
  procd_set_param env "$1"
}

reset_fw_tproxy() {
  if ! $NFT_SCRIPT reset > /dev/null 2>&1; then
    stop
    exit 1
  fi
  $ROUTE_SCRIPT reset_hijpass_route
}

remove_fw_tproxy() {
  $ROUTE_SCRIPT remove_hijpass_route
  $NFT_SCRIPT remove > /dev/null 2>&1
}

start_dns_service() {
  if [ "$DNS_SERVICE" = "chinadns-ng" ]; then
    log_info "Starting chinadns-ng service"
    $CHINANDS_NG_SCRIPT
    local cid
    cid="$(uci show hijpass.@dns[0] | grep 'dns$' | awk -F '[.=]' '{print $2}')"
    open_instance "$cid" "chinadns-ng" "$DNS_SERVICE -C $CHINADNS_NG_CONF"
  fi
  if [ -n "$DNS_HOOK" ]; then
    [ ! -f "$DNS_HOOK" ] && log_warn "Custom DNS hook script $DNS_HOOK not found" && return 1
    log_info "Executing custom DNS hook script (start)"
    $DNS_HOOK start || log_error "Custom DNS hook script execute failed"
  fi
}

stop_dns_service() {
  if [ "$DNS_SERVICE" = "chinadns-ng" ]; then
    rm -f "$CHINADNS_NG_CONF" && log_info "Deleted chinadns-ng conf"
  fi
  if [ -n "$DNS_HOOK" ]; then
    [ ! -f "$DNS_HOOK" ] && log_warn "Custom DNS hook script $DNS_HOOK not found" && return 1
    log_info "Executing custom DNS hook script (stop)"
    $DNS_HOOK stop || log_warn "Custom DNS hook script execute failed"
  fi
}

reset_geo_cron() {
  sed -i '/hijpass\/rules\.sh/d' /etc/crontabs/root 2>/dev/null
  sed -i '/hijpass\/clean-service-log\.sh hijpass/d' /etc/crontabs/root 2>/dev/null
  if [ "$REDIRECT_LOG" = "1" ]; then
    echo "*/10 * * * * /usr/lib/hijpass/clean-service-log.sh hijpass" >> /etc/crontabs/root
    log_info "Added log split service cron job: */10 * * * * /usr/lib/hijpass/clean-service-log.sh hijpass"
  fi
  if [ -z "$CRON_CONF" ] || [ "$ENABLED" != "1" ]; then
    /etc/init.d/cron restart > /dev/null 2>&1
    return 0
  fi

  echo "$CRON_CONF $RULE_UPDATE_SCRIPT update" >> /etc/crontabs/root
  log_info "Added rule update cron job: $CRON_CONF $RULE_UPDATE_SCRIPT update"

  /etc/init.d/cron restart > /dev/null 2>&1
}
