"""Discover internal backends for each proxied hostname by tracing NAT rules.

For each public IP in the 72.194.62.x block, pull pfSense port forwards on 443
(and other ports if visible) and map them to internal LAN IPs:ports.
Also pull NPM hosts from Jupiter to map hostnames -> backend services.
"""
import json, os, re, subprocess
import paramiko, yaml

def _pwd(vault_path):
    r = subprocess.run(['sops','-d',vault_path], capture_output=True, text=True, timeout=30, check=True)
    return yaml.safe_load(r.stdout)['credentials']['password']

def ssh(host, user, pwd, port=22):
    c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    c.connect(host, port=port, username=user, password=pwd, timeout=30, look_for_keys=False, allow_agent=False)
    return c

def run(c, cmd, to=60):
    _, o, _ = c.exec_command(cmd, timeout=to)
    return o.read().decode('utf-8','replace')

# -----------------------------------------------------------------
print('=== [1] pfSense NAT rules: public 72.194.62.x -> internal ===')
pf_pwd = _pwd('D:/vault/infrastructure/pfsense-firewall.sops.yaml')
pf = ssh('172.16.0.1', 'admin', pf_pwd, port=2248)
# Pull rdr rules referencing each public IP on :443
out = run(pf, r'pfctl -s nat 2>/dev/null | grep -E "rdr on igc0 .*tcp.*72\.194\.62\.[0-9]+ port = (https|2083|2087|3389|3000|8000)" | sort -u | head -40')
print(out.strip())
print()

# -----------------------------------------------------------------
print('=== [2] Jupiter docker ps + NPM inspection for :4 traffic ===')
j_pwd = _pwd('D:/vault/infrastructure/jupiter-unraid-primary.sops.yaml')
j = ssh('172.16.3.20', 'root', j_pwd)

# NPM container: find its config file
out = run(j, 'docker ps --format "{{.Names}}\\t{{.Image}}\\t{{.Ports}}" | grep -iE "npm|nginx-proxy|proxy"')
print('-- NPM container --')
print(out.strip())
print()

# Find NPM hosts config (usually /data/nginx/proxy_host or in database)
out = run(j, 'ls /mnt/user/appdata/NginxProxyManager*/data/nginx/proxy_host/ 2>/dev/null | head')
print('-- NPM proxy_host configs --')
print(out.strip())
print()

# Show the first few proxy_host configs to extract hostname -> upstream mappings
out = run(j, r'''
for f in /mnt/user/appdata/NginxProxyManager-v3/data/nginx/proxy_host/*.conf /mnt/user/appdata/NginxProxyManager/data/nginx/proxy_host/*.conf 2>/dev/null; do
    if [ -f "$f" ]; then
        srv=$(grep -oP "server_name \K[^;]+" "$f" | head -1)
        ups=$(grep -oP "(proxy_pass|set \$server) \K[^;\"]+" "$f" | head -2 | tr '\n' '|')
        echo "$(basename $f): server=$srv  upstream=$ups"
    fi
done 2>/dev/null
''', to=60)
print('-- server_name -> upstream --')
print(out.strip())
print()

# Also dump docker ps for the services themselves
out = run(j, 'docker ps --format "{{.Names}}\\t{{.Ports}}" | head -30')
print('-- all docker containers + ports --')
print(out.strip())

pf.close(); j.close()
