#!/usr/bin/env bash

# A website, as an application.
#
# The dock and the launcher had nothing to feed them but installed packages,
# while both macOS and Windows now turn a site into something with its own
# icon and its own window. This is that, in about a hundred lines of shell.
#
# What must hold:
#
#   1. The entry it writes is a valid desktop entry that opens the site in app
#      mode. A malformed one is invisible rather than broken, which is worse.
#   2. A name cannot escape the applications directory. The name comes from a
#      person typing into a launcher box, so "../../../.bashrc" is a thing it
#      will eventually be handed.
#   3. Remove only ever removes its own. Sharing a name with a real
#      application must not delete that application.
#   4. A missing icon does not fail the install. A web app with a generic icon
#      still works; an install that failed because a favicon 404'd does not.
#   5. A browser that cannot do app mode is refused rather than given
#      something that opens a normal window and pretends.
#
# Runs against a throwaway XDG_DATA_HOME, so nothing here touches the real
# applications directory. Network calls are stubbed.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
webapp="$repo_dir/bin/panama-webapp"

findings=()
note() { findings+=("$1"); }

[[ -x "$webapp" ]] || { printf 'webapp contract: %s is not executable\n' "$webapp" >&2; exit 1; }

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
data="$work/data"
stub="$work/bin"
mkdir -p "$data/applications" "$stub"

# curl fails for everything, which is the icon-less case. Item 4 says an
# install must survive it.
cat >"$stub/curl" <<'STUB'
#!/usr/bin/env bash
exit 1
STUB
chmod +x "$stub/curl"

# A chromium-family default browser.
cat >"$stub/xdg-settings" <<'STUB'
#!/usr/bin/env bash
printf 'chromium.desktop\n'
STUB
chmod +x "$stub/xdg-settings"
cat >"$stub/chromium" <<'STUB'
#!/usr/bin/env bash
exit 0
STUB
chmod +x "$stub/chromium"
mkdir -p "$data/applications"
cat >"$data/applications/chromium.desktop" <<STUB
[Desktop Entry]
Name=Chromium
Exec=$stub/chromium %U
STUB

run() { XDG_DATA_HOME="$data" PATH="$stub:$PATH" "$webapp" "$@" 2>&1; }

# ── 1 & 4. Install writes a valid entry, even with no icon ──────────────────

output="$(run install https://app.example.com "Example App")"
status=$?
(( status == 0 )) || note "install failed when the icon could not be fetched: $output"

entry="$data/applications/panama-webapp-example-app.desktop"
[[ -f "$entry" ]] || note 'install did not write a desktop entry'

if [[ -f "$entry" ]]; then
    grep -q '^Type=Application$' "$entry" || note 'the entry is not an Application'
    grep -q '^Name=Example App$' "$entry" || note 'the entry does not carry the name given'
    grep -q -- '--app=https://app.example.com' "$entry" \
        || note 'the entry does not open the site in app mode, so it would open an ordinary browser window'
    grep -q '^X-Panama-WebApp=' "$entry" \
        || note 'the entry is not marked as a Panama web app, so remove cannot tell it from a real application'
    if command -v desktop-file-validate >/dev/null 2>&1; then
        desktop-file-validate "$entry" >/dev/null 2>&1 \
            || note 'the entry does not pass desktop-file-validate, so a launcher may ignore it'
    fi
fi

# ── 2. A hostile name cannot write outside the directory ────────────────────

run install https://example.com "../../../../tmp/panama-escape" >/dev/null 2>&1
[[ -e "$work/panama-escape.desktop" || -e "/tmp/panama-escape.desktop" ]] \
    && note 'a name containing path separators wrote outside the applications directory'
# It should have landed as a slug inside the directory, or been refused.
escaped="$(find "$data/applications" -name '*escape*' | head -1)"
if [[ -n "$escaped" ]]; then
    [[ "$(dirname "$escaped")" == "$data/applications" ]] \
        || note 'a hostile name escaped the applications directory'
fi

# A name with nothing usable in it is refused rather than producing a file
# called ".desktop".
run install https://example.com "///" >/dev/null 2>&1 \
    && note 'a name with no usable characters was accepted'

# ── 3. Remove only removes its own ──────────────────────────────────────────

# A real application that happens to share a name.
cat >"$data/applications/panama-webapp-decoy.desktop" <<'DECOY'
[Desktop Entry]
Type=Application
Name=Decoy
Exec=/bin/true
DECOY
run remove "Decoy" >/dev/null 2>&1 \
    && note 'remove deleted an entry that is not a Panama web app'
[[ -f "$data/applications/panama-webapp-decoy.desktop" ]] \
    || note 'remove deleted a file it should have refused to touch'

run remove "Example App" >/dev/null 2>&1 || note 'remove failed on a web app it installed'
[[ -f "$entry" ]] && note 'remove left the entry behind'

run remove "Not Installed" >/dev/null 2>&1 \
    && note 'removing something that was never installed reported success'

# ── 5. A browser without app mode is refused ────────────────────────────────

cat >"$stub/xdg-settings" <<'STUB'
#!/usr/bin/env bash
printf 'firefox.desktop\n'
STUB
cat >"$data/applications/firefox.desktop" <<'STUB'
[Desktop Entry]
Name=Firefox
Exec=/usr/bin/firefox %U
STUB
output="$(run install https://example.com "Firefox Test" 2>&1)"
status=$?
(( status != 0 )) || note 'a browser with no app mode was accepted, so the entry would open an ordinary window'
grep -qi 'app' <<<"$output" || note 'the refusal does not explain why'

# ── The launcher command ────────────────────────────────────────────────────

command_file="$repo_dir/config/local/share/vicinae/scripts/install-web-app"
[[ -x "$command_file" ]] || note 'there is no launcher command to install a web app'
grep -q 'argument1' "$command_file" \
    || note 'the launcher command takes no URL argument'

if (( ${#findings[@]} > 0 )); then
    printf 'webapp contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'webapp contract: PASS\n'
