#!/bin/bash
set -uo pipefail

CONFIG_DIR="/etc/findmyphone-pc"
CONFIG_FILE="$CONFIG_DIR/config.json"
LOG_FILE="/var/log/findmyphone-pc.log"

# Bump on every release that changes this script - compared against
# downloads/version-deb.json on the server to decide whether to self-update.
CURRENT_VERSION_CODE=1

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

get_ip_location() {
    curl -s --max-time 10 'http://ip-api.com/json/'
}

get_uptime_minutes() {
    awk '{print int($1/60)}' /proc/uptime
}

get_battery_percent() {
    for bat in /sys/class/power_supply/BAT*; do
        if [ -f "$bat/capacity" ]; then
            cat "$bat/capacity"
            return
        fi
    done
    echo ""
}

check_for_update() {
    local server_url="$1"
    local version_json server_version version_name url tmp_deb

    # -f makes curl fail (empty output, non-zero exit) on a 4xx/5xx response -
    # without it, a missing version-deb.json returns an HTML error page that
    # jq can't parse, which previously fell through as if an update (with an
    # empty version) were available and tried to "install" the HTML page.
    version_json=$(curl -sf --max-time 15 "$server_url/downloads/version-deb.json") || return
    server_version=$(echo "$version_json" | jq -r '.versionCode' 2>/dev/null) || return
    if ! [[ "$server_version" =~ ^[0-9]+$ ]]; then
        log "Update-Pruefung: unerwartete Antwort vom Server, ueberspringe."
        return
    fi
    if [ "$server_version" -le "$CURRENT_VERSION_CODE" ]; then
        return
    fi

    version_name=$(echo "$version_json" | jq -r '.versionName')
    url=$(echo "$version_json" | jq -r '.url')
    if [[ "$url" != http* ]]; then
        url="$server_url$url"
    fi

    log "Update verfuegbar: Version $version_name (aktuell: $CURRENT_VERSION_CODE) - lade herunter..."
    tmp_deb="/tmp/findmyphone-pc-update.deb"
    if ! curl -sf --max-time 60 -o "$tmp_deb" "$url"; then
        log "Update-Download fehlgeschlagen."
        rm -f "$tmp_deb"
        return
    fi

    # dpkg -i would overwrite this very script's file on disk while this
    # process is still executing it - tried that directly first and it left
    # dpkg in a half-configured state ("iFR"), because the running bash
    # process got destabilized mid-unpack. Delegating the actual install to
    # an independent, detached systemd transient unit avoids that: it's a
    # fresh process reading its command from a string, not this file, and
    # systemd-run escapes this service's cgroup so it survives the restart.
    log "Uebergebe Installation an unabhaengigen Hintergrundprozess..."
    systemd-run --unit="findmyphone-pc-selfupdate-$$" --description="FindMyPhone PC Selbst-Update" \
        /bin/bash -c "dpkg -i '$tmp_deb' >> '$LOG_FILE' 2>&1 && rm -f '$tmp_deb' && systemctl restart findmyphone-pc || echo 'Selbst-Update fehlgeschlagen' >> '$LOG_FILE'" \
        >> "$LOG_FILE" 2>&1
    exit 0
}

report_location() {
    local server_url="$1" device_id="$2" device_token="$3"
    local ip_json status lat lon accuracy hostname_val battery uptime captured_at body http_status

    ip_json=$(get_ip_location)
    status=$(echo "$ip_json" | jq -r '.status // "fail"')
    if [ "$status" != "success" ]; then
        log "Kein Standort verfuegbar (IP-Abfrage fehlgeschlagen), ueberspringe diese Meldung."
        return
    fi
    lat=$(echo "$ip_json" | jq -r '.lat')
    lon=$(echo "$ip_json" | jq -r '.lon')
    accuracy=5000

    hostname_val=$(hostname)
    battery=$(get_battery_percent)
    uptime=$(get_uptime_minutes)
    captured_at=$(($(date +%s%N) / 1000000))

    body=$(jq -n \
        --argjson lat "$lat" \
        --argjson lon "$lon" \
        --argjson accuracy "$accuracy" \
        --argjson capturedAt "$captured_at" \
        --arg deviceModel "Debian-PC ($hostname_val)" \
        --argjson uptimeMinutes "$uptime" \
        '{lat: $lat, lon: $lon, accuracy: $accuracy, capturedAt: $capturedAt, deviceModel: $deviceModel, uptimeMinutes: $uptimeMinutes}')

    if [ -n "$battery" ]; then
        body=$(echo "$body" | jq --argjson batteryPercent "$battery" '. + {batteryPercent: $batteryPercent}')
    fi

    http_status=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 \
        -X POST "$server_url/api/devices/$device_id/location" \
        -H "Content-Type: application/json" \
        -H "X-Device-Token: $device_token" \
        -d "$body")

    if [ "$http_status" = "200" ]; then
        log "Standort gemeldet: $lat, $lon (Genauigkeit ${accuracy} m)"
    else
        log "Melden fehlgeschlagen (HTTP $http_status)."
    fi
}

# FINDMYPHONE_PC_TEST_MODE lets this run once against a given config and exit
# instead of reading the real config file and looping forever - used for
# automated testing and as a diagnostic path without waiting on the 60s loop.
if [ "${FINDMYPHONE_PC_TEST_MODE:-}" = "1" ]; then
    SERVER_URL=$(echo "$FINDMYPHONE_PC_TEST_CONFIG" | jq -r '.serverUrl')
    DEVICE_ID=$(echo "$FINDMYPHONE_PC_TEST_CONFIG" | jq -r '.deviceId')
    DEVICE_TOKEN=$(echo "$FINDMYPHONE_PC_TEST_CONFIG" | jq -r '.deviceToken')
    if [ "${FINDMYPHONE_PC_TEST_SELFUPDATE:-}" = "1" ]; then
        check_for_update "$SERVER_URL"
    else
        report_location "$SERVER_URL" "$DEVICE_ID" "$DEVICE_TOKEN"
    fi
    exit 0
fi

if [ ! -f "$CONFIG_FILE" ]; then
    log "Nicht eingerichtet - bitte 'sudo findmyphone-pc-setup' ausfuehren."
    exit 1
fi

SERVER_URL=$(jq -r '.serverUrl' "$CONFIG_FILE")
DEVICE_ID=$(jq -r '.deviceId' "$CONFIG_FILE")
DEVICE_TOKEN=$(jq -r '.deviceToken' "$CONFIG_FILE")

log "Gestartet (Version $CURRENT_VERSION_CODE)."

LOOP_COUNT=0
while true; do
    report_location "$SERVER_URL" "$DEVICE_ID" "$DEVICE_TOKEN"
    if [ $((LOOP_COUNT % 60)) -eq 0 ]; then
        check_for_update "$SERVER_URL"
    fi
    LOOP_COUNT=$((LOOP_COUNT + 1))
    sleep 60
done
