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

START=99
USE_PROCD=1

script=$(readlink "$initscript")
NAME="$(basename ${script:-$initscript})"
config_load "$NAME"

EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config"
EXTRA_HELP="        list_update     Updating domain and subnet lists
        check_proxy          Check if sing-box proxy works correctly
        check_nft           Show PodkopTable nftables rules
        check_github        Check GitHub connectivity and lists availability
        check_logs         Show podkop logs from system journal
        check_sing_box_connections Show active sing-box network connections
        check_sing_box_logs Show recent sing-box logs
        check_dnsmasq       Show current DNSMasq configuration
        show_config         Show current configuration with masked sensitive data
        show_version        Show current version
        show_sing_box_config Show current sing-box configuration"

[ ! -L /usr/sbin/podkop ] && ln -s /etc/init.d/podkop /usr/sbin/podkop

GITHUB_RAW_URL="https://raw.githubusercontent.com/itdoginfo/allow-domains/main"
SRS_MAIN_URL="https://github.com/itdoginfo/allow-domains/releases/latest/download"
DOMAINS_RU_INSIDE="${GITHUB_RAW_URL}/Russia/inside-dnsmasq-nfset.lst"
DOMAINS_RU_OUTSIDE="${GITHUB_RAW_URL}/Russia/outside-dnsmasq-nfset.lst" 
DOMAINS_UA="${GITHUB_RAW_URL}/Ukraine/inside-dnsmasq-nfset.lst"
DOMAINS_YOUTUBE="${GITHUB_RAW_URL}/Services/youtube.lst"
SUBNETS_TWITTER="${GITHUB_RAW_URL}/Subnets/IPv4/twitter.lst"
SUBNETS_META="${GITHUB_RAW_URL}/Subnets/IPv4/meta.lst"
SUBNETS_DISCORD="${GITHUB_RAW_URL}/Subnets/IPv4/discord.lst"
SUBNETS_TELERAM="${GITHUB_RAW_URL}/Subnets/IPv4/telegram.lst"
SING_BOX_CONFIG="/etc/sing-box/config.json"
CACHE_FILE_PATH="/tmp/cache.db"
FAKEIP="198.18.0.0/15"
VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram"

start_service() {
    log "Start podkop"

    sing_box_version=$(sing-box version | head -n 1 | awk '{print $3}')
    required_version="1.11.1"

    if [ "$(echo -e "$sing_box_version\n$required_version" | sort -V | head -n 1)" != "$required_version" ]; then
        echo "The version of sing-box ($sing_box_version) is lower than the minimum version. Update sing-box: opkg update && opkg remove sing-box && opkg install sing-box"
        exit 1
    fi

    if opkg list-installed | grep -qE "iptables|kmod-iptab"; then
        printf "\033[31;1mFound incompatible iptables packages. If you're using FriendlyWrt: https://t.me/itdogchat/44512/181082\033[0m\n"
    fi

    if ! ip addr | grep -q "br-lan"; then
        log "Interface br-lan not found"
        exit 1
    fi
    
    migration

    config_foreach process_validate_service

	procd_open_instance
	procd_set_param command /bin/sh -c "/etc/init.d/podkop main &"
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance
}

stop_service() {
    log "Stopping the podkop"
    remove_cron_job
    dnsmasq_rm

    rm -rf /tmp/podkop/*

    log "Flush nft"
    if nft list table inet PodkopTable >/dev/null 2>&1; then
        nft delete table inet PodkopTable
    fi

    log "Flush ip rule"
    if ip rule list | grep -q "podkop"; then
        ip rule del fwmark 0x105 table podkop priority 105
    fi

    log "Flush ip route"
    if ip route list table podkop >/dev/null 2>&1; then
        ip route flush table podkop
    fi

    log "Stop sing-box"
    /etc/init.d/sing-box stop
    /etc/init.d/sing-box disable

}

restart_service() {
    stop
    start
}

reload_service() {
    stop
    start
}

service_triggers() {
    log "service_triggers start"
    procd_add_config_trigger "config.change" "$NAME" "$initscript" reload 'on_config_change'
}

log() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local CYAN="\033[0;36m"
    local GREEN="\033[0;32m"
    local RESET="\033[0m"

    echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}"
    logger -t "podkop" "$timestamp $message"
}

nolog() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local CYAN="\033[0;36m"
    local GREEN="\033[0;32m"
    local RESET="\033[0m"

    echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}"
}

main() {
    sleep 5

    config_foreach wget_github

    mkdir -p /tmp/podkop
  
    # base
    route_table_rule_mark
    create_nft_table
    sing_box_uci

    # sing-box
    sing_box_inbound_proxy 1602
    sing_box_dns
    sing_box_dns_rule_fakeip
    sing_box_rule_dns
    sing_box_cache_file
    process_socks5

    # sing-box outbounds and rules
    config_foreach sing_box_outdound
    config_foreach process_domains_for_section
    config_foreach process_remote_ruleset
    config_foreach sing_box_rule_preset
    config_foreach process_domains_list_local
    config_foreach process_domains_list_url
    config_foreach process_subnet_for_section
    config_foreach process_subnet_for_section_remote
    config_foreach process_all_traffic_for_section
    config_foreach add_cron_job

    # Future: exclude at the fakeip?
    config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" "0"
    if [ "$exclude_from_ip_enabled" -eq 1 ]; then
        log "Adding an IP for exclusion"
        config_list_foreach main exclude_traffic_ip sing_box_rules_source_ip_cidr $exclude_traffic_ip direct-out
    fi

    config_get_bool yacd "main" "yacd" "0"
    if [ "$yacd" -eq 1 ]; then
        log "Yacd enable"
        jq '.experimental.clash_api = {
            "external_ui": "ui",
            "external_controller": "0.0.0.0:9090"
        }' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
    fi

    config_get_bool exclude_ntp "main" "exclude_ntp" "0"
    if [ "$exclude_ntp" -eq 1 ]; then
        log "NTP traffic exclude for proxy"
        nft insert rule inet PodkopTable mangle udp dport 123 return
    fi

    config_get_bool quic_disable "main" "quic_disable" "0"
    if [ "$quic_disable" -eq 1 ]; then
        log "Rule for disable QUIC"
        sing_box_quic_reject
    fi

    sing_box_config_check
    /etc/init.d/sing-box restart
    /etc/init.d/sing-box enable

    config_get proxy_string "main" "proxy_string"
    config_get interface "main" "interface"

    if [ -n "$proxy_string" ] || [ -n "$interface" ]; then
        dnsmasq_add
    fi
}

# Migrations and validation funcs
migration() {
    # list migrate
    local CONFIG="/etc/config/podkop"

    if grep -q "ru_inside" $CONFIG; then
        log "Depricated list found: ru_inside"
        sed -i '/ru_inside/d' $CONFIG
    fi

    if grep -q "list domain_list 'ru_outside'" $CONFIG; then
        log "Depricated list found: sru_outside"
        sed -i '/ru_outside/d' $CONFIG
    fi

    if grep -q "list domain_list 'ua'" $CONFIG; then
        log "Depricated list found: ua"
        sed -i '/ua/d' $CONFIG
    fi

    # Subnet list
    if grep -q "list subnets" $CONFIG; then
        log "Depricated second section found"
        sed -i '/list subnets/d' $CONFIG
    fi 

    # second remove
    if grep -q "config second 'second'" $CONFIG; then
        log "Depricated second section found"
        sed -i '/second/d' $CONFIG
    fi

    # cron update
    if grep -qE "^\s*option update_interval '[0-9*/,-]+( [0-9*/,-]+){4}'" $CONFIG; then
        log "Depricated update_interval"
        sed -i "s|^\(\s*option update_interval\) '[0-9*/,-]\+\( [0-9*/,-]\+\)\{4\}'|\1 '1d'|" $CONFIG
    fi

    # dnsmasq https
    if grep -q "^filter-rr=HTTPS" "/etc/dnsmasq.conf"; then
        log "Found and removed filter-rr=HTTPS in dnsmasq config"
        sed -i '/^filter-rr=HTTPS/d' "/etc/dnsmasq.conf"
    fi
}

validate_service() {
    local domain="$1"
    
    for valid_service in $VALID_SERVICES; do
        if [ "$domain" = "$valid_service" ]; then
            return 0
        fi
    done

    log "Invalid service in domain_list: $domain. Exiting. Check config and LuCI cache"
    exit 1
}

process_validate_service() {
    config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
    if [ "$domain_list_enabled" -eq 1 ]; then
        config_list_foreach "$section" domain_list validate_service
    fi
}

# Main funcs

route_table_rule_mark() {
    local table=podkop

    grep -q "105 $table" /etc/iproute2/rt_tables || echo "105 $table" >>/etc/iproute2/rt_tables

    if ! ip route list table $table | grep -q "local default dev lo scope host"; then
        log "Added route for tproxy"
        ip route add local 0.0.0.0/0 dev lo table $table
    else
        log "Route for tproxy exists"
    fi

    if ! ip rule list | grep -q "from all fwmark 0x105 lookup $table"; then
        log "Create marking rule"
        ip -4 rule add fwmark 0x105 table $table priority 105
    else
        log "Marking rule exist"
    fi
}

create_nft_table() {
    local table="PodkopTable"

    nft add table inet $table
    log "Create nft rules"
    nft add chain inet $table mangle { type filter hook prerouting priority -150 \; policy accept \;}
    nft add chain inet $table proxy { type filter hook prerouting priority -100 \; policy accept \;}

    nft add set inet $table podkop_subnets { type ipv4_addr\; flags interval\; auto-merge\; }

    nft add rule inet $table mangle iifname "br-lan" ip daddr @podkop_subnets meta l4proto tcp meta mark set 0x105 counter
    nft add rule inet $table mangle iifname "br-lan" ip daddr @podkop_subnets meta l4proto udp meta mark set 0x105 counter
    nft add rule inet $table mangle iifname "br-lan" ip daddr "$FAKEIP" meta l4proto tcp meta mark set 0x105 counter
    nft add rule inet $table mangle iifname "br-lan" ip daddr "$FAKEIP" meta l4proto udp meta mark set 0x105 counter

    nft add rule inet $table proxy meta mark 0x105 meta l4proto tcp tproxy ip to :1602 counter
    nft add rule inet $table proxy meta mark 0x105 meta l4proto udp tproxy ip to :1602 counter
}

dnsmasq_add() {
    ## Future: Check config and skip restart
    log "Configure dnsmasq for sing-box"
    uci set dhcp.@dnsmasq[0].noresolv="1"
    uci set dhcp.@dnsmasq[0].filter_aaaa="1"
    uci set dhcp.@dnsmasq[0].cachesize="0"
    uci -q delete dhcp.@dnsmasq[0].server
    uci add_list dhcp.@dnsmasq[0].server="127.0.0.42"
    uci add_list dhcp.@dnsmasq[0].server='/use-application-dns.net/'
    uci commit dhcp

    /etc/init.d/dnsmasq restart
}

dnsmasq_rm() {
    log "Removing configuration for dnsmasq"
    uci set dhcp.@dnsmasq[0].noresolv="0"
    uci set dhcp.@dnsmasq[0].filter_aaaa="0"
    uci set dhcp.@dnsmasq[0].cachesize="1000"
    uci -q delete dhcp.@dnsmasq[0].server
    uci add_list dhcp.@dnsmasq[0].server="8.8.8.8"
    uci commit dhcp

    /etc/init.d/dnsmasq restart
}

process_domains_text() {
    local text="$1"
    local name="$2"
    
    local tmp_file=$(mktemp)
    echo "$text" > "$tmp_file"
    
    sed 's/[, ]\+/\n/g' "$tmp_file" | while IFS= read -r domain; do
        domain=$(echo "$domain" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        if [ -n "$domain" ]; then
            sing_box_ruleset_domains "$domain" "$name"
        fi
    done
    
    rm -f "$tmp_file"
}

process_subnets_text() {
    local text="$1"
    local name="$2"
    
    local tmp_file=$(mktemp)
    echo "$text" > "$tmp_file"
    
    sed 's/[, ]\+/\n/g' "$tmp_file" | while IFS= read -r subnet; do
        subnet=$(echo "$subnet" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        if [ -n "$subnet" ]; then
            if ! echo "$subnet" | grep -q "/"; then
                subnet="$subnet/32"
            fi
            sing_box_ruleset_subnets "$subnet" "$name"
        fi
    done
    
    rm -f "$tmp_file"
}

wget_github() {
    local count_nslookup=0
    local count_curl=0

    config_get domain_list_enabled "$section" "domain_list_enabled"
    config_get subnets_list_enabled "$section" "subnets_list_enabled"
    config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
    config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled"

    if [ "$domain_list_enabled" -eq 1 ] || [ "$subnets_list_enabled" -eq 1 ] ||
        [ "$custom_download_domains_list_enabled" -eq 1 ] || [ "$custom_download_subnets_list_enabled" -eq 1 ] ; then

        while true; do
            if ! nslookup google.com >/dev/null 2>&1; then
                log "DNS not working. Retrying... [$count_nslookup sec]"
                count_nslookup=$((count_nslookup + 1))
            else
                return
            fi

            if [ $count_nslookup -lt 30 ]; then
                sleep_interval=1
            elif [ $count_nslookup -ge 30 ] && [ $count_nslookup -lt 60 ]; then
                sleep_interval=5
            elif [ $count_nslookup -ge 60 ] && [ $count_nslookup -lt 90 ]; then
                sleep_interval=10
            else
                sleep_interval=30
            fi

            sleep $sleep_interval
        done

        while true; do
            if ! curl -m 3 github.com; then
                log "GitHub is not available. Check the internet availability [$count_curl sec]"
                count_curl=$((count_curl + 1))
            else
                return
            fi

            if [ $count_curl -lt 30 ]; then
                sleep_interval=1
            elif [ $count_curl -ge 30 ] && [ $count_curl -lt 60 ]; then
                sleep_interval=5
            elif [ $count_curl -ge 60 ] && [ $count_curl -lt 90 ]; then
                sleep_interval=10
            else
                sleep_interval=30
            fi

            sleep $sleep_interval
        done
    fi
}


add_cron_job() {
    ## Future: make a check so that it doesn't recreate many times
    config_get domain_list_enabled "$section" "domain_list_enabled"
    config_get subnets_list_enabled "$section" "subnets_list_enabled"
    config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
    config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled"
    config_get update_interval "main" "update_interval"

    case "$update_interval" in
        "1h")
            cron_job="13 * * * * /etc/init.d/podkop list_update"
            ;;
        "3h")
            cron_job="13 */3 * * * /etc/init.d/podkop list_update"
            ;;
        "12h")
            cron_job="13 */12 * * * /etc/init.d/podkop list_update"
            ;;
        "1d")
            cron_job="13 9 * * * /etc/init.d/podkop list_update"
            ;;
        "3d")
            cron_job="13 9 */3 * * /etc/init.d/podkop list_update"
            ;;
        *)
            log "Invalid update_interval value: $update_interval"
            return
            ;;
    esac

    if [ "$domain_list_enabled" -eq 1 ] || [ "$subnets_list_enabled" -eq 1 ] ||
       [ "$custom_download_domains_list_enabled" -eq 1 ] || [ "$custom_download_subnets_list_enabled" -eq 1 ] ; then
        remove_cron_job
        crontab -l | {
            cat
            echo "$cron_job"
        } | crontab -
        log "The cron job has been created: $cron_job"
    fi
}

remove_cron_job() {
    (crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab -
    log "The cron job removed"
}

list_update() {
    log "Update remote lists"
    config_foreach process_remote_ruleset
    config_foreach process_domains_list_url
    config_foreach process_subnet_for_section_remote
}


# sing-box funcs

sing_box_uci() {
    local config="/etc/config/sing-box"
    if grep -q "option enabled '0'" "$config" ||
        grep -q "option user 'sing-box'" "$config"; then
        sed -i \
            -e "s/option enabled '0'/option enabled '1'/" \
            -e "s/option user 'sing-box'/option user 'root'/" $config
        log "Change sing-box UCI config"
    else
        log "Sing-box UCI config OK"
    fi
}

# Future: for every section. +1 port?
process_socks5() {
    config_get_bool socks5 "main" "socks5" "0"
    if [ "$socks5" -eq 1 ]; then
        log "Socks5 local enable port 2080"
        jq '.inbounds += [{
            "tag": "mixed-in",
            "type": "mixed",
            "listen": "0.0.0.0",
            "listen_port": 2080,
            "set_system_proxy": false
        }]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG

        #local rule_exists=$(jq -r '.route.rules[] | select(.inbound[] == "mixed-in")' $SING_BOX_CONFIG)
        local rule_exists=$(jq -r '.route.rules // [] | map(select(.inbound // [] | index("mixed-in"))) | length' $SING_BOX_CONFIG)

        if [ -z "$rule_exists" ]; then
            jq '.route.rules += [{
                "inbound": ["mixed-in"],
                "outbound": "main",
                "action": "route"
            }]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
        fi
    fi
}

sing_box_inbound_proxy() {
    local listen_port="$1"

    jq -n \
    --arg listen_port "$listen_port" \
    '{
        "log": {
            "level": "warn"
        },
        "inbounds": [
            {
                "tag": "tproxy-in",
                "type": "tproxy",
                "listen": "::",
                "listen_port": ($listen_port|tonumber),
                "tcp_fast_open": true,
                "udp_fragment": true
            },
            {
                "tag": "dns-in",
                "type": "direct",
                "listen": "127.0.0.42",
                "listen_port": 53
            }
        ],
        "outbounds": [
            {
            "tag": "direct-out",
            "type": "direct"
            }
        ]
    }'  > $SING_BOX_CONFIG
}

sing_box_dns() {
    log "Configure DNS in sing-box"
    jq \
    --arg FAKEIP "$FAKEIP" \
    '.dns = {
        "strategy": "ipv4_only",
        "fakeip": {
            "enabled": true,
            "inet4_range": $FAKEIP
        },
        "servers": [
            {
                "tag": "cloudflare-doh-server",
                "address": "https://1.1.1.1/dns-query",
                "detour": "direct-out"
            },
            {
                "tag": "fakeip-server",
                "address": "fakeip"
            }
        ]
    }' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}

sing_box_dns_rule_fakeip() {
    log "Configure fakeip route in sing-box"
    jq \
    '.dns += {
        "rules": [
              {
            "query_type": [
                "HTTPS"
            ],
            "action": "reject"
            },
            {
            "server": "fakeip-server",
            "rule_set": []
            }
        ]
    }' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}

sing_box_dns_rule_fakeip_section() {
    local rule_set=$1
    echo $rule_set
    log "Adding section to fakeip route rules in sing-box"

    jq \
    --arg rule_set "$rule_set" \
    '.dns.rules |= map(
        if .server == "fakeip-server" then 
            if any(.rule_set[]?; . == $rule_set) then 
                .
            else 
                .rule_set += [$rule_set] 
            end
        else 
            . 
        end
    )' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
}

sing_box_cache_file() {
    log "Configure cache.db in sing-box"
    jq \
    --arg CACHE_FILE_PATH "$CACHE_FILE_PATH" \
    '.experimental = {
        "cache_file": {
        "enabled": true,
        "store_fakeip": true,
        "path": $CACHE_FILE_PATH
        }
    }' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}

sing_box_outdound() {
    local section="$1"

    config_get mode "$section" "mode"
    case "$mode" in
    "vpn")
        log "VPN mode"
        log "You are using VPN mode, make sure you have installed all the necessary packages and configured."
        config_get interface "$section" "interface"
        sing_box_outbound_interface $section $interface
        ;;
    "proxy")
        log "Proxy mode"
        config_get proxy_config_type "$section" "proxy_config_type"

        if [ "$proxy_config_type" = "outbound" ]; then
            config_get outbound_json $section "outbound_json"
            if [ -n "$outbound_json" ]; then
                log "Using JSON outbound configuration"
                sing_box_config_outbound_json "$outbound_json" 
            else
                log "Missing outbound JSON configuration"
                return
            fi
        else
            config_get proxy_string $section "proxy_string"
            if [[ "$proxy_string" =~ ^ss:// ]]; then
                sing_box_config_shadowsocks "$section" "$proxy_string"
            elif [[ "$proxy_string" =~ ^vless:// ]]; then
                sing_box_config_vless "$section" "$proxy_string"
            else
                log "Unsupported proxy type or missing configuration"
                return
            fi
        fi
        ;;
    *)
        log "Requires *vpn* or *proxy* value"
        return
        ;;
    esac    
}

sing_box_outbound_interface() {
    local section="$1"
    local interface="$2"

    jq --arg section "$section" \
       --arg interface "$interface" \
       '. |
          .outbounds |= (
            map(
              if .tag == $section then
                . + {"type": "direct", "bind_interface": $interface}
              else . end
            ) +
            (
              if (map(select(.tag == $section)) | length) == 0 then
                [{"tag": $section, "type": "direct", "bind_interface": $interface}]
              else [] end
            )
          )' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"

    if [ $? -eq 0 ]; then
        log "Config updated successfully"
    else
        log "Error: Invalid JSON config generated"
        return 1
    fi
}

sing_box_rule_dns() {
    log "Configure rule dns in sing-box"
    jq \
    '.route += {
        "rules": [
        {
            "inbound": [
                "dns-in",
                "tproxy-in"
            ],
            "action": "sniff"
        },
        {
            "protocol": "dns",
            "action": "hijack-dns"
        }
        ],
        "auto_detect_interface": true
    }' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}

sing_box_config_check() {
    if ! sing-box -c $SING_BOX_CONFIG check >/dev/null 2>&1; then
        log "Sing-box configuration is invalid"
        exit 1
    fi
}

sing_box_config_outbound_json() {
    local json_config="$1"
    local listen_port="$2"
    
    cat > /tmp/base_config.json << EOF
{
  "log": {
    "level": "warn"
  },
  "inbounds": [
    {
        "type": "tproxy",
        "listen": "::",
        "listen_port": $listen_port,
        "tcp_fast_open": true,
        "udp_fragment": true
    },
    {
        "tag": "dns-in",
        "type": "direct",
        "listen": "127.0.0.42",
        "listen_port": 53
    }    
  ],
  "outbounds": [],
  "route": {
    "auto_detect_interface": true
  }
}
EOF

    jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > $SING_BOX_CONFIG
    rm -f /tmp/base_config.json
}



sing_box_config_shadowsocks() {
    local section="$1"
    local STRING="$2"

    if echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 -d 2>/dev/null | grep -q ":"; then
        local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 -d 2>/dev/null )
        local method=$(echo "$encrypted_part" | cut -d':' -f1)
        local password=$(echo "$encrypted_part" | cut -d':' -f2-)
    else
        local method_and_password=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1)
        local method=$(echo "$method_and_password" | cut -d':' -f1)
        local password=$(echo "$method_and_password" | cut -d':' -f2- | sed 's/%3D/=/g')
        if echo "$method" | base64 -d ; then
            method=$(echo "$method" | base64 -d)
        fi
    fi

    local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1)
    local port=$(echo "$STRING" | sed -n 's|.*:\([0-9]\+\).*|\1|p')

    jq \
    --arg section "$section" \
    --arg server "$server" \
    --argjson port "$port" \
    --arg method "$method" \
    --arg password "$password" \
    '. |
      .outbounds |= (
        map(
          if .tag == $section then
            . + {
              "type": "shadowsocks",
              "server": $server,
              "server_port": ($port | tonumber),
              "method": $method,
              "password": $password,
              "udp_over_tcp": { "enabled": true, "version": 2 }
            }
          else . end
        ) +
        (
          if (map(select(.tag == $section)) | length) == 0 then
            [{
              "tag": $section,
              "type": "shadowsocks",
              "server": $server,
              "server_port": ($port | tonumber),
              "method": $method,
              "password": $password,
              "udp_over_tcp": { "enabled": true, "version": 2 }
            }]
          else [] end
        )
      )' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG

    if [ $? -eq 0 ]; then
        log "Config updated successfully"
    else
        log "Error: Invalid JSON config generated"
        return 1
    fi
}

sing_box_config_vless() {
    local section="$1"
    local STRING="$2"

    get_param() {
        local param="$1"
        local value=$(echo "$STRING" | sed -n "s/.*[?&]$param=\([^&?#]*\).*/\1/p")
        value=$(echo "$value" | sed 's/%2F/\//g; s/%2C/,/g; s/%3D/=/g; s/%2B/+/g; s/%20/ /g' | tr -d '\n' | tr -d '\r')
        echo "$value"
    }

    uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g')
    server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g')
    port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1 | cut -d'#' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g')

    jq \
    --arg server      "$server" \
    --argjson port    "$port" \
    --arg uuid        "$uuid" \
    --arg type        "$(get_param "type")" \
    --arg flow        "$(get_param "flow")" \
    --arg sni         "$(get_param "sni")" \
    --arg fp          "$(get_param "fp")" \
    --arg security    "$(get_param "security")" \
    --arg pbk         "$(get_param "pbk")" \
    --arg sid         "$(get_param "sid")" \
    --arg alpn        "$(get_param "alpn")" \
    --arg path        "$(get_param "path")" \
    --arg host        "$(get_param "host")" \
    --arg spx         "$(get_param "spx")" \
    --arg insecure    "$(get_param "allowInsecure")" \
    --arg section     "$section" \
    '. |
      # Updating an existing outbound by tag or adding a new one
      .outbounds |= (
        # If an element with the required tag is found, update it
        map(
          if .tag == $section then
            . + {
              "type": "vless",
              "server": $server,
              "server_port": ($port | tonumber),
              "uuid": $uuid,
              "packet_encoding": "",
              "domain_strategy": "",
              "flow": $flow
            }
          else . end
        ) +
        # Add a new outbound if the required tag is not present
        (
          if (map(select(.tag == $section)) | length) == 0 then
            [{
              "tag": $section,
              "type": "vless",
              "server": $server,
              "server_port": ($port | tonumber),
              "uuid": $uuid,
              "packet_encoding": "",
              "domain_strategy": "",
              "flow": $flow
            }]
          else [] end
        )
      ) |
      # Additional parameters such as transport and tls
      if $flow != "" then
        .outbounds |= map(
          if .tag == $section then
            .flow = $flow
          else . end
        )
      else . end |
      if $type == "ws" then
        .outbounds |= map(
          if .tag == $section then
            .transport = {
              "type": "ws",
              "path": $path
            } |
            if $host != "" then
              .transport.headers = { "Host": $host }
            else . end
          else . end
        )
      elif $type == "grpc" then
        .outbounds |= map(
          if .tag == $section then
            .transport = { "type": "grpc" }
          else . end
        )
      else . end |
      if $security == "reality" or $security == "tls" then
        .outbounds |= map(
          if .tag == $section then
            .tls = {
              "enabled": true,
              "server_name": $sni,
              "utls": {
                "enabled": true,
                "fingerprint": $fp
              },
              "insecure": ($insecure == "1")
            } |
            if $alpn != "" then
              .tls.alpn = ($alpn | split(","))
            else . end |
            if $security == "reality" then
              .tls.reality = {
                "enabled": true,
                "public_key": $pbk,
                "short_id": $sid
              }
            else . end
          else . end
        )
      else . end' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG


    if [ $? -eq 0 ]; then
        log "Config created successfully"
    else
        log "Error: Invalid JSON config generated"
        return 1
    fi
}

# Process. Sing-box rules

sing_box_ruleset_domains() {
    log "Configure ruleset domains in sing-box"

    local domain=$1
    local tag=$2

    # Check if there is a route.rule_set for the specified tag
    local tag_exists=$(jq -r --arg tag "$tag" '
        .route.rule_set[]? | select(.tag == $tag) | .tag
    ' /etc/sing-box/config.json)

    # If the tag exists, add the domain
    if [[ -n "$tag_exists" ]]; then
        jq \
        --arg tag "$tag" \
        --arg domain "$domain" \
        '
        .route.rule_set[] |= 
        if .tag == $tag then
            .rules[0].domain_suffix += [$domain]
        else
            .
        end
        ' /etc/sing-box/config.json > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json

        log "$domain added to the list for tag $tag"
    else
        # If tag does not exist, add a new set of rules
        jq \
        --arg tag "$tag" \
        --arg domain "$domain" \
        '
        .route.rule_set += [
            {
                "tag": $tag,
                "type": "inline",
                "rules": [
                    {
                        "domain_suffix": [$domain]
                    }
                ]
            }
        ]' /etc/sing-box/config.json > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json

        log "$domain added as a new rule set for tag $tag"
    fi
}

sing_box_ruleset_subnets() {
    log "Configure ruleset domains in sing-box"

    local subnet=$1
    local tag=$2
    
    # nft
    nft add element inet PodkopTable podkop_subnets { $subnet }

    # Check if there is a route.rule_set for the specified tag
    local tag_exists=$(jq -r --arg tag "$tag" '
        .route.rule_set[]? | select(.tag == $tag) | .tag
    ' /etc/sing-box/config.json)

    # If tag exists, add the domain
    if [[ -n "$tag_exists" ]]; then
        jq \
        --arg tag "$tag" \
        --arg subnet "$subnet" \
        '
        .route.rule_set[] |= 
        if .tag == $tag then
            .rules[0].ip_cidr += [$subnet]
        else
            .
        end
        ' /etc/sing-box/config.json > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json

        log "$subnet added to the list for tag $tag"
    else
        # If tag does not exist, add a new set of rules
        jq \
        --arg tag "$tag" \
        --arg subnet "$subnet" \
        '
        .route.rule_set += [
            {
                "tag": $tag,
                "type": "inline",
                "rules": [
                    {
                        "ip_cidr": [$subnet]
                    }
                ]
            }
        ]' /etc/sing-box/config.json > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json

        log "$subnet added as a new rule set for tag $tag"
    fi
}

process_domains_for_section() {
    local section="$1"

    config_get custom_domains_list_type "$section" "custom_domains_list_type" "disabled"

    if [ "$custom_domains_list_type" != "disabled" ]; then
        log "Adding a custom domains list for section $section"
        if [ "$custom_domains_list_type" = "dynamic" ]; then
            # Handle list domains from custom_domains
            config_list_foreach "$section" custom_domains "sing_box_ruleset_domains" "$section"
        elif [ "$custom_domains_list_type" = "text" ]; then
            # Handle domains from text
            config_get custom_domains_text "$section" "custom_domains_text"
            process_domains_text "$custom_domains_text" "$section"
        fi
    fi
}

sing_box_ruleset_remote() {
    local tag=$1
    local type=$2
    local update_interval=$3

    url="$SRS_MAIN_URL/$tag.srs"

    local tag_exists=$(jq -r --arg tag "$tag" '
        .route.rule_set[]? | select(.tag == $tag) | .tag
    ' "$SING_BOX_CONFIG")

    if [[ -n "$tag_exists" ]]; then
        log "Ruleset with tag $tag already exists. Skipping addition."
    else
        jq \
        --arg tag "$tag" \
        --arg type "$type" \
        --arg url "$url" \
        --arg update_interval "$update_interval" \
        '
        .route.rule_set += [
            {
                "tag": $tag,
                "type": $type,
                "format": "binary",
                "url": $url,
                "update_interval": $update_interval
            }
        ]' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"

        log "Added new ruleset with tag $tag"
    fi
}

list_subnets_download() {
    local service="$1"
    local table="PodkopTable"

    case "$service" in
        "twitter")
            URL=$SUBNETS_TWITTER
            ;;
        "meta")
            URL=$SUBNETS_META
            ;;
        "telegram")
            URL=$SUBNETS_TELERAM
            ;;
        "discord")
            URL=$SUBNETS_DISCORD
            nft add set inet $table podkop_discord_subnets { type ipv4_addr\; flags interval\; auto-merge\; }
            nft add rule inet $table mangle iifname "br-lan" ip daddr @podkop_discord_subnets udp dport { 50000-65535 } meta mark set 0x105 counter
            ;;
        *)
            return
            ;;
    esac

    local filename=$(basename "$URL")
    wget -q -O "/tmp/podkop/$filename" "$URL"

    while IFS= read -r subnet; do
        if [ "$service" = "discord" ]; then
            nft add element inet $table podkop_discord_subnets { $subnet }
        else
            nft add element inet $table podkop_subnets { $subnet }
        fi
    done <"/tmp/podkop/$filename"
}

sing_box_rules() {
    log "Configure rule in sing-box"
    local rule_set="$1"
    local outbound="$2"

    # Check if there is an outbound rule for "tproxy-in"
    local rule_exists=$(jq -r '.route.rules[] | select(.outbound == "'"$outbound"'" and .inbound == ["tproxy-in"])' "$SING_BOX_CONFIG")

    if [[ -n "$rule_exists" ]]; then
        # If a rule for tproxy-in exists, add a new rule_set to the existing rule
        jq \
        --arg rule_set "$rule_set" \
        --arg outbound "$outbound" \
        '(.route.rules[] | select(.outbound == $outbound and .inbound == ["tproxy-in"]) .rule_set) += [$rule_set]' \
        "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
    else
        # If there is no rule for tproxy-in, create a new one with rule_set
        jq \
        --arg rule_set "$rule_set" \
        --arg outbound "$outbound" \
        '.route.rules += [{
            "inbound": ["tproxy-in"],
            "rule_set": [$rule_set],
            "outbound": $outbound,
            "action": "route"
        }]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
    fi
}

sing_box_quic_reject() {
    local quic_rule_exists=$(jq -e '.route.rules[] | select(.protocol == "quic" and .action == "reject")' "$SING_BOX_CONFIG")

    if [[ -z "$quic_rule_exists" ]]; then
        jq '
        .route.rules |= (
            reduce .[] as $rule ([];
                if $rule.protocol == "dns" and $rule.action == "hijack-dns" then
                    . + [$rule, {"protocol": "quic", "action": "reject"}]
                else
                    . + [$rule]
                end
            )
        )' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"

        log "QUIC reject rule added successfully"
    fi
}

process_remote_ruleset() {
    config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
    if [ "$domain_list_enabled" -eq 1 ]; then
        log "Adding a srs list for $section"
        config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d"
        config_list_foreach "$section" domain_list "list_subnets_download" "$section" "$domain_list"
    fi
}

sing_box_rule_preset() {
    config_get custom_domains_list_type "$section" "custom_domains_list_type"
    config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled"
    config_get custom_local_domains_list_enabled "$section" "custom_local_domains_list_enabled"
    config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"

    if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_subnets_list_enabled" != "disabled" ] ||
        [ "$custom_local_domains_list_enabled" = "1" ] || [ "$custom_download_domains_list_enabled" = "1" ]; then
        sing_box_rules "$section" "$section"
        sing_box_dns_rule_fakeip_section "$section" "$section"
    fi

    config_get domain_list_enabled "$section" "domain_list_enabled"
    config_get domain_list "$section" "domain_list"
    if [ "$domain_list_enabled" -eq 1 ]; then
        config_list_foreach $section domain_list sing_box_rules $section
        config_list_foreach $section domain_list sing_box_dns_rule_fakeip_section domain_list
    fi 
}

list_custom_local_domains_create() {
    local section="$2"
    local local_file="$1"
    local filename=$(basename "$local_file" | cut -d. -f1)

    while IFS= read -r domain; do
        log "From local file: $domain"
        sing_box_ruleset_domains $domain $section
    done <"$local_file"
}

process_domains_list_local() {
    local section="$1"

    config_get custom_local_domains_list_enabled "$section" "custom_local_domains_list_enabled"
    if [ "$custom_local_domains_list_enabled" -eq 1 ]; then
        log "Adding a custom domains list from file in $section"
        config_list_foreach "$section" "custom_local_domains" list_custom_local_domains_create "$section"
    fi
}

list_custom_url_domains_create() {
    local section="$2"
    local URL="$1"
    local filename=$(basename "$URL")

    wget -q -O "/tmp/podkop/${filename}" "$URL"

    while IFS= read -r domain; do
        log "From local file: $domain"
        sing_box_ruleset_domains $domain $section
    done <"/tmp/podkop/$filename"
}

process_domains_list_url() {
    local section="$1"

    config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
    if [ "$custom_download_domains_list_enabled" -eq 1 ]; then
        log "Adding a custom domains list from URL in $section"
        config_list_foreach "$section" "custom_download_domains" list_custom_url_domains_create "$section"
    fi
}

process_subnet_for_section() {
    local section="$1"

    config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled" "disabled"
    if [ "$custom_subnets_list_enabled" != "disabled" ]; then
        log "Adding a custom subnet list for section $section"
        if [ "$custom_subnets_list_enabled" = "dynamic" ]; then
            # Handle list domains from custom_domains
            config_list_foreach "$section" custom_subnets "sing_box_ruleset_subnets" "$section"
        elif [ "$custom_subnets_list_enabled" = "text" ]; then
            # Handle domains from text
            config_get custom_subnets_text "$section" "custom_subnets_text"
            process_subnets_text "$custom_subnets_text" "$section"
        fi
    fi
}

list_custom_url_subnets_create() {
    local section="$2"
    local URL="$1"
    local filename=$(basename "$URL")

    wget -q -O "/tmp/podkop/${filename}" "$URL"

    while IFS= read -r subnet; do
        log "From local file: $subnet"
        sing_box_ruleset_subnets $subnet $section
    done <"/tmp/podkop/$filename"
}

process_subnet_for_section_remote() {
    local section="$1"

    config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled" "disabled"
    if [ "$custom_download_subnets_list_enabled" -eq "1" ]; then
            log "Adding a custom SUBNET list from URL in $section"
            config_list_foreach "$section" "custom_download_subnets" list_custom_url_subnets_create "$section"
    fi
}

process_all_traffic_for_section() {
    local section="$1"

    config_get all_traffic_from_ip_enabled "$section" "all_traffic_from_ip_enabled"
    if [ "$all_traffic_from_ip_enabled" -eq "1" ]; then
        log "Adding an IP to redirect all traffic"
        config_list_foreach $section all_traffic_ip list_all_traffic_from_ip
        config_list_foreach $section all_traffic_ip sing_box_rules_source_ip_cidr "$section" "$all_traffic_ip"
    fi
}

sing_box_rules_source_ip_cidr() {
    log "Configure source_ip_cidr rule in sing-box"
    local outbound="$2"
    local source_ip_cidr="$1"

    local current_source_ip_cidr=$(jq -r ".route.rules[] | select(.outbound == \"$outbound\" and .source_ip_cidr) | .rule_set" $SING_BOX_CONFIG)

    if [[ -n "$current_source_ip_cidr" ]]; then
        jq \
        --arg source_ip_cidr "$source_ip_cidr" \
        --arg outbound "$outbound" \
        '(.route.rules[] | select(.outbound == $outbound) | .source_ip_cidr) += [$source_ip_cidr]' \
        $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
    else
        jq \
        --arg source_ip_cidr "$source_ip_cidr" \
        --arg outbound "$outbound" \
        '.route.rules = [
            {
                "inbound": ["tproxy-in"],
                "source_ip_cidr": [$source_ip_cidr],
                "outbound": $outbound
            }
        ] + .route.rules' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
    fi
}

## nftables
list_all_traffic_from_ip() {
    local ip="$1"
    if ! nft list chain inet PodkopTable mangle | grep -q "ip saddr $ip"; then
        nft add set inet PodkopTable localv4 { type ipv4_addr\; flags interval\; }
        nft add element inet PodkopTable localv4 { \
            0.0.0.0/8, \
            10.0.0.0/8, \
            127.0.0.0/8, \
            169.254.0.0/16, \
            172.16.0.0/12, \
            192.0.0.0/24, \
            192.0.2.0/24, \
            192.88.99.0/24, \
            192.168.0.0/16, \
            198.51.100.0/24, \
            203.0.113.0/24, \
            224.0.0.0/4, \
            240.0.0.0-255.255.255.255 }
        nft insert rule inet PodkopTable mangle iifname "br-lan" ip saddr $ip meta l4proto { tcp, udp } meta mark set 0x105 counter
        nft insert rule inet PodkopTable mangle ip saddr $ip ip daddr @localv4 return
    fi
}

# Diagnotics
check_proxy() {
    if ! command -v sing-box >/dev/null 2>&1; then
        nolog "sing-box is not installed"
        return 1
    fi

    if [ ! -f $SING_BOX_CONFIG ]; then
        nolog "Configuration file not found"
        return 1
    fi

    nolog "Checking sing-box configuration..."

    if ! sing-box -c $SING_BOX_CONFIG check >/dev/null; then
        nolog "Invalid configuration"
        return 1
    fi

    jq '
    walk(
        if type == "object" then
            with_entries(
                if .key == "uuid" then
                    .value = "MASKED"
                elif .key == "server" then
                    .value = "MASKED"
                elif .key == "server_name" then
                    .value = "MASKED"
                elif .key == "password" then
                    .value = "MASKED"
                elif .key == "public_key" then
                    .value = "MASKED"
                elif .key == "short_id" then
                    .value = "MASKED"
                elif .key == "fingerprint" then
                    .value = "MASKED"
                elif .key == "server_port" then
                    .value = "MASKED"
                else . end
            )
        else . end
    )' $SING_BOX_CONFIG

    nolog "Checking proxy connection..."


   for attempt in `seq 1 5`; do
       response=$(sing-box tools fetch ifconfig.me -D /etc/sing-box 2>/dev/null)
       if echo "$response" | grep -q "^<html\|403 Forbidden"; then
           continue
       fi
       if [[ $response =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
           ip=$(echo "$response" | sed -n 's/^[0-9]\+\.[0-9]\+\.[0-9]\+\.\([0-9]\+\)$/X.X.X.\1/p')
           nolog "$ip - should match proxy IP"
           return 0
       elif echo "$response" | grep -q "^[0-9a-fA-F:]*::[0-9a-fA-F:]*$\|^[0-9a-fA-F:]\+$"; then
           ip=$(echo "$response" | sed 's/\([0-9a-fA-F]\+:[0-9a-fA-F]\+:[0-9a-fA-F]\+\):.*/\1:XXXX:XXXX:XXXX/')
           nolog "$ip - should match proxy IP"
           return 0
       fi
       if [ $attempt -eq 5 ]; then
           nolog "Failed to get valid IP address after 5 attempts"
           if [ -z "$response" ]; then
               nolog "Error: Empty response"
           else
               nolog "Error response: $response"
           fi
           return 1
       fi
   done
}

check_nft() {
    if ! command -v nft >/dev/null 2>&1; then
        nolog "nft is not installed"
        return 1
    fi

    nolog "Checking PodkopTable rules..."
    
    # Check if table exists
    if ! nft list table inet PodkopTable >/dev/null 2>&1; then
        nolog "PodkopTable not found"
        return 1
    fi

    # Get all sets
    nolog "\nSets configuration:"

    nft list table inet PodkopTable

    nolog "\nNFT check completed"
}

check_github() {
    nolog "Checking GitHub connectivity..."
    
    if ! curl -m 3 github.com; then
        nolog "Error: Cannot connect to GitHub"
        return 1
    fi
    nolog "GitHub is accessible"
    
    nolog "Checking lists availability:"
    for url in "$DOMAINS_RU_INSIDE" "$DOMAINS_RU_OUTSIDE" "$DOMAINS_UA" "$DOMAINS_YOUTUBE" \
              "$SUBNETS_TWITTER" "$SUBNETS_META" "$SUBNETS_DISCORD"; do
        local list_name=$(basename "$url")
        wget -q -O /dev/null "$url"
        if [ $? -eq 0 ]; then
            nolog "- $list_name: available"
        else
            nolog "- $list_name: not available"
        fi
    done
}

check_dnsmasq() {
    nolog "Checking dnsmasq configuration..."

    local config=$(uci show dhcp.@dnsmasq[0])
    if [ -z "$config" ]; then
        nolog "No dnsmasq configuration found"
        return 1
    fi

    echo "$config" | while IFS='=' read -r key value; do
        nolog "$key = $value"
    done
}

check_sing_box_connections() {
    nolog "Checking sing-box connections..."
    
    if ! command -v netstat >/dev/null 2>&1; then
        nolog "netstat is not installed"
        return 1
    fi

    local connections=$(netstat -tuanp | grep sing-box)
    if [ -z "$connections" ]; then
        nolog "No active sing-box connections found"
        return 1
    fi

    echo "$connections" | while read -r line; do
        nolog "$line"
    done
}

check_sing_box_logs() {
    nolog "Showing sing-box logs from system journal..."

    local logs=$(logread -e sing-box | tail -n 50)
    if [ -z "$logs" ]; then
        nolog "No sing-box logs found"
        return 1
    fi

    echo "$logs"
}

check_logs() {
    nolog "Showing podkop logs from system journal..."
    
    if command -v logread >/dev/null 2>&1; then
        logread -e podkop  | tail -n 50
    else
        nolog "Error: logread command not found"
        return 1
    fi
}

show_sing_box_config() {
    nolog "Current sing-box configuration:"
    
    if [ ! -f "$SING_BOX_CONFIG" ]; then
        nolog "Configuration file not found"
        return 1
    fi

    jq '
    walk(
        if type == "object" then
            with_entries(
                if .key == "uuid" then
                    .value = "MASKED"
                elif .key == "server" then
                    .value = "MASKED"
                elif .key == "server_name" then
                    .value = "MASKED"
                elif .key == "password" then
                    .value = "MASKED"
                elif .key == "public_key" then
                    .value = "MASKED"
                elif .key == "short_id" then
                    .value = "MASKED"
                elif .key == "fingerprint" then
                    .value = "MASKED"
                elif .key == "server_port" then
                    .value = "MASKED"
                else . end
            )
        else . end
    )' "$SING_BOX_CONFIG"
}

show_config() {
    nolog "Current podkop configuration:"
    
    if [ ! -f /etc/config/podkop ]; then
        nolog "Configuration file not found"
        return 1
    fi

    tmp_config=$(mktemp)
    
    cat /etc/config/podkop | sed \
        -e 's/\(option proxy_string\).*/\1 '\''MASKED'\''/g' \
        -e 's/\(option outbound_json\).*/\1 '\''MASKED'\''/g' \
        -e 's/\(option second_proxy_string\).*/\1 '\''MASKED'\''/g' \
        -e 's/\(option second_outbound_json\).*/\1 '\''MASKED'\''/g' \
        -e 's/\(vless:\/\/[^@]*@\)/vless:\/\/MASKED@/g' \
        -e 's/\(ss:\/\/[^@]*@\)/ss:\/\/MASKED@/g' \
        -e 's/\(pbk=[^&]*\)/pbk=MASKED/g' \
        -e 's/\(sid=[^&]*\)/sid=MASKED/g' \
        > "$tmp_config"

    cat "$tmp_config"
    rm -f "$tmp_config"
}

show_version() {
    local version=$(opkg info podkop | grep -m 1 "Version:" | cut -d' ' -f2)
    echo "$version"
}