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

START=99
STOP=10
USE_PROCD=1

PROG=/usr/bin/mihomo
CONFIG_DIR=/etc/mihomo
CONFIG_FILE=$CONFIG_DIR/config.yaml
UI_DIR=/etc/mihomo/ui
LOG_FILE=/var/log/mihomo.log

uci_get() {
	local val
	val=$(uci -q get mihomo.config."$1")
	echo "${val:-$2}"
}

get_subscription_url_via_auth() {
	local panel_url="$1"
	local username="$2"
	local password="$3"
	local auth_response auth_token subscribe_url

	[ -z "$panel_url" ] || [ -z "$username" ] || [ -z "$password" ] && return 1

	# Normalize panel URL (remove trailing slash)
	panel_url=$(echo "$panel_url" | sed 's:/*$::')

	logger -t mihomo "Authenticating with panel: $panel_url"

	# Authenticate with Xboard/v2board API
	# Try Xboard endpoint first
	auth_response=$(curl -s --connect-timeout 10 --max-time 10 \
		-H "Content-Type: application/json" \
		-d "{\"email\":\"$username\",\"password\":\"$password\"}" \
		"$panel_url/api/v1/user/login" 2>/dev/null)

	auth_token=$(echo "$auth_response" | grep -o '"token":"[^"]*' | cut -d'"' -f4)

	if [ -z "$auth_token" ]; then
		logger -t mihomo "Panel authentication failed for $username"
		return 1
	fi

	logger -t mihomo "Panel authentication success, getting subscription URL..."

	# Get subscription info
	local subscribe_response
	subscribe_response=$(curl -s --connect-timeout 10 --max-time 10 \
		-H "Authorization: Bearer $auth_token" \
		"$panel_url/api/v1/user/getSubscribe" 2>/dev/null)

	subscribe_url=$(echo "$subscribe_response" | grep -o '"subscribe_url":"[^"]*' | cut -d'"' -f4)

	if [ -z "$subscribe_url" ]; then
		logger -t mihomo "Failed to get subscription URL from panel"
		return 1
	fi

	# Output the subscription URL
	echo "$subscribe_url"
	return 0
}

convert_base64_subscription_to_clash() {
	local base64_content="$1"
	local output_file="$2"

	logger -t mihomo "Converting subscription format..."

	# Use sub-web API to convert base64 subscription to Clash format
	# This converts common proxy formats (ss, trojan, vless, etc) to Clash YAML
	local clash_config
	clash_config=$(curl -s --connect-timeout 10 --max-time 30 \
		-X POST \
		-H "Content-Type: application/json" \
		-d "{\"subscription\":\"$base64_content\",\"target\":\"clash\",\"del\":\"\"}" \
		"https://api.dler.io/sub?target=clash" 2>/dev/null)

	if [ -z "$clash_config" ]; then
		logger -t mihomo "Conversion API failed, trying fallback..."
		return 1
	fi

	# Check if conversion was successful
	if echo "$clash_config" | grep -q "proxies:"; then
		echo "$clash_config" > "$output_file"
		logger -t mihomo "Subscription converted successfully"
		return 0
	fi

	return 1
}

download_subscription() {
	local url="$1"
	[ -z "$url" ] && return 1
	logger -t mihomo "Downloading subscription from: $url"

	local http_code
	local temp_file="$CONFIG_DIR/subscription.tmp"

	http_code=$(curl -sL --connect-timeout 10 --max-time 30 \
		-w "%{http_code}" -o "$temp_file" "$url" 2>/dev/null)

	if [ "$http_code" != "200" ]; then
		logger -t mihomo "Subscription download failed (HTTP $http_code)"
		rm -f "$temp_file"
		return 1
	fi

	if [ ! -s "$temp_file" ]; then
		logger -t mihomo "Subscription file is empty"
		rm -f "$temp_file"
		return 1
	fi

	# Check if it's a valid YAML file (should start with # or valid YAML keys)
	local first_bytes
	first_bytes=$(head -c 20 "$temp_file")

	if echo "$first_bytes" | grep -qE '^(#|[a-z-]+:|-)'; then
		# Already Clash YAML format, use as-is
		mv "$temp_file" "$CONFIG_DIR/subscription.yaml"
		logger -t mihomo "Subscription downloaded successfully"
		return 0
	fi

	# Check if it's base64 encoded (common from Xboard/v2board)
	if echo "$first_bytes" | grep -qE '^[A-Za-z0-9+/=]'; then
		logger -t mihomo "Detected base64 subscription, auto-converting..."
		local base64_content
		base64_content=$(cat "$temp_file")

		if convert_base64_subscription_to_clash "$base64_content" "$CONFIG_DIR/subscription.yaml"; then
			rm -f "$temp_file"
			return 0
		else
			logger -t mihomo "Auto-conversion failed"
			rm -f "$temp_file"
			return 1
		fi
	fi

	logger -t mihomo "Unknown subscription format"
	rm -f "$temp_file"
	return 1
}

download_ui() {
	[ -d "$UI_DIR" ] && [ -f "$UI_DIR/index.html" ] && return 0
	local ui_url
	ui_url=$(uci_get ui_url)
	[ -z "$ui_url" ] && return 1
	logger -t mihomo "Downloading web dashboard..."
	mkdir -p "$UI_DIR"
	curl -sL --connect-timeout 10 --max-time 60 "$ui_url" | tar xz -C "$UI_DIR" 2>/dev/null
	if [ -f "$UI_DIR/index.html" ]; then
		logger -t mihomo "Web dashboard installed"
	else
		local subdir
		subdir=$(ls -d "$UI_DIR"/*/ 2>/dev/null | head -1)
		if [ -n "$subdir" ] && [ -f "${subdir}index.html" ]; then
			mv "${subdir}"* "$UI_DIR/" 2>/dev/null
			rmdir "$subdir" 2>/dev/null
			logger -t mihomo "Web dashboard installed"
		else
			logger -t mihomo "Web dashboard download failed"
		fi
	fi
}

generate_config() {
	local enabled api_port api_secret mixed_port redir_port tproxy_port
	local dns_port dns_mode allow_lan log_level ipv6 subscription_url
	local panel_url panel_username panel_password

	enabled=$(uci_get enabled 0)
	[ "$enabled" != "1" ] && return 1

	subscription_url=$(uci_get subscription_url)
	panel_url=$(uci_get panel_url)
	panel_username=$(uci_get panel_username)
	panel_password=$(uci_get panel_password)
	api_port=$(uci_get api_port 9090)
	api_secret=$(uci_get api_secret)
	mixed_port=$(uci_get mixed_port 7890)
	redir_port=$(uci_get redir_port 7892)
	tproxy_port=$(uci_get tproxy_port 7893)
	dns_port=$(uci_get dns_port 1053)
	dns_mode=$(uci_get dns_mode fake-ip)
	allow_lan=$(uci_get allow_lan 1)
	log_level=$(uci_get log_level info)
	ipv6=$(uci_get ipv6 0)

	# If panel credentials provided, authenticate and get subscription URL
	if [ -n "$panel_url" ] && [ -n "$panel_username" ] && [ -n "$panel_password" ]; then
		local auth_url
		auth_url=$(get_subscription_url_via_auth "$panel_url" "$panel_username" "$panel_password")
		if [ -n "$auth_url" ]; then
			subscription_url="$auth_url"
		else
			logger -t mihomo "Failed to authenticate, falling back to provided subscription URL or local config"
		fi
	fi

	if [ -n "$subscription_url" ]; then
		download_subscription "$subscription_url"
	fi

	mkdir -p "$CONFIG_DIR"

	# Use subscription as base config if available
	if [ -f "$CONFIG_DIR/subscription.yaml" ] && [ -s "$CONFIG_DIR/subscription.yaml" ]; then
		cp "$CONFIG_DIR/subscription.yaml" "$CONFIG_FILE"
	elif [ ! -f "$CONFIG_FILE" ]; then
		# Create minimal default config with basic structure
		cat > "$CONFIG_FILE" <<-YAML
# Default minimal config - update via subscription URL
port: 7890
socks-port: 7891
mixed-port: 7890
mode: rule
allow-lan: true
log-level: info

proxies: []
proxy-groups:
  - name: DIRECT
    type: select
    proxies:
      - DIRECT

rules:
  - MATCH,DIRECT
YAML
	fi

	# Override config (mihomo merges this on top)
	cat > "$CONFIG_DIR/override.yaml" <<-YAML
mixed-port: $mixed_port
redir-port: $redir_port
tproxy-port: $tproxy_port
external-controller: 0.0.0.0:$api_port
$([ -n "$api_secret" ] && echo "secret: $api_secret")
external-ui: $UI_DIR
allow-lan: $([ "$allow_lan" = "1" ] && echo "true" || echo "false")
bind-address: "*"
mode: rule
log-level: $log_level
ipv6: $([ "$ipv6" = "1" ] && echo "true" || echo "false")
dns:
  enable: true
  listen: 0.0.0.0:$dns_port
  ipv6: $([ "$ipv6" = "1" ] && echo "true" || echo "false")
  enhanced-mode: $dns_mode
  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  nameserver:
    - https://doh.pub/dns-query
    - https://dns.alidns.com/dns-query
  fake-ip-filter:
    - "+.lan"
    - "+.local"
    - "+.localhost"
tun:
  enable: false
YAML

	return 0
}

setup_firewall() {
	local tproxy_port dns_port transparent_proxy dns_hijack
	tproxy_port=$(uci_get tproxy_port 7893)
	dns_port=$(uci_get dns_port 1053)
	transparent_proxy=$(uci_get transparent_proxy 1)
	dns_hijack=$(uci_get dns_hijack 1)

	cleanup_firewall

	[ "$transparent_proxy" != "1" ] && [ "$dns_hijack" != "1" ] && return 0

	logger -t mihomo "Setting up nftables rules..."

	nft -f - <<-NFT
table inet mihomo {
	set local_ipv4 {
		type ipv4_addr
		flags interval
		elements = {
			0.0.0.0/8,
			10.0.0.0/8,
			100.64.0.0/10,
			127.0.0.0/8,
			169.254.0.0/16,
			172.16.0.0/12,
			192.168.0.0/16,
			224.0.0.0/4,
			240.0.0.0/4
		}
	}
	chain prerouting {
		type filter hook prerouting priority mangle; policy accept;
		$([ "$dns_hijack" = "1" ] && echo "meta l4proto { tcp, udp } th dport 53 redirect to :$dns_port")
		$([ "$transparent_proxy" = "1" ] && cat <<-TPROXY
		ip daddr @local_ipv4 return
		meta l4proto tcp meta mark set 1 tproxy to :$tproxy_port accept
		meta l4proto udp meta mark set 1 tproxy to :$tproxy_port accept
		TPROXY
		)
	}
	chain output {
		type route hook output priority mangle; policy accept;
		$([ "$dns_hijack" = "1" ] && echo "meta l4proto { tcp, udp } th dport 53 meta mark set 1 accept")
	}
}
NFT

	if [ "$transparent_proxy" = "1" ]; then
		ip rule add fwmark 1 table 100 2>/dev/null
		ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
	fi

	logger -t mihomo "Firewall rules applied"
}

cleanup_firewall() {
	nft delete table inet mihomo 2>/dev/null
	ip rule del fwmark 1 table 100 2>/dev/null
	ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
}

start_service() {
	if ! generate_config; then
		logger -t mihomo "ERROR: Failed to generate config (service not enabled?)"
		return 1
	fi

	download_ui

	if [ ! -f "$CONFIG_FILE" ]; then
		logger -t mihomo "ERROR: Config file not created: $CONFIG_FILE"
		return 1
	fi

	logger -t mihomo "Starting mihomo service..."
	logger -t mihomo "Using config: $CONFIG_FILE"

	procd_open_instance mihomo
	procd_set_param command "$PROG" -d "$CONFIG_DIR"
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_set_param respawn 3600 5 5
	procd_set_param file "$CONFIG_FILE"
	procd_set_param limits nofile="65535 65535"
	procd_close_instance

	# Wait for core to start, then apply firewall
	sleep 2
	if pgrep -f "$PROG" >/dev/null; then
		logger -t mihomo "Service started successfully"
		setup_firewall
	else
		logger -t mihomo "ERROR: Mihomo process failed to start"
		return 1
	fi
}

stop_service() {
	logger -t mihomo "Stopping mihomo..."
	cleanup_firewall
}

service_triggers() {
	procd_add_reload_trigger "mihomo"
}

reload_service() {
	local subscription_url panel_url panel_username panel_password

	subscription_url=$(uci_get subscription_url)
	panel_url=$(uci_get panel_url)
	panel_username=$(uci_get panel_username)
	panel_password=$(uci_get panel_password)

	# If panel credentials provided, authenticate and get subscription URL
	if [ -n "$panel_url" ] && [ -n "$panel_username" ] && [ -n "$panel_password" ]; then
		local auth_url
		auth_url=$(get_subscription_url_via_auth "$panel_url" "$panel_username" "$panel_password")
		if [ -n "$auth_url" ]; then
			subscription_url="$auth_url"
		fi
	fi

	if [ -n "$subscription_url" ]; then
		[ "$(uci_get auto_update_subscription 1)" = "1" ] && \
			download_subscription "$subscription_url"
	fi

	generate_config || return 0
	local pid
	pid=$(pgrep -f "$PROG")
	[ -n "$pid" ] && kill -HUP "$pid"
	setup_firewall
}
