#!/bin/sh
set -u

STATE_DIR="/etc/peervpn/state"
SESSION_FILE="$STATE_DIR/session.json"
DEVICE_FILE="$STATE_DIR/device.json"
LIONVPN="/usr/bin/lionvpn"

run_capture() {
	set +e
	output="$("$@" 2>&1)"
	rc=$?
	set +e
	printf '%s\n' "$output"
	printf 'exit_code: %s\n' "$rc"
	return 0
}

run_lionvpn() {
	"$LIONVPN" -session "$SESSION_FILE" -device "$DEVICE_FILE" "$@"
}

validate_nonempty() {
	name="$1"
	value="$2"
	if [ -z "$value" ]; then
		printf '%s is required\n' "$name" >&2
		return 1
	fi
}

validate_plain_value() {
	name="$1"
	value="$2"
	if printf '%s' "$value" | grep -q '[[:cntrl:]]'; then
		printf '%s contains control characters\n' "$name" >&2
		return 1
	fi
}

validate_target() {
	case "$1" in
		''|*[!A-Za-z0-9._-]*)
			printf 'invalid target: %s\n' "$1" >&2
			return 1
			;;
	esac
}

validate_access_host() {
	case "$1" in
		''|*[!A-Za-z0-9._-]*|.*|*.)
			printf 'invalid VPN host: %s\n' "$1" >&2
			return 1
			;;
	esac
}

validate_access_port() {
	case "$1" in
		''|*[!0-9]*)
			printf 'invalid VPN port: %s\n' "$1" >&2
			return 1
			;;
	esac
	if [ "$1" -lt 1 ] || [ "$1" -gt 65535 ]; then
		printf 'invalid VPN port: %s\n' "$1" >&2
		return 1
	fi
}

validate_access_protocol() {
	case "$1" in
		socks|socks5|vless)
			return 0
			;;
		*)
			printf 'invalid access protocol: %s\n' "$1" >&2
			return 1
			;;
	esac
}

connect_args() {
	target="$1"
	case "$target" in
		''|recommended)
			return 0
			;;
		*.*)
			country="${target%%.*}"
			name="${target#*.}"
			case "$name" in
				chatgpt|streaming)
					printf '%s\n' --group "$name" "$country"
					;;
				*)
					printf '%s\n' --city "$name" "$country"
					;;
			esac
			;;
		*)
			printf '%s\n' "$target"
			;;
	esac
}

print_config() {
	access_url="$(uci -q get lionvpn.main.access_url 2>/dev/null || true)"
	access_protocol="$(uci -q get lionvpn.main.access_protocol 2>/dev/null || true)"
	[ -n "$access_protocol" ] || access_protocol="socks"
	printf 'access_url: %s\n' "$access_url"
	printf 'access_protocol: %s\n' "$access_protocol"
	printf 'exit_code: 0\n'
}

login_account() {
	account="$1"
	password="$2"
	auth_url="${3:-}"

	validate_nonempty account "$account" || return 2
	validate_nonempty password "$password" || return 2
	validate_plain_value account "$account" || return 2
	if [ -n "$auth_url" ]; then
		validate_plain_value auth_url "$auth_url" || return 2
		printf '%s\n' "$password" | run_lionvpn login --account "$account" --password-stdin --auth-url "$auth_url"
		return $?
	fi
	printf '%s\n' "$password" | run_lionvpn login --account "$account" --password-stdin
}

logout_account() {
	set +e
	output="$(run_lionvpn logout 2>&1)"
	rc=$?
	printf '%s\n' "$output"
	if [ -f "$SESSION_FILE" ]; then
		mv "$SESSION_FILE" "$SESSION_FILE.logout.bak" 2>/dev/null || rm -f "$SESSION_FILE"
		if [ "$rc" -ne 0 ]; then
			printf 'warning: remote logout failed; local session was cleared\n'
		else
			printf 'local session cleared\n'
		fi
	fi
	return 0
}

set_access_config() {
	host="${1:-}"
	port="${2:-}"
	protocol="${3:-socks}"

	[ -n "$protocol" ] || protocol="socks"
	validate_access_protocol "$protocol" || return 2

	if [ -z "$host" ] && [ -z "$port" ]; then
		uci -q delete lionvpn.main.access_url 2>/dev/null || true
		uci -q set lionvpn.main.access_protocol="$protocol"
		uci -q commit lionvpn
		/etc/init.d/lionvpn restart >/dev/null 2>&1 || true
		printf 'access_url: \n'
		printf 'access_protocol: %s\n' "$protocol"
		printf 'message: automatic VPN backend selection enabled\n'
		return 0
	fi

	[ -n "$port" ] || port="18082"
	validate_access_host "$host" || return 2
	validate_access_port "$port" || return 2

	access_url="https://$host:$port"
	uci -q set lionvpn.main.access_url="$access_url"
	uci -q set lionvpn.main.access_protocol="$protocol"
	uci -q commit lionvpn
	/etc/init.d/lionvpn restart >/dev/null 2>&1 || true
	printf 'access_url: %s\n' "$access_url"
	printf 'access_protocol: %s\n' "$protocol"
	printf 'message: VPN backend saved\n'
}

action="${1:-status}"
case "$action" in
	status)
		run_capture "$LIONVPN" status
		;;
	doctor)
		run_capture "$LIONVPN" doctor
		;;
	account)
		run_capture run_lionvpn account
		;;
	config)
		print_config
		;;
	login)
		run_capture login_account "${2:-}" "${3:-}" "${4:-}"
		;;
	logout)
		run_capture logout_account
		;;
	servers)
		run_capture run_lionvpn servers
		;;
	refresh)
		run_capture run_lionvpn refresh
		;;
	connect)
		target="${2:-recommended}"
		if ! validate_target "$target"; then
			printf 'exit_code: 2\n'
			exit 0
		fi
		/etc/init.d/lionvpn start >/dev/null 2>&1 || true
		set -- $(connect_args "$target")
		run_capture run_lionvpn connect "$@"
		;;
	disconnect)
		run_capture "$LIONVPN" disconnect
		;;
	cleanup)
		run_capture "$LIONVPN" cleanup
		;;
	set_access)
		run_capture set_access_config "${2:-}" "${3:-}" "${4:-socks}"
		;;
	service_start)
		run_capture /etc/init.d/lionvpn start
		;;
	service_stop)
		run_capture /etc/init.d/lionvpn stop
		;;
	logs)
		printf 'logs: disabled\n'
		printf 'exit_code: 0\n'
		;;
	*)
		printf 'unknown action: %s\n' "$action"
		printf 'exit_code: 2\n'
		;;
esac
