#!/bin/bash

# Upload a certificate to the NetSet API and assign it to one or more interfaces.
#
# The API uses TWO steps, which this script performs for you:
#   1. POST /api/network/certs                              -> upload the cert (multipart), returns certID
#   2. PUT  /api/network/interfaces/<interface>/certificate -> assign certID + fqdn to an interface
#      (repeated once per interface)
#
# The certificate is uploaded ONCE, then assigned to each given interface with its own fqdn.
# This covers a single cert serving several interfaces (e.g. a wildcard cert with a different
# fqdn per interface). If you need a DIFFERENT certificate per interface, run this script once
# per certificate.
#
# Usage: ./upload_cert.sh <cert_file> <key_file> <ca_file> <endpoint> <iface:fqdn> [<iface:fqdn> ...]
# Environment: WEBADMIN_API_KEY must be set
# Requirements: bash + curl (plus coreutils: grep, sed, cut, tr). No jq/awk/python needed.
#               On minimal systems (e.g. Alpine), install bash first: apk add bash curl

set -e

print_usage() {
    echo "Usage: $0 <cert_file> <key_file> <ca_file> <endpoint> <iface:fqdn> [<iface:fqdn> ...]"
    echo ""
    echo "Arguments:"
    echo "  cert_file    Path to the certificate file (.crt)"
    echo "  key_file     Path to the private key file (.key)"
    echo "  ca_file      Path to the CA certificate file (.ca)"
    echo "  endpoint     Target IP/domain (e.g., 192.168.1.100 or example.com)"
    echo "  iface:fqdn   Interface and the fqdn to serve on it, joined by a colon."
    echo "               Interface must be one of: officeLan, surfLan"
    echo "               The fqdn must be covered by the certificate (a SAN entry,"
    echo "               or match a wildcard SAN). Pass one or more pairs."
    echo ""
    echo "Environment:"
    echo "  WEBADMIN_API_KEY   API key for authorization (required)"
    echo "  WEBADMIN_INSECURE  Set to any value to skip TLS certificate verification"
    echo "                     (use for dev/self-signed hosts; implied for IP endpoints)"
    echo ""
    echo "Examples:"
    echo "  export WEBADMIN_API_KEY=my_api_key_123"
    echo "  # wildcard cert, both interfaces, different fqdn each:"
    echo "  $0 wild.crt wild.key ca.crt 192.168.1.100 surfLan:portal.example.com officeLan:admin.example.com"
    echo "  # single interface:"
    echo "  $0 server.crt server.key ca.crt 192.168.1.100 surfLan:portal.example.com"
}

# Need at least: cert, key, ca, endpoint, and one iface:fqdn pair
if [ $# -lt 5 ]; then
    print_usage
    exit 1
fi

CERT_FILE="$1"
KEY_FILE="$2"
CA_FILE="$3"
ENDPOINT="$4"
shift 4
PAIRS=("$@")   # remaining args are the iface:fqdn pairs

# Get API key from environment variable
API_KEY="$WEBADMIN_API_KEY"
if [ -z "$API_KEY" ]; then
    echo "Error: WEBADMIN_API_KEY environment variable is not set"
    echo "Please set it with: export WEBADMIN_API_KEY=your_api_key"
    exit 1
fi

# Validate that all files exist
for file in "$CERT_FILE" "$KEY_FILE" "$CA_FILE"; do
    if [ ! -f "$file" ]; then
        echo "Error: File '$file' does not exist or is not readable"
        exit 1
    fi
done

# Normalize an interface name to the exact casing the API expects (officeLan / surfLan)
normalize_interface() {
    case "$(echo "$1" | tr '[:upper:]' '[:lower:]')" in
        officelan) echo "officelan" ;;
        surflan)   echo "surflan" ;;
        *)         echo "" ;;
    esac
}

# Validate ALL pairs up front so we never upload a cert we then can't assign.
# Build parallel arrays of normalized interfaces and their fqdns.
IFACES=()
FQDNS=()
for pair in "${PAIRS[@]}"; do
    raw_iface="${pair%%:*}"   # part before the first colon
    fqdn="${pair#*:}"         # part after the first colon

    if [ "$raw_iface" = "$pair" ] || [ -z "$fqdn" ]; then
        echo "Error: invalid pair '$pair' - expected format <iface:fqdn> (e.g. surfLan:portal.example.com)"
        exit 1
    fi

    iface="$(normalize_interface "$raw_iface")"
    if [ -z "$iface" ]; then
        echo "Error: unknown interface '$raw_iface' in '$pair' - expected officeLan or surfLan"
        exit 1
    fi

    IFACES+=("$iface")
    FQDNS+=("$fqdn")
done

# Check if endpoint is an IP address (basic IPv4 validation)
is_ip() {
    [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
}

# Use an insecure connection for IP endpoints (self-signed / no matching hostname)
CURL_OPTS=""
if is_ip "$ENDPOINT"; then
    echo "Detected IP address - using insecure connection"
    CURL_OPTS="--insecure"
elif [ -n "$WEBADMIN_INSECURE" ]; then
    echo "WEBADMIN_INSECURE set - skipping TLS certificate verification"
    CURL_OPTS="--insecure"
fi

API_BASE="https://$ENDPOINT/api/network"
echo "Using API key: ${API_KEY:0:4}..."

# Run a curl request and capture its result.
# On a curl-level failure (e.g. TLS verification error, exit 60) it prints the
# error and returns non-zero instead of letting 'set -e' kill us silently.
# On success it sets HTTP_STATUS and RESPONSE_BODY.
http_request() {
    local ec=0
    RESPONSE=$(curl $CURL_OPTS -sS -w $'\nHTTP_STATUS:%{http_code}' "$@" 2>&1) || ec=$?
    if [ "$ec" -ne 0 ]; then
        echo "ERROR: curl failed (exit $ec):" >&2
        echo "$RESPONSE" >&2
        if [ "$ec" -eq 60 ]; then
            echo "Hint: TLS certificate verification failed. For a dev/self-signed host," >&2
            echo "      re-run with WEBADMIN_INSECURE=1 to skip verification." >&2
        fi
        return "$ec"
    fi
    HTTP_STATUS=$(echo "$RESPONSE" | grep "HTTP_STATUS:" | cut -d: -f2)
    RESPONSE_BODY=$(echo "$RESPONSE" | sed '/HTTP_STATUS:/d')
}

# ---------------------------------------------------------------------------
# Step 1: Upload the certificate (multipart POST) - done ONCE
# ---------------------------------------------------------------------------
UPLOAD_URL="$API_BASE/certs"
echo ""
echo "[1/2] Uploading certificate to: $UPLOAD_URL"

http_request \
    -X POST \
    -H "Authorization: ApiKey $API_KEY" \
    -F "cert=@$CERT_FILE" \
    -F "key=@$KEY_FILE" \
    -F "ca=@$CA_FILE" \
    "$UPLOAD_URL" || exit 1

echo "Response Status: $HTTP_STATUS"
if [ "$HTTP_STATUS" != "200" ]; then
    echo "Certificate upload failed!"
    echo "Response: $RESPONSE_BODY"
    exit 1
fi

# Extract the certID from the JSON response: {"message":"...","certID":"<serial>"}
CERT_ID=$(echo "$RESPONSE_BODY" | grep -o '"certID"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:[[:space:]]*"\([^"]*\)".*/\1/')
if [ -z "$CERT_ID" ]; then
    echo "Certificate uploaded, but could not parse certID from response:"
    echo "Response: $RESPONSE_BODY"
    exit 1
fi
echo "Certificate upload successful! certID: $CERT_ID"

# ---------------------------------------------------------------------------
# Step 2: Assign the certificate to each interface (one PUT per pair)
# ---------------------------------------------------------------------------
echo ""
echo "[2/2] Assigning certificate $CERT_ID to ${#IFACES[@]} interface(s)..."

for i in "${!IFACES[@]}"; do
    iface="${IFACES[$i]}"
    fqdn="${FQDNS[$i]}"
    ASSIGN_URL="$API_BASE/interfaces/$iface/certificate"
    ASSIGN_PAYLOAD="{\"certID\": \"$CERT_ID\", \"fqdn\": \"$fqdn\"}"

    echo "  - $iface (fqdn: $fqdn): PUT $ASSIGN_URL"
    http_request \
        -X PUT \
        -H "Content-Type: application/json" \
        -H "Authorization: ApiKey $API_KEY" \
        -d "$ASSIGN_PAYLOAD" \
        "$ASSIGN_URL" || exit 1

    if [ "$HTTP_STATUS" = "200" ]; then
        echo "    OK ($HTTP_STATUS): $RESPONSE_BODY"
    else
        echo "    FAILED ($HTTP_STATUS): $RESPONSE_BODY"
        exit 1
    fi
done

echo ""
echo "Done. Certificate $CERT_ID assigned to: ${IFACES[*]}"
