#!/bin/bash
# Hourlet installer. Copyright (c) 2026 Rudra K. Hourlet is used under its license (license.html on the site).
# Installs Hourlet into /Applications:
#   curl --proto '=https' --tlsv1.2 -fsSL https://hourlet.pages.dev/install.sh | bash
# Files downloaded with curl aren't marked as quarantined, so macOS opens Hourlet without the
# "Hourlet Not Opened" warning a browser download gets (Hourlet isn't notarized by Apple).
# Safety: downloads use HTTPS only, the disk image must match the SHA-256 pinned below, and the app
# inside must be Hourlet with an intact code signature, or nothing is installed.
# Everything runs inside main, so a download cut off halfway never runs half a script.
# The new copy is staged next to the old one and swapped in, so a failed copy leaves the old app as it was.
set -euo pipefail

main() {
    # Pinned for each release by scripts/prepare-site.sh.
    local site="https://hourlet.pages.dev"
    local dmg="Hourlet-0.7.2.dmg"
    local sha256="aea0643a6dbf7a473600efd54be66694384646729da0631ae45ed7855052457d"
    local bundle_id="com.rudrak.hourlet"
    local app="/Applications/Hourlet.app"

    say() { printf '%s\n' "$*"; }
    fail() { printf '✗ %s\n' "$*" >&2; exit 1; }

    [ "$(uname -s)" = Darwin ] || fail "Hourlet is a Mac app."
    local major
    major=$(sw_vers -productVersion | cut -d. -f1)
    [ "$major" -ge 15 ] || fail "Hourlet needs macOS 15 or later."

    local get=(curl --proto '=https' --tlsv1.2 -fL)
    if [ -n "${HOURLET_SITE:-}" ]; then
        # Local testing only: plain HTTP is allowed for this Mac, never for another host.
        case "$HOURLET_SITE" in
            http://localhost|http://localhost:*|http://127.0.0.1|http://127.0.0.1:*) get=(curl -fL) ;;
            https://*) ;;
            *) fail "HOURLET_SITE must be https://, or http:// on localhost for testing." ;;
        esac
        site="$HOURLET_SITE"
        say "Installing from $site."
    fi
    [[ "$site$dmg$sha256" != *REPLACE-* ]] || fail "This installer hasn't been set up for a release yet."

    # Globals, not locals: the EXIT trap runs after main has returned.
    mount=""
    staged=""
    sudo=()
    tmp=$(mktemp -d -t hourlet)
    cleanup() {
        if [ -n "$mount" ]; then hdiutil detach "$mount" -quiet -force >/dev/null 2>&1 || true; fi
        if [ -n "$staged" ]; then "${sudo[@]+"${sudo[@]}"}" rm -rf "$staged" 2>/dev/null || true; fi
        rm -rf "$tmp"
    }
    trap cleanup EXIT

    say "By installing, you agree to the Hourlet license: $site/license.html"
    say "Downloading Hourlet…"
    "${get[@]}" --progress-bar "$site/downloads/$dmg" -o "$tmp/Hourlet.dmg" || fail "The download failed."
    local actual
    actual=$(shasum -a 256 "$tmp/Hourlet.dmg" | cut -d' ' -f1)
    [ "$actual" = "$sha256" ] || fail "The download doesn't match its checksum. Nothing was installed."

    mount="$tmp/mnt"
    mkdir -p "$mount"
    hdiutil attach "$tmp/Hourlet.dmg" -nobrowse -readonly -noautoopen -quiet -mountpoint "$mount" \
        || { mount=""; fail "Couldn't open the disk image."; }
    local source="$mount/Hourlet.app"
    [ -d "$source" ] || fail "The disk image doesn't contain Hourlet.app."
    [ "$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$source/Contents/Info.plist" 2>/dev/null)" = "$bundle_id" ] \
        || fail "The disk image contains a different app. Nothing was installed."
    codesign --verify --strict --deep "$source" 2>/dev/null || fail "Hourlet's signature is invalid. Nothing was installed."

    if pgrep -xq Hourlet; then
        say "Quitting the running copy…"
        osascript -e 'quit app "Hourlet"' >/dev/null 2>&1 || true
        local i
        for i in 1 2 3 4 5 6 7 8 9 10; do pgrep -xq Hourlet || break; sleep 0.5; done
        pkill -x Hourlet 2>/dev/null || true
    fi

    say "Installing to /Applications…"
    if [ ! -w /Applications ] || { [ -e "$app" ] && [ ! -w "$app" ]; }; then
        say "Your account can't write to /Applications, so macOS will ask for your password."
        sudo=(sudo)
    fi
    staged="/Applications/.Hourlet.app.new"
    "${sudo[@]+"${sudo[@]}"}" rm -rf "$staged"
    "${sudo[@]+"${sudo[@]}"}" ditto "$source" "$staged" || fail "Couldn't copy Hourlet into Applications. Your current copy is unchanged."
    # Curl downloads aren't quarantined, but clear it anyway so Gatekeeper's one-time prompt is skipped.
    "${sudo[@]+"${sudo[@]}"}" xattr -dr com.apple.quarantine "$staged" 2>/dev/null || true
    "${sudo[@]+"${sudo[@]}"}" rm -rf "$app"
    "${sudo[@]+"${sudo[@]}"}" mv "$staged" "$app"
    staged=""

    hdiutil detach "$mount" -quiet >/dev/null 2>&1 || true
    mount=""

    open "$app"
    # Only promise updates when this build knows where to find them.
    local feed
    feed=$(/usr/libexec/PlistBuddy -c "Print :SUFeedURL" "$app/Contents/Info.plist" 2>/dev/null || true)
    if [[ "$feed" == https://* && "$feed" != *REPLACE-* ]]; then
        say "✓ Hourlet is installed and in your menu bar. It updates itself from now on."
    else
        say "✓ Hourlet is installed and in your menu bar. Check the website for new versions."
    fi
}

main "$@"
