#!/usr/bin/env bash
# sunset-alarm — alert at a specified number of minutes before sunset
# Usage: sunset-alarm (-l LAT:LONG | -t HH24:MI) (-m MINUTES) [-h]
set -euo pipefail

SUNSET=""         # epoch seconds for today's sunset
ALERT_EPOCH=""    # epoch when alert should fire (sunset - offset)
OFFSET_MIN=0      # signed minute offset from sun -t/--hardcoded
LAT="" LON=""     # coordinates  (-l/--location)
HARDCODED_TIME="" # HH:MM string              (-t/--hardcoded)

# ── helpers ────────────────────────────────────────────────────────
usage() {
    cat <<'EOF'
Usage: sunset-alarm (-l LAT:LONG | -t HH24:MI) (-m MINUTES) [-h]

Options:
  -l LAT:LONG        Latitude and longitude (e.g. 32.123:-111.111)
  -t HH24:MI         Hardcoded sunset time (ignored when combined with -l)
  -m MINUTES         Minutes offset before (+) or after (-) sunset to alert
  -h                 Show this help and exit
EOF
}

die() { echo "ERROR: $*" >&2; exit 1; }

warn_reconnect() { echo "Network unavailable, retrying in 5 minutes…" >&2; }

# Query Open-Meteo for today's sunset epoch (local timezone).
fetch_sunset_epoch() {
    local lat="$1" lon="$2"
    local json resp_code
    # curl –silent + show HTTP code (-w) so we can detect server errors.
    json=$(curl -sf -w '\n%{http_code}' \
        "https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=sunset&timezone=auto" \
        --connect-timeout 10 --max-time 30) || return 1
    resp_code="${json##*$'\n'}"
    json="${json%$'\n'*}"
    [[ "$resp_code" == "200" ]] || return 2
    # jq → "YYYY-MM-DDTHH:MM" (ISO-8601 local-time string for the sunset field)
    local iso_ts
    iso_ts=$(echo "$json" | jq -r '.current.sunset' | grep -v '^null$')
    [[ -n "$iso_ts" ]] || return 3
    # date -d parses ISO-8601 timestamps directly.
    SUNSET=$(date -d "$iso_ts" +%s)
}

# Build today's sunset epoch from HH:MM when using the hardcoded path (-t).
set_sunset_from_time() {
    local hhmm="$1" hh mm
    read -r hh mm <<< "$(echo "$hhmm" | tr ':' ' ')"
    # Pad hours / minutes in case user passed "8:30" etc. (they won't via arg
    # parsing, but defensive never hurts).
    SUNSET=$(date -d "$(date +%Y-%m-%d) ${hh}:${mm}:00" +%s)
}

# Fire a desktop notification (best-effort).
notify() {
    local title="$1" body="$2"
    notify-send -u normal "$title" "$body" 2>/dev/null || true
    printf '%s — %s\n' "$(date '+%F %T')" "ALERT: $body"
}

# ── argument parsing ───────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
    case "$1" in
        -l)
            [[ $# -ge 2 ]] || die "-l requires an argument (LAT:LONG)"
            IFS=':' read -r LAT LON <<< "$2"
            shift 2
            ;;
        -t)
            [[ $# -ge 2 ]] || die "-t requires an argument (HH24:MI)"
            HARDCODED_TIME="$2"
            shift 2
            ;;
        -m)
            [[ $# -ge 2 ]] || die "-m requires an argument (MINUTES)"
            OFFSET_MIN="$2"
            shift 2
            ;;
        -h|--help) usage; exit 0 ;;
        *)         die "Unknown option: $1" ;;
    esac
done

# ── guardrails ─────────────────────────────────────────────────────
[[ $# -eq 0 ]] || die "extra arguments provided — see --help"
(( OFFSET_MIN >= -60 )) || [[ $OFFSET_MIN -le 0 ]] 2>/dev/null \
    1>/dev/null || die "-m must be ≥ -60 (or any value ≤ 0)"

# Need either -l or -t.
if [[ -z "$LAT" && -z "$HARDCODED_TIME" ]]; then
    die "Need at least one of: -l LAT:LONG, -t HH24:MI"
fi

# ── Phase 1 — resolve today's sunset (epoch) ───────────────────────
if [[ -n "$LAT" && -n "$LON" ]]; then
    while true; do
        fetch_sunset_epoch "$LAT" "$LON" && break
        delay=$(( 5 * 60 ))
        warn_reconnect; sleep "$delay" || warn_reconnect
    done
else
    set_sunset_from_time "$HARDCODED_TIME"
fi

# ── Phase 2 — compute alert time and loop ──────────────────────────
ALERT_EPOCH=$(( SUNSET - OFFSET_MIN * 60 ))
now_epoch=$(date +%s)

if (( now_epoch >= ALERT_EPOCH )); then
    # Already past the window — fire once, exit.
    notify "Sunset Alarm" \
        "Triggered (offset: ${OFFSET_MIN:++}$(printf '%d' "$OFFSET_MIN")). Sunset at $(date -d @"$SUNSET" '+%H:%M')."
    exit 0
fi

while true; do
    now_epoch=$(date +%s)
    if (( ALERT_EPOCH <= now_epoch && now_epoch < SUNSET )); then
        notify "Sunset Alarm" \
            "Triggered (offset: ${OFFSET_MIN:++}$(printf '%d' "$OFFSET_MIN")). Sunset at $(date -d @"$SUNSET" '+%H:%M')."
    elif (( now_epoch > SUNSET )); then
        # Day is over — re-fetch for tomorrow (if we have -l).
        if [[ -n "$LAT" && -n "$LON" ]]; then
            warn_reconnect=true   # just print a quick line, don't sleep
            while true; do
                fetch_sunset_epoch "$LAT" "$LON" && break
                delay=$(( 5 * 60 ))
                warn_reconnect; sleep "$delay" || warn_reconnect
            done
        else
            set_sunset_from_time "$HARDCODED_TIME"
        fi
        ALERT_EPOCH=$(( SUNSET - OFFSET_MIN * 60 ))
        continue
    fi
    sleep 60
done
