#!/usr/bin/env bash
# wml-firstboot — multi-WAN / multi-LAN auto-detection
set -e
exec > >(tee -a /var/log/wml/firstboot.log) 2>&1
echo "===== $(date -Is) wml-firstboot ====="

[[ -f /var/lib/wml/firstboot-done ]] && { echo "already done"; exit 0; }

# CRITICAL: Drop a "started" marker IMMEDIATELY so that even if the service
# is killed/restarted mid-execution, the unit's ConditionPathExists check
# prevents a second invocation overlapping with us.
mkdir -p /var/lib/wml
echo "started $(date -Is)" > /var/lib/wml/firstboot-started


source /etc/wml/lan-default.conf 2>/dev/null || true
LAN_BASE_IP="${LAN_IP:-172.30.30.1}"
LAN_PREFIX="${LAN_PREFIX:-24}"
LAN_BASE_NET="${LAN_NETWORK:-172.30.30.0/24}"
LAN_BASE_OCTET=$(echo "$LAN_BASE_IP" | cut -d. -f3)
LAN_FAMILY=$(echo "$LAN_BASE_IP" | cut -d. -f1-2)
MAX_WANS=4

mapfile -t IFACES < <(
    for d in /sys/class/net/*; do
        n=$(basename "$d")
        case "$n" in lo|docker*|virbr*|veth*|tap*|wg*|br-*|tun*) continue ;; esac
        [[ -e "$d/device" ]] || continue
        echo "$n"
    done | sort
)
echo "Detected NICs: ${IFACES[*]:-(none)}"

echo "Waiting for carrier (up to 8s)..."
DL=$(( $(date +%s) + 8 ))
while [[ $(date +%s) -lt $DL ]]; do
    any=false
    for i in "${IFACES[@]}"; do
        ip link set "$i" up 2>/dev/null
        [[ "$(cat /sys/class/net/$i/carrier 2>/dev/null)" == "1" ]] && any=true
    done
    $any && break
    sleep 1
done
sleep 1

declare -a WANS=()
declare -a LANS=()

for i in "${IFACES[@]}"; do
    if [[ "$(cat /sys/class/net/$i/carrier 2>/dev/null)" != "1" ]]; then
        echo "  $i: no carrier -> LAN candidate"
        LANS+=("$i")
        continue
    fi
    echo "  $i: probing DHCP..."
    if timeout 6 dhclient -1 -v "$i" 2>/dev/null && \
       ip -o -4 addr show "$i" 2>/dev/null | grep -q "inet "; then
        lease=$(ip -o -4 addr show "$i" | awk '{print $4}' | cut -d/ -f1 | head -1)
        if [[ -n "$lease" && "$lease" =~ ^${LAN_FAMILY}\. ]]; then
            echo "    lease $lease inside LAN family - this is LAN side"
            dhclient -r "$i" 2>/dev/null || true
            LANS+=("$i")
        else
            echo "    got upstream lease $lease - WAN"
            if [[ ${#WANS[@]} -lt $MAX_WANS ]]; then
                WANS+=("$i")
            else
                echo "    WAN cap reached, demoting $i to LAN"
                dhclient -r "$i" 2>/dev/null || true
                LANS+=("$i")
            fi
        fi
    else
        echo "    no DHCP -> LAN candidate"
        LANS+=("$i")
    fi
done

# If NO NIC got upstream DHCP, leave WAN empty. All NICs stay as LAN candidates.
# This avoids mis-classifying a single LAN-side cable as WAN (Bug 33).
# wml-wan-monitor will continuously retry detection; user can manually
# assign via console option 1 if the upstream is static IP / PPPoE.
if [[ ${#WANS[@]} -eq 0 ]]; then
    echo "No upstream DHCP found on any NIC. All NICs treated as LAN."
    echo "wml-wan-monitor will retry; use console option 1 for manual assignment."
fi

echo "Final assignment: WANs=[${WANS[*]}]  LANs=[${LANS[*]}]"

mkdir -p /var/lib/wml
date -Is > /var/lib/wml/firstboot-done

mkdir -p /etc/systemd/network
rm -f /etc/systemd/network/10-wml-*.network /etc/systemd/network/20-wml-*.network 2>/dev/null

metric=100
for w in "${WANS[@]}"; do
    f=/etc/systemd/network/10-wml-wan-${w}.network
    echo "[Match]" > "$f"
    echo "Name=${w}" >> "$f"
    echo "" >> "$f"
    echo "[Network]" >> "$f"
    echo "DHCP=ipv4" >> "$f"
    echo "IPv6AcceptRA=true" >> "$f"
    echo "" >> "$f"
    echo "[DHCP]" >> "$f"
    echo "UseDNS=false" >> "$f"
    echo "UseDomains=false" >> "$f"
    echo "UseHostname=false" >> "$f"
    echo "RouteMetric=${metric}" >> "$f"
    metric=$((metric+10))
done

lan_idx=0
declare -a LAN_IPS=()
declare -a LAN_NETS=()
for l in "${LANS[@]}"; do
    octet=$((LAN_BASE_OCTET + lan_idx))
    lan_ip="${LAN_FAMILY}.${octet}.1"
    lan_net="${LAN_FAMILY}.${octet}.0/${LAN_PREFIX}"
    LAN_IPS+=("$lan_ip")
    LAN_NETS+=("$lan_net")
    f=/etc/systemd/network/20-wml-lan-${l}.network
    echo "[Match]" > "$f"
    echo "Name=${l}" >> "$f"
    echo "" >> "$f"
    echo "[Network]" >> "$f"
    echo "Address=${lan_ip}/${LAN_PREFIX}" >> "$f"
    echo "DHCPServer=no" >> "$f"
    echo "IPMasquerade=no" >> "$f"
    lan_idx=$((lan_idx+1))
done

systemctl enable systemd-networkd
systemctl restart --no-block systemd-networkd

{
    echo "# Multi-WAN/LAN assignment"
    echo "WAN_IFS=\"${WANS[*]}\""
    echo "LAN_IFS=\"${LANS[*]}\""
    echo "WAN_IF=${WANS[0]:-}"
    echo "LAN_IF=${LANS[0]:-}"
    echo "LAN_IP=${LAN_IPS[0]:-$LAN_BASE_IP}"
    echo "LAN_PREFIX=$LAN_PREFIX"
    echo "LAN_NETWORK=${LAN_NETS[0]:-$LAN_BASE_NET}"
    i=0
    for l in "${LANS[@]}"; do
        echo "LAN_IF_${i}=${l}"
        echo "LAN_IP_${i}=${LAN_IPS[$i]}"
        echo "LAN_NET_${i}=${LAN_NETS[$i]}"
        i=$((i+1))
    done
} > /etc/wml/interfaces.conf

if [[ ${#LANS[@]} -gt 0 ]]; then
    KEA=/etc/kea/kea-dhcp4.conf
    echo '{' > $KEA
    echo '  "Dhcp4": {' >> $KEA
    printf '    "interfaces-config": { "interfaces": [' >> $KEA
    first=1
    for l in "${LANS[@]}"; do
        [[ $first -eq 1 ]] || printf ',' >> $KEA
        printf '"%s"' "$l" >> $KEA
        first=0
    done
    echo '] },' >> $KEA
    echo '    "valid-lifetime": 86400,' >> $KEA
    echo '    "lease-database": { "type": "memfile", "persist": true, "name": "/var/lib/kea/dhcp4.leases" },' >> $KEA
    echo '    "subnet4": [' >> $KEA
    i=0
    for l in "${LANS[@]}"; do
        [[ $i -gt 0 ]] && echo ',' >> $KEA
        lan_ip="${LAN_IPS[$i]}"
        lan_net="${LAN_NETS[$i]}"
        octet=$((LAN_BASE_OCTET + i))
        pool_start="${LAN_FAMILY}.${octet}.100"
        pool_end="${LAN_FAMILY}.${octet}.200"
        echo '      {' >> $KEA
        echo "        \"id\": $((i+1))," >> $KEA
        echo "        \"subnet\": \"${lan_net}\"," >> $KEA
        echo "        \"pools\": [{ \"pool\": \"${pool_start} - ${pool_end}\" }]," >> $KEA
        echo '        "option-data": [' >> $KEA
        echo "          { \"name\": \"routers\", \"data\": \"${lan_ip}\" }," >> $KEA
        echo "          { \"name\": \"domain-name-servers\", \"data\": \"${lan_ip}\" }" >> $KEA
        echo '        ]' >> $KEA
        echo '      }' >> $KEA
        i=$((i+1))
    done
    echo '    ]' >> $KEA
    echo '  }' >> $KEA
    echo '}' >> $KEA
    systemctl unmask kea-dhcp4-server 2>/dev/null || true
    systemctl enable kea-dhcp4-server 2>/dev/null || true
    systemctl restart --no-block kea-dhcp4-server || true
fi

mkdir -p /var/lib/unbound
if command -v unbound-anchor >/dev/null && [[ ! -f /var/lib/unbound/root.key ]]; then
    unbound-anchor -a /var/lib/unbound/root.key || true
fi
chown -R unbound:unbound /var/lib/unbound 2>/dev/null || true
rm -f /etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf 2>/dev/null

UB=/etc/unbound/unbound.conf.d/wml.conf
echo 'server:' > $UB
echo '    interface: 127.0.0.1' >> $UB
echo '    interface: ::1' >> $UB
for ip in "${LAN_IPS[@]}"; do echo "    interface: ${ip}" >> $UB; done
echo '    access-control: 127.0.0.0/8 allow' >> $UB
echo '    access-control: ::1 allow' >> $UB
for net in "${LAN_NETS[@]}"; do echo "    access-control: ${net} allow" >> $UB; done
echo '    hide-identity: yes' >> $UB
echo '    hide-version: yes' >> $UB
echo '    module-config: "iterator"' >> $UB
echo '    do-not-query-localhost: no' >> $UB
echo '' >> $UB
echo 'forward-zone:' >> $UB
echo '    name: "."' >> $UB
echo '    forward-addr: 1.1.1.1' >> $UB
echo '    forward-addr: 1.0.0.1' >> $UB
echo '    forward-addr: 8.8.8.8' >> $UB

systemctl unmask unbound 2>/dev/null || true
systemctl enable unbound 2>/dev/null || true
# Wait up to 10s for the LAN IP to actually be present before unbound starts.
# Otherwise unbound fails with "cannot bind" and systemd shows red errors.
if [[ -n "${LAN_IPS[0]:-}" ]]; then
    for _ in 1 2 3 4 5 6 7 8 9 10; do
        if ip -4 addr show 2>/dev/null | grep -q "${LAN_IPS[0]}/"; then
            break
        fi
        sleep 1
    done
fi
systemctl restart --no-block unbound || true

ssh-keygen -A 2>/dev/null || true
systemctl unmask ssh 2>/dev/null || true
systemctl enable ssh 2>/dev/null || true
systemctl restart --no-block ssh || true

sysctl -w net.ipv4.ip_forward=1 2>/dev/null || true
sysctl -w net.ipv6.conf.all.forwarding=1 2>/dev/null || true

systemctl unmask nftables 2>/dev/null || true
systemctl enable nftables 2>/dev/null || true

if [[ ${#WANS[@]} -gt 0 ]]; then
    nft flush ruleset 2>/dev/null || true
    NF=/etc/nftables.conf
    echo '#!/usr/sbin/nft -f' > $NF
    echo 'flush ruleset' >> $NF
    echo '' >> $NF
    echo 'table ip wml-nat {' >> $NF
    echo '    chain postrouting {' >> $NF
    echo '        type nat hook postrouting priority 100; policy accept;' >> $NF
    for w in "${WANS[@]}"; do
        echo "        oifname \"${w}\" masquerade" >> $NF
    done
    echo '    }' >> $NF
    echo '}' >> $NF
    echo '' >> $NF
    echo 'table inet wml-filter {' >> $NF
    echo '    chain forward {' >> $NF
    echo '        type filter hook forward priority 0; policy accept;' >> $NF
    if [[ ${#LANS[@]} -gt 1 ]]; then
        for l1 in "${LANS[@]}"; do
            for l2 in "${LANS[@]}"; do
                [[ "$l1" == "$l2" ]] && continue
                echo "        iifname \"${l1}\" oifname \"${l2}\" drop" >> $NF
            done
        done
    fi
    echo '    }' >> $NF
    echo '}' >> $NF
    systemctl restart --no-block nftables 2>/dev/null || true
    echo "NAT enabled for ${#WANS[@]} WAN(s), inter-LAN isolation for ${#LANS[@]} LAN(s)"
fi

systemctl enable wml-wan-monitor.service 2>/dev/null || true
systemctl restart --no-block wml-wan-monitor.service 2>/dev/null || true

# --- 11a. Generate admin-UI TLS (mini local CA + leaf) ---
SSL_DIR=/etc/wml/ssl
CA_DIR=/var/lib/wml/ssl-ca
mkdir -p "$SSL_DIR" "$CA_DIR" /var/www/wml-public
chmod 700 "$SSL_DIR" "$CA_DIR"

if [[ ! -f "$CA_DIR/wml-ca.crt" ]]; then
    echo "[ssl] generating local CA"
    openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
        -keyout "$CA_DIR/wml-ca.key" \
        -out "$CA_DIR/wml-ca.crt" \
        -subj "/CN=WML OS Root CA/O=WML OS" \
        -addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
        -addext "keyUsage=critical,keyCertSign,cRLSign" 2>/dev/null
    chmod 600 "$CA_DIR/wml-ca.key"
    chmod 644 "$CA_DIR/wml-ca.crt"
fi

# Copy CA cert to a public-downloadable location so users can install it.
cp "$CA_DIR/wml-ca.crt" /var/www/wml-public/wml-ca.crt
chmod 644 /var/www/wml-public/wml-ca.crt

# Leaf cert for admin UI - regenerate every boot with current LAN IPs
SAN_ENTRIES="DNS:wml,DNS:wml.local,DNS:firewall,DNS:firewall.local,IP:127.0.0.1"
i=0
for ip in "${LAN_IPS[@]}"; do
    SAN_ENTRIES="${SAN_ENTRIES},IP:${ip}"
    i=$((i+1))
done

openssl req -newkey rsa:2048 -nodes \
    -keyout "$SSL_DIR/admin.key" \
    -out "$SSL_DIR/admin.csr" \
    -subj "/CN=${LAN_IPS[0]:-wml}/O=WML OS" 2>/dev/null

openssl x509 -req -in "$SSL_DIR/admin.csr" \
    -CA "$CA_DIR/wml-ca.crt" -CAkey "$CA_DIR/wml-ca.key" -CAcreateserial \
    -days 3650 -sha256 \
    -out "$SSL_DIR/admin.crt" \
    -extfile <(printf "subjectAltName=%s\nextendedKeyUsage=serverAuth\n" "$SAN_ENTRIES") 2>/dev/null

chmod 600 "$SSL_DIR/admin.key"
chmod 644 "$SSL_DIR/admin.crt"
rm -f "$SSL_DIR/admin.csr"

# --- 11b. nginx with HTTPS + HTTP-to-HTTPS redirect ---
cat > /etc/nginx/sites-available/wml-admin <<'NGINX_EOF'
# HTTP: serves only the CA cert download + redirects everything else to HTTPS
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    server_tokens off;

    # CA cert downloadable over plain HTTP so users can install it
    # before they trust the HTTPS cert.
    location = /wml-ca.crt {
        alias /var/www/wml-public/wml-ca.crt;
        default_type application/x-x509-ca-cert;
        add_header Content-Disposition 'attachment; filename="wml-ca.crt"';
    }

    # Everything else -> HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS: the actual admin UI + API
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    http2 on;
    server_name _;
    server_tokens off;
    client_max_body_size 16M;

    ssl_certificate     /etc/wml/ssl/admin.crt;
    ssl_certificate_key /etc/wml/ssl/admin.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5:!3DES;
    ssl_prefer_server_ciphers on;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    root /opt/wml-os/ui/dist;
    index index.html;

    location /api/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
        proxy_connect_timeout 60s;
    }

    # CA cert also reachable over HTTPS for convenience
    location = /wml-ca.crt {
        alias /var/www/wml-public/wml-ca.crt;
        default_type application/x-x509-ca-cert;
        add_header Content-Disposition 'attachment; filename="wml-ca.crt"';
    }

    location / { try_files $uri $uri/ /index.html; }
}
NGINX_EOF
ln -sf /etc/nginx/sites-available/wml-admin /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
systemctl restart --no-block nginx || true

systemctl restart --no-block wml-api || true

# --- ClamAV: ensure DB present, then start the daemon.
# freshclam may finish downloading the DB AFTER this point, so we spawn a
# background waiter that polls for the DB (up to 5 min) and starts clamd as
# soon as it's ready. Avoids the "condition unmet / inactive dead" state.
(
    for _ in $(seq 1 60); do
        if ls /var/lib/clamav/main.{cvd,cld} /var/lib/clamav/daily.{cvd,cld} 2>/dev/null | head -1 >/dev/null; then
            systemctl restart clamav-daemon 2>/dev/null || true
            break
        fi
        # Kick freshclam if DB still missing
        systemctl start clamav-freshclam 2>/dev/null || true
        sleep 5
    done
) >/dev/null 2>&1 &

# --- Suricata: try to start now (in case the user/group fix in chroot hook
# means it can finally come up).
systemctl restart --no-block suricata 2>/dev/null || true


echo "===== wml-firstboot complete: ${#WANS[@]} WAN(s), ${#LANS[@]} LAN(s) ====="
