#!/usr/bin/env bash
set -euo pipefail

if [ "$(id -u)" -ne 0 ]; then
  echo "Start deze installer met sudo/root."
  exit 1
fi

DEFAULT_PORTAL_URL="${REMOTE_PLATFORM_URL:-https://remote.no-li.nl}"
INSTALL_VERSION="0.9.0"

cat >/tmp/rp_installer.py <<'END_RP_INSTALLER'
import json, os, platform, getpass, subprocess, urllib.request, urllib.error, time, ipaddress, shutil
from pathlib import Path

PORTAL_URL = os.environ.get("REMOTE_PLATFORM_URL", "https://remote.no-li.nl").rstrip("/")
INSTALL_VERSION = "0.9.0"
AGENT_BIN = Path("/usr/local/bin/gateway-agent")
CONFIG_DIR = Path("/etc/remote-platform/agent")
CONFIG_FILE = CONFIG_DIR / "config.json"
SERVICE_FILE = Path("/etc/systemd/system/remote-platform-agent.service")
OLD_SERVICE_FILE = Path("/etc/systemd/system/gateway-agent.service")
WG_CLIENT = Path("/etc/wireguard/rpwg-client.conf")

def ask(prompt, default=None, secret=False):
    suffix = f" [{default}]" if default not in (None, "") else ""
    v = getpass.getpass(f"{prompt}{suffix}: ") if secret else input(f"{prompt}{suffix}: ")
    v = v.strip()
    return v if v else (default or "")

def req(method, url, token=None, data=None):
    body = json.dumps(data).encode("utf-8") if data is not None else None
    r = urllib.request.Request(url, data=body, method=method)
    if token:
        r.add_header("Authorization", "Bearer " + token)
    if data is not None:
        r.add_header("Content-Type", "application/json")
    with urllib.request.urlopen(r) as x:
        payload = x.read()
        return json.loads(payload.decode("utf-8")) if payload else {}

def run(cmd, check=True):
    print("+", cmd)
    return subprocess.run(cmd, shell=True, check=check)

def output(cmd):
    try:
        return subprocess.check_output(cmd, shell=True, text=True, stderr=subprocess.DEVNULL)
    except Exception:
        return ""

def service_exists(name):
    return subprocess.run(f"systemctl list-unit-files {name} --no-legend 2>/dev/null | grep -q '^{name}'", shell=True).returncode == 0

def stop_services():
    for svc in ["remote-platform-agent.service", "gateway-agent.service", "wg-quick@rpwg-client.service"]:
        run(f"systemctl stop {svc} 2>/dev/null || true", check=False)

def disable_services():
    for svc in ["remote-platform-agent.service", "gateway-agent.service", "wg-quick@rpwg-client.service"]:
        run(f"systemctl disable {svc} 2>/dev/null || true", check=False)

def load_existing_config():
    if not CONFIG_FILE.exists():
        return {}
    try:
        return json.loads(CONFIG_FILE.read_text())
    except Exception:
        return {}

def backup_existing_config():
    if CONFIG_FILE.exists():
        backup = CONFIG_FILE.with_suffix(".json.backup-" + time.strftime("%Y%m%d-%H%M%S"))
        shutil.copy2(CONFIG_FILE, backup)
        print("Bestaande config backup:", backup)

def find_existing_gateway_id(cfg, token):
    gid = cfg.get("gateway_id") or cfg.get("gatewayId") or ""
    if gid:
        return gid
    serial = cfg.get("gateway_serial") or cfg.get("serial")
    if serial:
        try:
            gateways = req("GET", PORTAL_URL + "/gateways", token=token)
            for g in gateways:
                if g.get("serial") == serial:
                    return g.get("id") or ""
        except Exception as e:
            print("Kon gateway niet op serial zoeken:", e)
    return ""

def existing_markers():
    markers = []
    if AGENT_BIN.exists(): markers.append(str(AGENT_BIN))
    if CONFIG_FILE.exists(): markers.append(str(CONFIG_FILE))
    if SERVICE_FILE.exists() or service_exists("remote-platform-agent.service"): markers.append("remote-platform-agent.service")
    if OLD_SERVICE_FILE.exists() or service_exists("gateway-agent.service"): markers.append("gateway-agent.service")
    if WG_CLIENT.exists(): markers.append(str(WG_CLIENT))
    return markers

def remove_local_files(remove_config_dir=True):
    stop_services()
    disable_services()
    backup_existing_config()

    for f in [SERVICE_FILE, OLD_SERVICE_FILE]:
        if f.exists():
            f.unlink()

    if AGENT_BIN.exists():
        AGENT_BIN.unlink()

    if WG_CLIENT.exists():
        WG_CLIENT.unlink()

    if remove_config_dir and CONFIG_DIR.exists():
        shutil.rmtree(CONFIG_DIR, ignore_errors=True)

    run("systemctl daemon-reload", check=False)

def choose_existing_action(token):
    markers = existing_markers()
    cfg = load_existing_config()
    if not markers:
        return "new", cfg
    print("\nBestaande installatie gevonden:")
    for m in markers:
        print(" -", m)
    print("\nKies actie:")
    print("1. Herstellen/updaten bestaande installatie")
    print("2. Opnieuw installeren")
    print("3. Volledig verwijderen (ook uit portal indien gevonden)")
    print("4. Afbreken")
    choice = ask("Keuze", "1")
    if choice == "1":
        stop_services()
        if AGENT_BIN.exists(): AGENT_BIN.unlink()
        return "repair", cfg
    if choice == "2":
        remove_local_files(remove_config_dir=False)
        return "reinstall", cfg
    if choice == "3":
        gid = find_existing_gateway_id(cfg, token)
        if gid:
            confirm = ask(f"Type VERWIJDER om gateway {gid} ook uit portal te verwijderen", "")
            if confirm == "VERWIJDER":
                req("DELETE", PORTAL_URL + f"/gateways/{gid}", token=token)
                print("Gateway uit portal verwijderd:", gid)
        else:
            print("Geen bestaande gateway_id gevonden; portal verwijderen overgeslagen.")
        remove_local_files()
        raise SystemExit("Volledig verwijderd.")
    raise SystemExit("Afgebroken.")

def detect_subnets(include_advanced=False):
    out = output("ip -4 route show scope link")
    found = []
    for line in out.splitlines():
        parts = line.split()
        if not parts: continue
        net = parts[0]
        dev = ""
        if "dev" in parts:
            try: dev = parts[parts.index("dev")+1]
            except Exception: dev = ""
        if net == "default" or net.startswith("169.254.") or "/" not in net:
            continue
        if not include_advanced:
            if dev.startswith(("docker", "br-", "veth", "wg", "rpwg")): continue
            if net.startswith(("172.17.", "172.18.", "172.19.", "10.99.")): continue
        try:
            n = ipaddress.ip_network(net, strict=False)
        except Exception:
            continue
        if n.is_loopback or n.is_link_local: continue
        s = str(n)
        if s not in found: found.append(s)
    return found


def choose_reuse_gateway(old_cfg):
    gid = old_cfg.get("gateway_id") or old_cfg.get("gatewayId") or ""
    if not gid:
        return ""
    print("\nBestaande gateway gevonden in config:")
    print(gid)
    print("\nKies actie:")
    print("1. Deze bestaande gateway hergebruiken")
    print("2. Nieuwe gateway aanmaken")
    c = ask("Keuze", "1")
    if c == "1":
        return gid
    return ""

def choose_subnets():
    advanced = ask("Geavanceerde/VPN/Docker netwerken tonen? j/n", "n").lower().startswith("j")
    detected = detect_subnets(include_advanced=advanced)
    print("\nGevonden netwerken:")
    if detected:
        for i, net in enumerate(detected, 1): print(f"{i}. {net}")
    else:
        print("Geen bruikbare IPv4-linknetwerken gevonden.")
    selected = []
    if len(detected) == 1:
        choice = ask("Selecteer subnetnummer(s), komma-gescheiden", "1")
    elif len(detected) > 1:
        choice = ask("Selecteer subnetnummer(s), komma-gescheiden. Leeg = niets selecteren", "")
    else:
        choice = ""
    for item in [x.strip() for x in choice.split(",") if x.strip()]:
        selected.append(detected[int(item)-1])
    while True:
        manual = ask("Handmatig subnet toevoegen? voorbeeld 10.10.10.0/24, leeg = klaar", "")
        if not manual: break
        try: selected.append(str(ipaddress.ip_network(manual, strict=False)))
        except Exception as e: print("Ongeldig subnet:", e)
    out = []
    for s in selected:
        if s not in out: out.append(s)
    if not out: raise SystemExit("Er is geen subnet gekozen.")
    return out

def choose_org(options):
    orgs = options.get("organizations", [])
    if not orgs: raise SystemExit("Geen klanten beschikbaar voor deze gebruiker.")
    print("\nKlanten:")
    for i,o in enumerate(orgs,1): print(f"{i}. {o['name']}")
    if options.get("can_create_organization"): print("N. Nieuwe klant")
    c = ask("Kies klantnummer" + (" of N" if options.get("can_create_organization") else ""))
    if c.lower()=="n":
        if not options.get("can_create_organization"): raise SystemExit("Deze gebruiker mag geen klanten aanmaken.")
        return req("POST", PORTAL_URL+"/installer/organizations", token=TOKEN, data={"name":ask("Nieuwe klantnaam")})["id"]
    return orgs[int(c)-1]["id"]

def choose_site(options, org_id):
    sites = [s for s in options.get("sites", []) if s["organization_id"] == org_id]
    print("\nLocatie:")
    print("0. Geen locatie / direct onder klant")
    if len(sites)==1:
        print(f"1. {sites[0]['name']} (voorgesteld)")
        print("N. Nieuwe locatie")
        c = ask("Kies locatie", "1")
    elif len(sites)>1:
        for i,s in enumerate(sites,1): print(f"{i}. {s['name']}")
        print("N. Nieuwe locatie")
        c = ask("Kies locatie")
    else:
        print("Geen locaties gevonden.")
        print("N. Nieuwe locatie")
        c = ask("Kies 0 of N", "0")
    if c == "0": return None
    if c.lower()=="n":
        return req("POST", PORTAL_URL+"/installer/sites", token=TOKEN, data={"organization_id":org_id,"name":ask("Nieuwe locatienaam")})["id"]
    return sites[int(c)-1]["id"]


def configure_wayvnc_compat_if_needed():
    has_wayvnc = subprocess.run("command -v wayvnc >/dev/null 2>&1", shell=True).returncode == 0
    running = subprocess.run("pgrep -x wayvnc >/dev/null 2>&1", shell=True).returncode == 0
    if not has_wayvnc or not running:
        return

    print("\nwayvnc gedetecteerd.")
    print("Standaard wayvnc kan in noVNC de fout geven: Unsupported security types (types: 262).")
    print("Remote Platform kan wayvnc lokaal-only noVNC-compatible zetten:")
    print(" - bind op 127.0.0.1")
    print(" - poort 5900")
    print(" - auth uit, omdat toegang via gateway-tunnel loopt")
    choice = ask("wayvnc compatibel maken voor browser/noVNC? j/n", "j")
    if not choice.lower().startswith("j"):
        return

    script_url = PORTAL_URL + "/downloads/tools/fix-wayvnc-novnc.sh"
    tmp_script = "/tmp/fix-wayvnc-novnc.sh"
    try:
        urllib.request.urlretrieve(script_url, tmp_script)
        run(f"chmod +x {tmp_script}")
        run(f"{tmp_script}", check=False)
    except Exception as e:
        print("wayvnc compatibility stap mislukt:", e)


def install_service():
    SERVICE_FILE.write_text("""[Unit]
Description=Remote Platform Gateway Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/gateway-agent -config /etc/remote-platform/agent/config.json
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
""")
    run("systemctl daemon-reload")
    run("systemctl enable --now remote-platform-agent.service")

print("Remote Platform Gateway Installer", INSTALL_VERSION)
print("Portal:", PORTAL_URL)
print("\nInloggen")
username = ask("Gebruiker/email")
password = ask("Wachtwoord", secret=True)
try:
    login = req("POST", PORTAL_URL+"/installer/login", data={"username":username,"password":password})
except urllib.error.HTTPError as e:
    raise SystemExit(f"Login mislukt: HTTP {e.code}")
TOKEN = login["access_token"]

mode, old_cfg = choose_existing_action(token=TOKEN)
reuse_gateway_id = choose_reuse_gateway(old_cfg) if mode in ("repair", "reinstall") else ""

if reuse_gateway_id:
    org_id = None
    site_id = None
    gateway_name = ask("Gateway naam", platform.node())
else:
    options = req("GET", PORTAL_URL+"/installer/options", token=TOKEN)
    org_id = choose_org(options)
    options = req("GET", PORTAL_URL+"/installer/options", token=TOKEN)
    site_id = choose_site(options, org_id)
    gateway_name = ask("Gateway naam", platform.node())

subnets = choose_subnets()
primary_subnet = subnets[0]

print("\nSamenvatting:")
print("Actie:", mode)
print("Gateway:", gateway_name)
if reuse_gateway_id:
    print("Hergebruik gateway_id:", reuse_gateway_id)
else:
    print("Locatie:", site_id or "direct onder klant")
print("Subnets:", ", ".join(subnets))
if not ask("Installeren? j/n", "j").lower().startswith("j"):
    raise SystemExit("Afgebroken.")

arch = platform.machine().lower()
if arch in ("aarch64","arm64"): bin_url = PORTAL_URL+"/downloads/bin/gateway-agent-linux-arm64"
elif arch in ("x86_64","amd64"): bin_url = PORTAL_URL+"/downloads/bin/gateway-agent-linux-amd64"
else: raise SystemExit("Niet ondersteunde architectuur: "+arch)

run("apt-get update && apt-get install -y curl ca-certificates wireguard python3 iproute2")
tmp_bin = "/tmp/gateway-agent.download"
print("Agent downloaden:", bin_url)
urllib.request.urlretrieve(bin_url, tmp_bin)
run(f"install -m 0755 {tmp_bin} /usr/local/bin/gateway-agent")
run(f"rm -f {tmp_bin}", check=False)

if reuse_gateway_id:
    gw = {
        "id": reuse_gateway_id,
        "serial": old_cfg.get("gateway_serial") or old_cfg.get("serial") or ("GW-REUSE-" + reuse_gateway_id[:8])
    }
    cfg = dict(old_cfg)
    cfg["server_url"] = PORTAL_URL
    cfg["gateway_id"] = reuse_gateway_id
    cfg["machine_subnet"] = primary_subnet
    if "gateway_serial" not in cfg:
        cfg["gateway_serial"] = gw["serial"]
else:
    reg = req("POST", PORTAL_URL+"/installer/register-gateway", token=TOKEN, data={
        "organization_id": org_id,
        "site_id": site_id,
        "gateway_name": gateway_name,
        "machine_subnet": primary_subnet,
        "hostname": platform.node(),
        "os": "linux",
        "arch": arch,
        "installer_version": INSTALL_VERSION
    })
    gw = reg["gateway"]
    cfg = reg["agent_config"]
    cfg["server_url"] = PORTAL_URL
    cfg["gateway_id"] = gw["id"]

CONFIG_DIR.mkdir(parents=True, exist_ok=True)
CONFIG_FILE.write_text(json.dumps(cfg, indent=2))
run("chmod 600 /etc/remote-platform/agent/config.json")
install_service()
configure_wayvnc_compat_if_needed()

for net in subnets:
    try:
        req("POST", PORTAL_URL+f"/gateways/{gw['id']}/routes", token=TOKEN, data={"network":net,"description":"Selected during installer","source":"installer","published":True,"enabled":True})
    except Exception as e:
        print("Route registratie overgeslagen/fout:", net, e)

try: req("POST", PORTAL_URL+f"/gateways/{gw['id']}/vpn/generate-keys")
except Exception as e: print("Generate keys melding:", e)
time.sleep(8)

host = PORTAL_URL.replace("http://","").replace("https://","").split("/")[0].split(":")[0]
endpoint = os.environ.get("REMOTE_PLATFORM_VPN_ENDPOINT", host+":51820")
try:
    out = req("POST", PORTAL_URL+f"/gateways/{gw['id']}/vpn/bootstrap", data={"server_code":"primary","endpoint":endpoint,"transport_mode":"udp","machine_subnets":subnets})
    print(json.dumps(out, indent=2))
except Exception as e:
    print("VPN bootstrap melding:", e)

print("\nAgent status:")
subprocess.call("systemctl status remote-platform-agent.service --no-pager -l || true", shell=True)
print("\nKlaar.")
print("Gateway ID:", gw["id"])
print("Serial:", gw["serial"])
END_RP_INSTALLER

REMOTE_PLATFORM_URL="${DEFAULT_PORTAL_URL}" python3 /tmp/rp_installer.py
