#!/bin/sh

set -eu

readonly AUTH_FILE=/var/lib/nemesis-web/admin.json
readonly BOOTSTRAP_FILE=/var/lib/nemesis-web/bootstrap.json
readonly COMMISSIONED_FILE=/etc/nemesis/commissioned.json
readonly AGREEMENT_FILE=/usr/share/nemesis/legal/SOFTWARE-LICENSE-AGREEMENT.txt
readonly TERMS_ID_FILE=/usr/share/nemesis/legal/TERMS-ID
readonly TLS_DIRECTORY=/var/lib/nemesis-web/tls
readonly WEB_ENVIRONMENT=/etc/nemesis/web.env
readonly WEB_DROPIN_DIRECTORY=/etc/systemd/system/nemesis-web.service.d
readonly WEB_NETWORK_DROPIN="$WEB_DROPIN_DIRECTORY/10-nemesis-lan.conf"
readonly WEB_SERVICE=nemesis-web.service
readonly LOCAL_SERVICE=nemesis-local.service
readonly COMMISSION_PATH=nemesis-commission.path
readonly PACKAGED_WEB_UNIT=/usr/lib/systemd/system/nemesis-web.service
readonly PACKAGED_LOCAL_UNIT=/usr/lib/systemd/system/nemesis-local.service
readonly SYSTEM_UNIT_DIRECTORY=/etc/systemd/system
readonly LEGACY_UNIT_BACKUP_DIRECTORY=/var/backups/nemesis/archive-systemd
readonly LEGACY_WEB_UNIT_SHA256=90e177be2a25f05c437fc9099bc8b0227461097b09c2c16fe24af1e345264ce8
readonly LEGACY_LOCAL_UNIT_SHA256=1563a145d8cf752843cb8cd26d9094f1825c60b76847db6ba47573bb92234883

fail() {
    printf 'nemesis-bootstrap-token: %s\n' "$*" >&2
    exit 1
}

migrate_known_archive_unit() {
    unit="$1"
    known_sha256="$2"
    legacy_unit="$SYSTEM_UNIT_DIRECTORY/$unit"
    backup_unit="$LEGACY_UNIT_BACKUP_DIRECTORY/$unit"

    [ -e "$legacy_unit" ] || return 0
    [ -f "$legacy_unit" ] && [ ! -L "$legacy_unit" ] || return 0
    actual_sha256="$(sha256sum -- "$legacy_unit")" \
        || fail "could not inspect legacy system service: $legacy_unit"
    actual_sha256="${actual_sha256%% *}"
    [ "$actual_sha256" = "$known_sha256" ] || return 0
    [ ! -e "$backup_unit" ] && [ ! -L "$backup_unit" ] \
        || fail "legacy unit backup already exists: $backup_unit"

    install -d -o root -g root -m 0700 "$LEGACY_UNIT_BACKUP_DIRECTORY"
    mv -- "$legacy_unit" "$backup_unit"
    printf 'nemesis-bootstrap-token: archived stock legacy unit %s at %s\n' \
        "$legacy_unit" "$backup_unit"
}

require_packaged_unit() {
    unit="$1"
    expected_fragment="$2"
    fragment="$(
        systemctl show "$unit" --property=FragmentPath --value 2>/dev/null
    )" || fail "could not inspect system service: $unit"
    [ "$fragment" = "$expected_fragment" ] || fail \
        "$fragment shadows packaged unit $expected_fragment; move or remove the previous archive-install unit and retry"
}

if [ "$(id -u)" -ne 0 ]; then
    fail "run this command through sudo or as root"
fi

for command in awk chmod chown install ip mktemp mv rm runuser sha256sum systemctl; do
    command -v "$command" >/dev/null 2>&1 || fail "required command is unavailable: $command"
done

# The retired archive installer placed whole units in /etc/systemd/system while the
# Debian package correctly owns them beneath /usr/lib. An archive unit silently wins
# systemd's precedence rules and points at /usr/local binaries. Preserve exact stock
# units automatically, but leave modified or otherwise unknown administrator-owned
# units untouched and fail closed before creating TLS identity or enabling services.
migrate_known_archive_unit "$WEB_SERVICE" "$LEGACY_WEB_UNIT_SHA256"
migrate_known_archive_unit "$LOCAL_SERVICE" "$LEGACY_LOCAL_UNIT_SHA256"
systemctl daemon-reload
require_packaged_unit "$WEB_SERVICE" "$PACKAGED_WEB_UNIT"
require_packaged_unit "$LOCAL_SERVICE" "$PACKAGED_LOCAL_UNIT"

for asset in "$AGREEMENT_FILE" "$TERMS_ID_FILE"; do
    [ -f "$asset" ] && [ ! -L "$asset" ] \
        || fail "required commissioning package file is unavailable: $asset; reinstall or upgrade Nemesis"
    runuser -u nemesis-web -- test -r "$asset" \
        || fail "required commissioning package file is unreadable: $asset; reinstall or upgrade Nemesis"
done

if [ -e "$COMMISSIONED_FILE" ] || [ -L "$COMMISSIONED_FILE" ]; then
    [ -f "$COMMISSIONED_FILE" ] && [ ! -L "$COMMISSIONED_FILE" ] \
        || fail "commissioning marker is unsafe; run 'sudo -u nemesis /usr/bin/nemesis doctor'"
    [ -f "$AUTH_FILE" ] && [ ! -L "$AUTH_FILE" ] \
        || fail "commissioned state has no administrator; run 'sudo -u nemesis /usr/bin/nemesis doctor'"
    [ -f "$WEB_ENVIRONMENT" ] && [ ! -L "$WEB_ENVIRONMENT" ] \
        || fail "commissioned HTTPS configuration is unavailable; run 'sudo -u nemesis /usr/bin/nemesis doctor'"

    configured_listen="$(
        awk -F= '$1 == "NEMESIS_WEB_LISTEN" { print $2; exit }' "$WEB_ENVIRONMENT"
    )"
    [ -n "$configured_listen" ] \
        || fail "commissioned HTTPS address is unavailable; run 'sudo -u nemesis /usr/bin/nemesis doctor'"

    printf '\n'
    printf '%s\n' '==================== Nemesis is already active ==================='
    printf '%s\n' 'Administrator enrollment and appliance activation are complete.'
    printf '%s\n' 'No bootstrap token is needed.'
    printf 'Open Nemesis:      https://%s/\n' "$configured_listen"
    printf '%s\n' 'If the page is unavailable, run:'
    printf '%s\n' '  sudo -u nemesis /usr/bin/nemesis doctor'
    printf '%s\n' '=================================================================='
    printf '\n'
    exit 0
fi

if [ -e "$AUTH_FILE" ]; then
    fail 'administrator enrollment is already complete; open the existing Nemesis HTTPS address, sign in, and finish "Activate Nemesis"'
fi

if { [ -e "$TLS_DIRECTORY" ] && [ ! -e "$WEB_ENVIRONMENT" ]; } \
    || { [ ! -e "$TLS_DIRECTORY" ] && [ -e "$WEB_ENVIRONMENT" ]; }; then
    fail "partial HTTPS state requires recovery; existing identity material was preserved"
fi

if [ ! -e "$TLS_DIRECTORY" ]; then
    default_interface="$(
        ip -o -4 route show default | awk '
            {
                interface = ""
                metric = 0
                for (field = 1; field <= NF; field++) {
                    if ($field == "dev") interface = $(field + 1)
                    if ($field == "metric") metric = $(field + 1)
                }
                if (interface != "" && (selected == "" || metric < best_metric)) {
                    selected = interface
                    best_metric = metric
                }
            }
            END { print selected }
        '
    )"
    address_with_prefix=""
    if [ -n "$default_interface" ]; then
        address_with_prefix="$(
            ip -o -4 address show dev "$default_interface" scope global \
                | awk 'NR == 1 { print $4 }'
        )"
    fi
    if [ -z "$address_with_prefix" ]; then
        address_with_prefix="$(
            ip -o -4 address show scope global | awk 'NR == 1 { print $4 }'
        )"
    fi
    [ -n "$address_with_prefix" ] \
        || fail "no assigned private IPv4 address was detected; connect the management network and retry"
    web_address="${address_with_prefix%/*}"

    /usr/bin/nemesis-web init-tls \
        --directory "$TLS_DIRECTORY" \
        --ip-address "$web_address" \
        >/dev/null

    chown root:nemesis-web "$TLS_DIRECTORY"
    chmod 0750 "$TLS_DIRECTORY"
    chown root:nemesis-web \
        "$TLS_DIRECTORY/server.crt" \
        "$TLS_DIRECTORY/server.key" \
        "$TLS_DIRECTORY/manifest.json"
    chmod 0640 \
        "$TLS_DIRECTORY/server.crt" \
        "$TLS_DIRECTORY/server.key" \
        "$TLS_DIRECTORY/manifest.json"
    if [ -e "$TLS_DIRECTORY/local-ca.key" ]; then
        chown root:root "$TLS_DIRECTORY/local-ca.key" "$TLS_DIRECTORY/local-ca.crt"
        chmod 0600 "$TLS_DIRECTORY/local-ca.key"
        chmod 0644 "$TLS_DIRECTORY/local-ca.crt"
    fi

    environment_pending="$(mktemp /etc/nemesis/.web.env.XXXXXX)"
    dropin_pending="$(mktemp /etc/nemesis/.web-lan.XXXXXX)"
    trap 'rm -f -- "$environment_pending" "$dropin_pending"' EXIT
    chmod 0600 "$environment_pending" "$dropin_pending"
    printf 'NEMESIS_WEB_LISTEN=%s:8443\n' "$web_address" > "$environment_pending"
    printf 'NEMESIS_WEB_NETWORK=%s\n' "$address_with_prefix" >> "$environment_pending"
    printf '%s\n' \
        'NEMESIS_WEB_TLS_ARGS=--tls-certificate /var/lib/nemesis-web/tls/server.crt --tls-private-key /var/lib/nemesis-web/tls/server.key' \
        >> "$environment_pending"
    printf '[Service]\nIPAddressAllow=%s\n' "$address_with_prefix" > "$dropin_pending"
    install -d -o root -g root -m 0755 "$WEB_DROPIN_DIRECTORY"
    install -o root -g root -m 0600 "$environment_pending" "$WEB_ENVIRONMENT"
    install -o root -g root -m 0644 "$dropin_pending" "$WEB_NETWORK_DROPIN"
    rm -f -- "$environment_pending" "$dropin_pending"
    trap - EXIT
else
    [ -d "$TLS_DIRECTORY" ] && [ ! -L "$TLS_DIRECTORY" ] \
        || fail "existing TLS identity must be a real directory"
    [ -f "$WEB_ENVIRONMENT" ] && [ ! -L "$WEB_ENVIRONMENT" ] \
        || fail "existing web environment must be a regular file"
    [ -f "$WEB_NETWORK_DROPIN" ] && [ ! -L "$WEB_NETWORK_DROPIN" ] \
        || fail "existing HTTPS network policy is unavailable"
fi

configured_listen="$(
    awk -F= '$1 == "NEMESIS_WEB_LISTEN" { print $2; exit }' "$WEB_ENVIRONMENT"
)"
[ -n "$configured_listen" ] || fail "configured HTTPS listener is unavailable"

systemctl enable --now "$COMMISSION_PATH" "$WEB_SERVICE" >/dev/null
systemctl is-active --quiet "$COMMISSION_PATH" \
    || fail "privileged commissioning listener did not become active"
systemctl is-active --quiet "$WEB_SERVICE" \
    || fail "protected first-run web service did not become active"

printf '\n'
printf '%s\n' '====================== Nemesis secure setup ======================'
runuser -u nemesis-web -- /usr/bin/nemesis-web bootstrap-token \
    --auth-file "$AUTH_FILE" \
    --bootstrap-file "$BOOTSTRAP_FILE" \
    --setup-url "https://$configured_listen/setup" \
    --tls-certificate "$TLS_DIRECTORY/server.crt"

if [ -e "$TLS_DIRECTORY/local-ca.crt" ]; then
    printf 'Local CA:         %s\n' "$TLS_DIRECTORY/local-ca.crt"
fi
printf '%s\n' 'Compare the TLS fingerprint before entering the token in a browser.'
printf '\n'
printf '%s\n' 'Complete both browser steps:'
printf '%s\n' '  1. Open the Setup URL above and create the administrator.'
printf '%s\n' '  2. Sign in and finish "Activate Nemesis".'
printf '%s\n' 'Network collection remains stopped until step 2 completes.'
printf '%s\n' '=================================================================='
printf '\n'
