#!/bin/bash

# omarchy:summary=Native messaging host: download the URL sent by the yt-dlp Chromium extension
# omarchy:hidden=true

set -euo pipefail

SCRIPT_PATH="${BASH_SOURCE[0]}"

# The browser launches us without Omarchy's environment, so locate the repo from
# our own path when OMARCHY_PATH isn't already set. Export it — omarchy-shell (used
# by omarchy-osd) needs it to find the running shell, and silently no-ops without it.
export OMARCHY_PATH="${OMARCHY_PATH:-$(cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)}"

# Make sure the Omarchy bin and yt-dlp are reachable when launched by the browser.
export PATH="$OMARCHY_PATH/bin:/usr/local/bin:/usr/bin:$PATH"

DOWNLOAD_DIR="${OMARCHY_YTDLP_DIR:-$HOME/Videos}"

parse_url() {
  jq -r '.url // empty' 2>/dev/null <<<"$1" || true
}

valid_url() {
  [[ $1 =~ ^https?:// ]]
}

# A printed path is only usable if it is a regular file inside DOWNLOAD_DIR.
# Forged records (leading-dash mpv options, paths with control chars, or
# anything that escaped the download directory) must not reach the click command.
resolve_download_file() {
  local candidate=$1 file_real dir_real

  [[ -n $candidate ]] || return 1
  [[ $candidate != *$'\n'* && $candidate != *$'\r'* && $candidate != *$'\t'* ]] || return 1
  [[ -f $candidate ]] || return 1

  # Read to a NUL: command substitution strips trailing newlines, which would
  # resolve a name ending in one to a different file that may well exist.
  IFS= read -r -d '' file_real < <(realpath -ze -- "$candidate") || return 1
  IFS= read -r -d '' dir_real < <(realpath -ze -- "$DOWNLOAD_DIR") || return 1

  [[ $file_real != *$'\n'* && $file_real != *$'\r'* && $file_real != *$'\t'* ]] || return 1
  # Trim the slash so a download directory of "/" still leaves a usable prefix.
  [[ $file_real == "${dir_real%/}"/* ]] || return 1

  printf '%s' "$file_real"
}

# yt-dlp prints the title JSON-encoded, so a newline or tab in page metadata is an
# escape sequence rather than a record boundary. This is toast text, never a command.
decode_title() {
  local decoded

  decoded=$(jq -r 'if type == "string" then . else empty end' <<<"$1" 2>/dev/null) || return 1
  decoded=${decoded%%[[:cntrl:]]*} # keep what a person would read, drop the forgery
  [[ -n $decoded && $decoded != -* ]] || return 1

  printf '%s' "$decoded"
}

title_from_file() {
  local name=${1##*/}
  name=${name%.*}
  name=${name//[$'\n\r\t']/}
  if [[ -z $name || $name == -* ]]; then
    printf '%s' "Video"
  else
    printf '%s' "$name"
  fi
}

# Drive the Quickshell OSD — a single overlay that updates in place (like the
# volume/brightness bar), so download progress never stacks like notifications.
osd_progress() {
  omarchy-osd -i 󰇚 -p "$1" -d 8000 >/dev/null 2>&1 || true
}

osd_close() {
  omarchy-shell -q osd close >/dev/null 2>&1 || true
}

download_url() {
  local url="$1"

  mkdir -p "$DOWNLOAD_DIR"

  # Don't show anything until yt-dlp confirms there's actually a video to grab.
  if ! yt-dlp --no-playlist --simulate --quiet --no-warnings --no-exec --no-exec-before-download -- "$url" >/dev/null 2>&1; then
    omarchy-notification-send -u critical -g 󰅖 "No video found for download" "$url"
    exit 0
  fi

  osd_progress 0

  # Stream the download: OMARCHY_PROG carries the percent (drives the OSD), and
  # OMARCHY_FILE and OMARCHY_TITLE (printed only after a successful move) carry the
  # path and the title. The title is JSON-encoded so metadata cannot forge a record,
  # and the file is named after it: yt-dlp strips control characters from a filename
  # with or without --restrict-filenames, so a record is still only ever one line.
  local line pct intpct last="" er nowms lastms=0 title="" filepath="" resolved
  while IFS= read -r line; do
    case $line in
    OMARCHY_PROG*)
      pct=${line#OMARCHY_PROG$'\t'}
      intpct=${pct%%.*}
      intpct=${intpct//[^0-9]/}
      [[ -n $intpct && $intpct != "$last" ]] || continue # skip no-op repeats
      # Throttle to ~4 redraws/sec so fast downloads don't spawn a flurry of processes.
      er=$EPOCHREALTIME
      nowms=$((${er%[.,]*} * 1000 + 10#${er##*[.,]} / 1000))
      ((nowms - lastms >= 250)) || continue
      last=$intpct
      lastms=$nowms
      osd_progress "$intpct"
      ;;
    OMARCHY_FILE*)
      resolved=$(resolve_download_file "${line#OMARCHY_FILE$'\t'}") || continue
      filepath=$resolved
      ;;
    OMARCHY_TITLE*)
      title=$(decode_title "${line#OMARCHY_TITLE$'\t'}") || title=""
      ;;
    esac
  done < <(PYTHONUNBUFFERED=1 yt-dlp --no-playlist --no-simulate \
    --quiet --no-warnings --no-exec --no-exec-before-download --progress --newline \
    --progress-template $'download:OMARCHY_PROG\t%(progress._percent_str)s' \
    --paths "$DOWNLOAD_DIR" -o '%(title)s.%(ext)s' \
    --print $'after_move:OMARCHY_FILE\t%(filepath)s' \
    --print $'after_move:OMARCHY_TITLE\t%(title)j' \
    -- "$url" 2>&1)

  osd_close

  # after_move only prints on a successful download+move, so a captured path == success.
  if [[ -n $filepath ]]; then
    [[ -n $title ]] || title=$(title_from_file "$filepath")
    ((${#title} > 50)) && title="${title:0:50}…" # keep the toast compact

    # Square, center-cropped thumbnail so the notification preview isn't stretched.
    local preview
    preview="$(mktemp --suffix=.jpg)"
    ffmpeg -y -i "$filepath" -ss 00:00:00.1 -vframes 1 \
      -vf "crop='min(iw,ih)':'min(iw,ih)',scale=256:256" -q:v 2 \
      "$preview" -loglevel quiet 2>/dev/null || true

    # Best-effort: the download already succeeded, and under `set -e` a failed
    # toast would exit before the thumbnail cleanup below is ever scheduled.
    # `--` keeps mpv from parsing a leading-dash filename as an option; the path
    # is one discrete argument, so it never reaches a shell.
    omarchy-notification-send -g 󰄬 "Download complete" "$title" \
      -t 10000 --image "${preview:-$filepath}" \
      --exec mpv -- "$filepath" || true

    # The shell loads the thumbnail into memory when the toast appears and never
    # re-reads the file, so the preview only has to outlive that load, not the
    # toast.
    (
      sleep 2
      rm -f "$preview"
    ) &
  else
    omarchy-notification-send -u critical -g 󰅖 "Download failed" "$url"
  fi

  exit 0
}

main() {
  local length payload url

  # Detached worker: this is what actually runs yt-dlp and fires notifications.
  if [[ ${1:-} == "--download" ]]; then
    download_url "$2"
  fi

  # Native messaging frame: 4-byte little-endian length prefix, then UTF-8 JSON.
  length=$(head -c4 | od -An -v -tu4 --endian=little | tr -d ' ')
  [[ -n ${length:-} ]] && ((length > 0)) || exit 0

  payload=$(head -c "$length")

  # Ack with an empty message so the extension's sendNativeMessage callback resolves cleanly.
  printf '\x02\x00\x00\x00{}'

  url=$(parse_url "$payload")
  [[ -n $url ]] || exit 0
  valid_url "$url" || exit 0

  # Detach the download so this host exits promptly and frees the browser's port.
  setsid -f "$SCRIPT_PATH" --download "$url" </dev/null >/dev/null 2>&1
}

if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
  main "$@"
fi
