#!/usr/bin/with-contenv bashio
# shellcheck shell=bash

set -euo pipefail

export PATH="/usr/local/sbin:/usr/local/bin:${PATH}"

# File where the real (non-VPN) public IP is stored
REAL_IP_FILE="/currentip"

# How often to re-check VPN IP (seconds)
VPN_CHECK_INTERVAL="${VPN_CHECK_INTERVAL:-300}"

# Service used to get IP + country (JSON)
VPN_INFO_URL="${VPN_INFO_URL:-https://ipinfo.io/json}"

# -------------------------------
# Helpers
# -------------------------------

read_real_ip() {
    # Reads the "real" IP saved before VPN start
    if [[ -f "${REAL_IP_FILE}" ]]; then
        # Strip whitespace/newlines just in case
        local ip
        ip="$(tr -d '[:space:]' < "${REAL_IP_FILE}")"
        if [[ -n "${ip}" ]]; then
            echo "${ip}"
            return 0
        fi
    fi
    # No baseline is not fatal: just return empty
    echo ""
    return 0
}

get_ip_info() {
    # Outputs: "<ip> <country>" on success
    local json ip country

    if ! json="$(curl -fsS --max-time 10 "${VPN_INFO_URL}" 2>/dev/null)"; then
        bashio::log.warning "Unable to reach VPN info service at ${VPN_INFO_URL}."
        return 1
    fi

    # Extract "ip" and "country" without jq
    ip="$(
        printf '%s\n' "${json}" \
            | sed -n 's/.*"ip"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
            | head -n1
    )"
    country="$(
        printf '%s\n' "${json}" \
            | sed -n 's/.*"country"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
            | head -n1
    )"

    if [[ -z "${ip}" ]]; then
        bashio::log.warning "Failed to parse IP from VPN info response."
        return 1
    fi

    printf '%s %s\n' "${ip}" "${country:-Unknown}"
}

wait_for_vpn_ip() {
    # Waits until:
    #  - we can read an external IP, AND
    #  - it is different from REAL_IP (if REAL_IP is known)
    # Outputs "<ip> <country>" when ready.
    local attempt out ip country

    for attempt in {1..20}; do
        if out="$(get_ip_info)"; then
            read -r ip country <<< "${out}"

            if [[ -n "${REAL_IP:-}" ]] && [[ "${ip}" == "${REAL_IP}" ]]; then
                bashio::log.warning \
                    "External IP still equals real IP (${ip}); VPN not ready yet (attempt ${attempt}/20)."
            else
                printf '%s %s\n' "${ip}" "${country}"
                return 0
            fi
        else
            bashio::log.warning "Unable to query external IP (attempt ${attempt}/20)."
        fi

        sleep 5
    done

    return 1
}

start_nginx_background() {
    bashio::log.info "Starting nginx..."
    nginx &
    echo $!
}

# -------------------------------
# Main logic
# -------------------------------

# Detect VPN mode(s)
vpn_openvpn=false
vpn_wireguard=false

if bashio::config.true 'openvpn_enabled'; then
    vpn_openvpn=true
fi

if bashio::config.true 'wireguard_enabled'; then
    vpn_wireguard=true
fi

# If both are enabled, that's a configuration error
if [[ "${vpn_openvpn}" == true && "${vpn_wireguard}" == true ]]; then
    bashio::log.error "Both OpenVPN and WireGuard are enabled. Only one VPN mode is supported."
    exit 1
fi

vpn_enabled=false
vpn_mode="none"

if [[ "${vpn_openvpn}" == true ]]; then
    vpn_enabled=true
    vpn_mode="OpenVPN"
fi

if [[ "${vpn_wireguard}" == true ]]; then
    vpn_enabled=true
    vpn_mode="WireGuard"
fi

if [[ "${vpn_enabled}" != true ]]; then
    # No VPN: just boot nginx, no IP leak monitoring
    bashio::log.info "No VPN enabled (OpenVPN/WireGuard). Starting nginx without IP monitoring."
    exec nginx
fi

# Read the baseline "real" IP from /currentip
REAL_IP="$(read_real_ip)"

if [[ -n "${REAL_IP}" ]]; then
    bashio::log.info "Real (non-VPN) IP from ${REAL_IP_FILE}: ${REAL_IP}"
else
    bashio::log.warning "Real IP file ${REAL_IP_FILE} missing or empty; IP leak detection will be less strict."
fi

bashio::log.info "VPN mode detected: ${vpn_mode}. Enabling IP leak protection and periodic monitoring."

# Wait until VPN is up and external IP != REAL_IP
if ! VPN_INFO_OUT="$(wait_for_vpn_ip)"; then
    bashio::log.error "Unable to obtain a VPN external IP different from real IP. Exiting to avoid leaking traffic."
    exit 1
fi

read -r VPN_IP VPN_COUNTRY <<< "${VPN_INFO_OUT}"

bashio::log.info "VPN external IP: ${VPN_IP} (${VPN_COUNTRY})"

# Start nginx in background and monitor
nginx_pid="$(start_nginx_background)"

# Forward termination signals
trap '
    bashio::log.info "Signal received, stopping nginx..."
    kill "'"${nginx_pid}"'" 2>/dev/null || true
    wait "'"${nginx_pid}"'" 2>/dev/null || true
    exit 0
' SIGTERM SIGINT SIGHUP

# Monitoring loop
while true; do
    # If nginx died, stop this service and let s6 handle restart policy
    if ! kill -0 "${nginx_pid}" 2>/dev/null; then
        bashio::log.error "nginx process exited unexpectedly; leaving service."
        exit 1
    fi

    sleep "${VPN_CHECK_INTERVAL}"

    if ! current_out="$(get_ip_info)"; then
        bashio::log.warning "Failed to refresh external IP; keeping previous assumptions."
        continue
    fi

    read -r current_ip current_country <<< "${current_out}"

    bashio::log.info "Current external IP: ${current_ip} (${current_country})"

    # If we know the real IP, treat equality as a leak
    if [[ -n "${REAL_IP}" ]] && [[ "${current_ip}" == "${REAL_IP}" ]]; then
        bashio::log.error "IP LEAK DETECTED: current external IP ${current_ip} matches real IP ${REAL_IP}."
        bashio::log.error "Stopping nginx and exiting so the supervisor can restart the add-on."
        kill "${nginx_pid}" 2>/dev/null || true
        wait "${nginx_pid}" 2>/dev/null || true
        exit 1
    fi
done
