# Update awareness (opt-in, nomarchy.updates.enable) — a passive background
# check that surfaces a Waybar indicator + a notification when updates are
# available, without ever changing anything (you still run sys-update /
# home-update / flatpak update). It counts:
#   • flake inputs whose locked rev is behind upstream (nixpkgs, the Nomarchy
#     input, home-manager …) — via `git ls-remote` on each branch-tracking
#     github/git input in flake.lock; offline → skipped, never a false alarm.
#   • Flatpak updates, when the `flatpak` CLI is present (services.flatpak on).
#
# nomarchy-updates is always on PATH and self-gates (status prints nothing
# until the timer has found something), so the Waybar module — generated and
# whole-swap — can exec it by name even when the feature is off.
{ config, lib, pkgs, ... }:

let
  cfg = config.nomarchy.updates;

  nomarchy-updates = pkgs.writeShellScriptBin "nomarchy-updates" ''
    set -u
    # System + user profiles, so flatpak / sys-update resolve from a timer-run
    # service too (build-time tools below use absolute store paths regardless).
    export PATH="$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/$USER/bin"
    GIT=${pkgs.git}/bin/git
    JQ=${pkgs.jq}/bin/jq
    cache="''${XDG_CACHE_HOME:-$HOME/.cache}/nomarchy"
    state="$cache/updates.json"
    flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}"
    mkdir -p "$cache"

    count_nix() {
      local lock="$flake/flake.lock" n=0 name kind owner repo url ref locked giturl up
      [ -f "$lock" ] || { echo 0; return; }
      while IFS=$'\t' read -r name kind owner repo url ref locked; do
        [ -n "$locked" ] || continue
        case "$kind" in
          github) giturl="https://github.com/$owner/$repo" ;;
          git)    giturl="$url" ;;
          *)      continue ;;
        esac
        up=$("$GIT" ls-remote "$giturl" "$ref" 2>/dev/null | ${pkgs.gawk}/bin/awk 'NR==1{print $1}')
        [ -n "$up" ] || continue            # offline / unknown → skip (no false alarm)
        [ "$up" != "$locked" ] && n=$((n + 1))
      done < <(
        # Only the flake's DIRECT inputs (root.inputs) — not the transitive
        # closure — so a deep dependency bump doesn't nag as an "update".
        "$JQ" -r '
          .nodes as $nodes
          | ($nodes.root.inputs | [ .[] | if type == "array" then .[0] else . end ]) as $direct
          | $nodes | to_entries[]
          | .key as $k | .value as $v
          | select($direct | index($k))
          | select(($v.original.type? == "github") or ($v.original.type? == "git"))
          | select(($v.original.rev? // "") == "")        # branch-tracking only
          | [ $k, $v.original.type,
              ($v.original.owner? // ""), ($v.original.repo? // ""),
              ($v.original.url? // ""), ($v.original.ref? // "HEAD"),
              ($v.locked.rev? // "") ] | @tsv
        ' "$lock"
      )
      echo "$n"
    }

    count_flatpak() {
      ${lib.optionalString cfg.flatpak ''
        if command -v flatpak >/dev/null 2>&1; then
          flatpak remote-ls --updates --columns=application 2>/dev/null | ${pkgs.gnugrep}/bin/grep -c . || true
          return
        fi
      ''}
      echo 0
    }

    refresh_bar() { ${pkgs.procps}/bin/pkill -RTMIN+9 -x waybar 2>/dev/null || true; }

    case "''${1:-status}" in
      check)
        nix=$(count_nix); fp=$(count_flatpak); total=$((nix + fp))
        prev=$("$JQ" -r '.total // 0' "$state" 2>/dev/null || echo 0)
        printf '{"nix":%d,"flatpak":%d,"total":%d,"ts":%d}\n' \
          "$nix" "$fp" "$total" "$(${pkgs.coreutils}/bin/date +%s)" > "$state"
        # Notify only when NEW updates appear, so a daily timer doesn't nag.
        if [ "$total" -gt 0 ] && [ "$total" -gt "$prev" ]; then
          msg="$nix flake input(s)"
          [ "$fp" -gt 0 ] && msg="$msg · $fp Flatpak(s)"
          ${pkgs.libnotify}/bin/notify-send -a Nomarchy "Updates available" \
            "$msg — click the bar icon, or run sys-update."
        fi
        refresh_bar ;;
      status)
        total=$("$JQ" -r '.total // 0' "$state" 2>/dev/null || echo 0)
        [ "$total" -gt 0 ] 2>/dev/null || exit 0          # up to date / unchecked → hide
        nix=$("$JQ" -r '.nix // 0' "$state"); fp=$("$JQ" -r '.flatpak // 0' "$state")
        tip="Updates available"
        [ "$nix" -gt 0 ] && tip="$tip\n• $nix flake input(s) — sys-update"
        [ "$fp" -gt 0 ] && tip="$tip\n• $fp Flatpak(s) — flatpak update"
        printf '{"text":"󰚰 %d","tooltip":"%s","class":"available"}\n' "$total" "$tip" ;;
      upgrade)
        echo "Checking…"; "$0" check
        nix=$("$JQ" -r '.nix // 0' "$state" 2>/dev/null || echo 0)
        fp=$("$JQ" -r '.flatpak // 0' "$state" 2>/dev/null || echo 0)
        echo "Pending: $nix flake input(s), $fp Flatpak(s)."
        if [ "$nix" -gt 0 ] && command -v sys-update >/dev/null 2>&1; then
          read -rp "Run sys-update (flake update + system rebuild)? [y/N] " a
          [ "$a" = y ] && sys-update
        fi
        if [ "$fp" -gt 0 ] && command -v flatpak >/dev/null 2>&1; then
          read -rp "Run flatpak update? [y/N] " a
          [ "$a" = y ] && flatpak update
        fi
        "$0" check
        echo "Done — press enter."; read -r _ || true ;;
      *) echo "usage: nomarchy-updates [check|status|upgrade]" >&2; exit 64 ;;
    esac
  '';
in
{
  config = lib.mkMerge [
    # Always on PATH so the Waybar module (incl. the static whole-swap themes)
    # can exec it; it self-gates at runtime.
    { home.packages = [ nomarchy-updates ]; }

    (lib.mkIf cfg.enable {
      systemd.user.services.nomarchy-updates = {
        Unit.Description = "Check for Nomarchy / nixpkgs / Flatpak updates";
        Service = {
          Type = "oneshot";
          ExecStart = "${nomarchy-updates}/bin/nomarchy-updates check";
        };
      };
      systemd.user.timers.nomarchy-updates = {
        Unit.Description = "Periodic update-awareness check";
        Timer = {
          OnStartupSec = "2min";
          OnCalendar = cfg.interval;
          Persistent = true;
        };
        Install.WantedBy = [ "timers.target" ];
      };
    })
  ];
}
