#!/usr/bin/env bash
# Creates a Chromium webapp .desktop entry with favicon as icon.
# Usage: create-webapp.sh <url> [display-name]

set -euo pipefail

usage() {
    echo "Usage: $(basename "$0") <url> [display-name]"
    echo
    echo "  Fetches the page title and favicon, then writes a .desktop"
    echo "  entry that opens the URL in Chromium app-window mode (no tabs/address bar)."
    exit 1
}

[[ $# -lt 1 ]] && usage

URL="$1"
CUSTOM_NAME="${2:-}"

# Prepend https:// if the user omitted the scheme, so curl/grep work correctly.
[[ "$URL" != http://* && "$URL" != https://* ]] && URL="https://$URL"

# Extract just the origin (scheme + host) — needed as a base for resolving
# relative favicon URLs later (e.g. "/icons/logo.png" → "https://example.com/icons/logo.png").
BASE_URL=$(echo "$URL" | grep -oE 'https?://[^/]+')

# Strip scheme and optional "www." to get a clean domain used as a fallback name.
DOMAIN=$(echo "$BASE_URL" | sed -E 's|https?://(www\.)?||')

echo "Fetching: $URL"
# -sL : silent + follow redirects.  -A : spoof a browser User-Agent so sites
# don't return a stripped mobile/bot page with no <title> or <link> tags.
# The "|| echo ''" prevents pipefail from killing the script on network errors.
PAGE_HTML=$(curl -sL --max-time 15 \
    -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
    "$URL" 2>/dev/null || echo "")

# Resolve app name from <title> or argument
if [[ -n "$CUSTOM_NAME" ]]; then
    APP_NAME="$CUSTOM_NAME"
else
    # Python inline: parse the <title> tag and HTML-unescape entities like &amp;
    # so the .desktop Name field looks clean.  Falls back to DOMAIN on failure.
    APP_NAME=$(echo "$PAGE_HTML" | python3 -c '
import sys, re, html as html_mod
content = sys.stdin.read()
m = re.search(r"<title[^>]*>([^<]+)</title>", content, re.IGNORECASE)
print(html_mod.unescape(m.group(1).strip()) if m else "", end="")
' 2>/dev/null || true)
    APP_NAME="${APP_NAME:-$DOMAIN}"
fi

# Build a filesystem-safe identifier from the app name.
# tr '[:upper:]' '[:lower:]'  : lowercase everything
# tr -cs 'a-z0-9' '-'         : replace any run of non-alphanumeric chars with '-'
# sed 's/^-*//;s/-*$//'       : trim leading/trailing hyphens
# Used for both the .desktop filename and the StartupWMClass (WM grouping key).
SAFE_ID=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//')
[[ -z "$SAFE_ID" ]] && SAFE_ID="$DOMAIN"

# Find the best favicon declared in <link rel="icon"> tags.
# The Python snippet ranks candidates by their declared pixel size (sizes="NxN")
# and picks the largest — "any" is treated as 999 to rank SVG/vector icons highest.
# urljoin resolves relative paths against BASE_URL, matching browser behavior.
FAVICON_URL=$(echo "$PAGE_HTML" | python3 -c '
import sys, re
from urllib.parse import urljoin

base = sys.argv[1]
content = sys.stdin.read()

candidates = []
for link in re.findall(r"<link([^>]+?)/??>", content, re.IGNORECASE | re.DOTALL):
    rel_m = re.search(r"rel=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE)
    if not rel_m or "icon" not in rel_m.group(1).lower():
        continue
    href_m = re.search(r"href=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE)
    if not href_m:
        continue
    size = 0
    sizes_m = re.search(r"sizes=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE)
    if sizes_m:
        val = sizes_m.group(1).lower().split("x")[0]
        try:
            size = 999 if val == "any" else int(val)
        except ValueError:
            pass
    candidates.append((size, href_m.group(1)))

if candidates:
    candidates.sort(key=lambda x: x[0], reverse=True)
    print(urljoin(base, candidates[0][1]), end="")
' "$BASE_URL" 2>/dev/null || true)

# Fall back to the conventional /favicon.ico path when no <link> tag was found.
FAVICON_URL="${FAVICON_URL:-$BASE_URL/favicon.ico}"
echo "Favicon: $FAVICON_URL"

# Store webapp icons separately from system icons to keep ~/.local/share/icons clean.
ICON_DIR="$HOME/.local/share/icons/webapps"
mkdir -p "$ICON_DIR"

# Use a temp file so a failed download doesn't leave a partial file at the final path.
TMP=$(mktemp /tmp/webapp-icon.XXXXXX)
trap 'rm -f "$TMP"' EXIT

# Default: fall through to the chromium themed icon if download/convert fails.
ICON_PATH="chromium"  # fallback

if curl -sL --max-time 10 -A "Mozilla/5.0" -o "$TMP" "$FAVICON_URL" && [[ -s "$TMP" ]]; then
    # Prefer ImageMagick: convert normalizes the format (handles multi-frame .ico
    # via [0]) and caps the size at 128x128 without upscaling (\> flag).
    if command -v convert &>/dev/null \
       && convert "${TMP}[0]" -resize 128x128\> "${ICON_DIR}/${SAFE_ID}.png" 2>/dev/null; then
        ICON_PATH="${ICON_DIR}/${SAFE_ID}.png"
        echo "Icon: $ICON_PATH  (PNG via ImageMagick)"
    else
        # Fallback: keep the file as-is, just fix the .ico MIME type alias so the
        # extension matches what XDG icon loaders expect.
        MIME=$(file -b --mime-type "$TMP")
        EXT="${MIME##*/}"
        [[ "$EXT" == "x-icon" || "$EXT" == "vnd.microsoft.icon" ]] && EXT="ico"
        cp "$TMP" "${ICON_DIR}/${SAFE_ID}.${EXT}"
        ICON_PATH="${ICON_DIR}/${SAFE_ID}.${EXT}"
        echo "Icon: $ICON_PATH  ($MIME)"
    fi
else
    echo "Warning: could not fetch favicon — using chromium default icon"
fi

# Write .desktop file per freedesktop.org Desktop Entry Specification.
# chromium --app=<url> opens the site in an app window (no address bar/tabs),
# making it feel like a native application.
DESKTOP_DIR="$HOME/.local/share/applications"
mkdir -p "$DESKTOP_DIR"
DESKTOP_FILE="${DESKTOP_DIR}/webapp-${SAFE_ID}.desktop"

cat > "$DESKTOP_FILE" << DESKTOP
[Desktop Entry]
Version=1.0
Type=Application
Name=${APP_NAME}
Exec=chromium --app=${URL}
Icon=${ICON_PATH}
Terminal=false
Categories=Network;WebBrowser;
StartupWMClass=${SAFE_ID}
DESKTOP

# Mark executable so the file manager and XDG launchers treat it as launchable.
chmod +x "$DESKTOP_FILE"

echo
echo "Created : $DESKTOP_FILE"
echo "  Name  : $APP_NAME"
echo "  URL   : $URL"
echo "  Icon  : $ICON_PATH"
