set positional-arguments # makes variables accesible as $1 $2 $@
export invokedir := invocation_directory()
export GUM_CHOOSE_HEIGHT := "15"
export GUM_FILTER_HEIGHT := "15"
export FZF_DEFAULT_OPTS := "--height 15 --cycle --bind 'ctrl-a:toggle-all' " + env('FZF_DEFAULT_OPTS', "")

#export hosts_nixos := `2>/dev/null nix eval .\#nixosConfigurations --apply builtins.attrNames --json`
#export hosts_fhs   := `2>/dev/null nix eval .\#systemConfigs --apply builtins.attrNames --json`

@_default:
  just "$(gum filter --placeholder "Pick a recipie..." $(just --summary --unsorted))"

eval $hostname=`just _a_host` $attrpath="system.build.toplevel.outPath" *args:
  nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.$attrpath" --show-trace "${@:3}"
eval-vm $hostname=`just _a_host` $attrpath="system.build.toplevel.outPath" *args:
  # nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.system.build.vm.outPath" --show-trace "${@:3}"
  nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.virtualisation.vmVariant.$attrpath" --show-trace "${@:3}"

repl $hostname=`just _a_host`:
  NIX_NO_NOM=1 nixos-rebuild-ng --flake .#"$hostname" repl

report hostname=`just _a_host`:
  nix eval .#nixosReports."{{hostname}}" --json --show-trace | yq . --yaml-output --width=999999 | bat --language yaml --style plain --paging never

reports:
  nix eval .#nixosReports --json --show-trace | yq . --yaml-output --width=999999 | bat --language yaml --style plain --paging never

update:
  git reset flake.lock
  git restore flake.lock
  git pull --rebase --autostash
  nix flake update --commit-lock-file
  @mkdir -p .direnv/
  @printf "%s\n" "$(date +%Y-%m-%d)" > .direnv/pull-date

@update-select:
  git reset flake.lock
  git restore flake.lock
  nix eval --file flake.nix --apply 'x: builtins.attrNames x.inputs' --json \
    | (printf "%s\n" --commit-lock-file; jq '.[]' -r | tr '0123456789' '9876543210' | sort | tr '0123456789' '9876543210' | grep -v "^self$") \
    | gum choose --no-limit \
    | xargs nix flake update
  git add flake.lock

pull-dconf:
  ./users/pbsds/home/profiles/desktop/gnome/dconf-pull.sh
  git diff ./users/pbsds/home/profiles/desktop/gnome/
  -gum confirm "git add --patch?" --default=no \
    && git add --patch ./users/pbsds/home/profiles/desktop/gnome/
  -gum confirm "git checkout --path?" --default=no \
    && git checkout --patch ./users/pbsds/home/profiles/desktop/gnome/

# todo: support system-manager
# todo: support home-manager?
build hostname="" *_:
  nixos-rebuild-ng build --accept-flake-config --show-trace --flake .#"{{hostname}}" "${@:2}"

build-vm hostname=`just _a_host` *_:
  nixos-rebuild-ng build-vm --accept-flake-config --show-trace --flake .#"{{hostname}}" "${@:2}"

build-vm-and-run $hostname=`just _a_host` *_:
  @# TODO: setup ports?
  @# TODO: attach serial instead of gui?
  @# TODO: headless and ssh?
  @just build-vm "$hostname" "${@:2}"
  ./result/bin/run-"$hostname"-vm

# build-home $user=`whoami`:
#   #!/usr/bin/env -S bash -euo pipefail

test *_:
  sudo nixos-rebuild-ng test --accept-flake-config --show-trace --flake . "$@"

switch *_:
  sudo nixos-rebuild-ng switch --accept-flake-config --show-trace --flake . "$@"

boot *_:
  sudo nixos-rebuild-ng boot --accept-flake-config --show-trace --flake . "$@"

boot-and-reboot *_:
  sudo nixos-rebuild-ng boot --accept-flake-config --show-trace --flake . "$@"
  sudo reboot

# TODO: nixos-rebuild-ng
push $hostname=`just _a_host` cmd=`gum choose test switch boot --header "Select mode..."`:
  #!/usr/bin/env -S bash -euo pipefail
  remote_sudo=1
  gum confirm "Use '--use-remote-sudo'?" || remote_sudo=0
  # build once first for nom output
  (set -x; nixos-rebuild build --flake .#"$hostname") || exit $?
  # target_host="root@$(nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.networking.fqdn" --json | jq . -r)"
  target_host="$hostname"
  printf "%s\n" pushing...
  # nixos-rebuild will perform a build still, meaning another full eval...
  # TODO: support a simple copy-closure with no activation on remote
  if [[ "$remote_sudo" -eq 1 ]]; then
    # set -x; NIX_NO_NOM=1 NIX_SSHOPTS="-t" nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "$target_host" --use-substitutes --use-remote-sudo
    set -x; NIX_NO_NOM=1 IX_SSHOPTS="-o RequestTTY=force" nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "$target_host" --use-substitutes --use-remote-sudo
  else
    set -x; NIX_NO_NOM=1 nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "root@$target_host" --use-substitutes
  fi

# test-files $hostname=`just _a_host` *filenames:
#   #!/usr/bin/env -S bash -euo pipefail
#   # TODO: remember previous choices
#   [[ -n "$hostname" ]] || { printf >&2 "%s\n" "ERROR: no hostname"; false; }
#   declare -a filenames=("${@:2}")
#   if [[ "${#filenames[@]}" -eq 0 ]]; then
#     etcfiles=$(
#       nix eval ".#nixosConfigurations.\"$hostname\".config.environment.etc" --json \
#         --apply 'builtins.mapAttrs (k: v: { inherit (v) enable target mode; })'
#     )
#     homekeys=$(
#       nix eval ".#nixosConfigurations.\"$hostname\".config.home-manager.users" --json \
#         --apply 'builtins.mapAttrs (user: config: { inherit (config.home) homeDirectory; files = builtins.mapAttrs (k: v: { inherit (v) enable target recursive; }) config.home.file; } )'
#         # | jq 'to_entries[]| .key as $user | .value.homeDirectory as $homeDirectory | .value.files | to_entries[] | select(.value) | [ "config.home-manager.users.\"\($user)\".home.file.\"\(.key)\"", $homeDirectory + "/" + .value.target, .value.target, .key, $user, $homeDirectory] | @tsv' -r
#     )
#     filepathindex=$(
#       jq --null-input -c \
#         --arg     hostname "$hostname" \
#         --argjson etcfiles "$etcfiles" \
#         --argjson homekeys "$homekeys" \
#         '{
#           "nixosConfigurations.\"\($hostname)\".config.environment.etc": (
#             $etcfiles | with_entries( select(.value.enable) | { key, value: (.value + { target : ("/etc/" + .value.target) }) } )
#           )
#         } + (
#           $homekeys | with_entries(
#             .key as $user |
#             .value.homeDirectory as $homeDirectory |
#             {
#               key: ("nixosConfigurations.\"\($hostname)\".config.home-manager.users.\"\($user)\".home.file"),
#               value: (
#                 .value.files | with_entries(
#                   select(.value.enable) | {
#                     key,
#                     value: (.value + {
#                       target: ($homeDirectory + "/" + .value.target),
#                     }),
#                   }
#                 )
#               ),
#             }
#           )
#         )'
#     )

#     # cache
#     if [[ "$UID" -ne 0 ]]; then
#       mkdir -p .direnv/
#       cat <<<"$filepathindex" >.direnv/just-cache-test-files-"$hostname".json
#     fi
#   else
#     [[ -s .direnv/just-cache-test-files-"$hostname".json ]] || { printf >&2 "%s\n" "ERROR: no cache found"; false; }
#     filepathindex=$(cat .direnv/just-cache-test-files-"$hostname".json)
#   fi

#   if [[ "${#filenames[@]}" -eq 0 ]]; then
#     filenames=$(
#       set +e
#       jq <<<"$filepathindex" 'to_entries[] | .value | to_entries[] | .value' -r \
#         | fzf --height=50% --reverse --multi -d$'\t' --with-nth 2
#     )
#     [[ -n "$selection" ]]
#   fi

#   # jq <<<"$filepathindex" . ; false

#   filepathindex_inverse=$(
#     jq <<<"$filepathindex" 'with_entries( .key as $attr | .value | to_entries[] | {key: .value.target, value: (.value + {attr: $attr, key})} )' -c
#   )
#   # jq <<<"$filepathindex_inverse" . ; false

#   # jq <<<"$filepathindex" 'to_entries[] | .key as $attr | .value | to_entries[] | @sh "\(.value) \($attr).\("\""+.key+"\"")"' -r

#   nix_expr=$(
#     printf "%s\n" 'pkgs.writeScript "do" ('
#     printf '"%s\\n" +\n' '#!${pkgs.runtimeShell}'
#     printf '"%s\\n" +\n' 'set -euo pipefail'
#     printf '"%s\\n" +\n' 'sudo : && SUDO=sudo || SUDO=:'
#     printf "%s\n" "${filenames[@]}" |
#       jq -sR --argjson index "$filepathindex_inverse" '
#         split("\n") | map(select(.==""|not) | $index[.] ) | group_by(.attr) | map(
#           "  (let cfg = \(.[0].attr); in '"''"'" + (map(
#             if .target | startswith("/etc/") then
#               if .mode == "symlink" and false then "
#                 $SUDO ln -sfn ${cfg.\"\(.key)\".source} \(.target | @sh )
#               " else "
#                 $SUDO rm -rf \(.target | @sh )
#                 $SUDO cp -a ${cfg.\"\(.key)\".source} \(.target | @sh )
#                 $SUDO chown -R ${toString cfg.\"\(.key)\".uid}:${toString cfg.\"\(.key)\".gid} \(.target | @sh )
#                 $SUDO chmod -R ${toString cfg.\"\(.key)\".uid}:${toString cfg.\"\(.key)\".gid} \(.target | @sh )
#                 # TODO: cfg.\"\(.key)\".user
#                 # TODO: cfg.\"\(.key)\".group
#               " end
#             else
#               if .recursive then
#                 "${lib.getExe pkgs.xorg.lndir} -silent ${cfg.\"\(.key)\".source} \(.target | @sh )\n"
#               else
#                 "ln -sfn ${cfg.\"\(.key)\".source} \(.target | @sh )\n"
#               end
#             end
#           ) | join("")) + "'"''"')"
#         ) | join(" + \n")
#       ' -r
#     printf "%s\n" ')'
#   )
#   # printf "%s\n" "$nix_expr" ; false

#   flake=$(nix flake archive  . --json | jq .path -r)
#   nix build --impure --expr "with (builtins.getFlake ''$flake''); let inherit (nixosConfigurations.\"$hostname\") pkgs; in $nix_expr" --show-trace
#   false

#   # TODO: run the result


inspect:
  nix run -- nixpkgs#nix-inspect -p .

inspect-config host=`just _a_host` prefix="":
  nix run -- nixpkgs#nix-inspect  -e '(builtins.getFlake "'"$PWD"'").nixosConfigurations."{{host}}".config{{ if prefix == "" { "" } else { "." + prefix } }}'

_nixos_attrnames:
  #!/usr/bin/env -S bash -euo pipefail
  hostname=$(hostname)
  cachefile=.direnv/just-cache-nixos-attrnames.txt
  if [[ flake.nix -nt "$cachefile" ]]; then
    hostnames=$(nix eval .#nixosConfigurations --apply builtins.attrNames --json 2>/dev/null | jq '.[]' -r)
    if [[ "$(grep <<<"$hostnames" -Fx "$hostname" | wc -l)" -eq 1 ]]; then
      hostnames="$(printf "%s\n" "$hostname"; grep <<<"$hostnames" -v "^$hostname$")"
    fi
    if [[ "$UID" -ne 0 ]]; then
      mkdir -p .direnv/
      cat <<<"$hostnames" >"$cachefile"
    fi
  else
    hostnames="$(cat $cachefile)"
  fi
  current_remote=$(just remote-current)
  if [[ -n "$current_remote" && "$current_remote" != "$hostname" && "$(grep <<<"$hostnames" -Fx "$current_remote" | wc -l)" -eq 1 ]]; then
    head -n1 <<<"$hostnames"
    printf "%s\n" "$current_remote"
    tail -n+2 <<<"$hostnames" | grep -vFx "$current_remote"
  else
    printf "%s\n" "$hostnames"
  fi

@_a_host:
  # just _nixos_attrnames | gum filter --placeholder "Pick a host..."
  # just _nixos_attrnames | fzf --reverse
  just _nixos_attrnames | fzf

@_some_hosts:
  # just _nixos_attrnames | gum filter --placeholder "Pick a host..." --no-limit
  # just _nixos_attrnames | fzf --reverse --multi
  just _nixos_attrnames | fzf --multi

_nixos_fqdns:
  #!/usr/bin/env -S bash -euo pipefail
  cachefile=.direnv/just-cache-nixos-fqdns.json
  if [[ flake.nix -nt "$cachefile" ]]; then
    mkdir -p .direnv/
    fqdns=$(nix eval .#nixosConfigurations --apply 'builtins.mapAttrs (_: x: x.config.networking.fqdn)' --json 2>/dev/null)
    if [[ "$UID" -ne 0 ]]; then
      cat <<<"$fqdns" >"$cachefile"
    fi
  else
    fqdns=$( cat "$cachefile" )
  fi
  just _nixos_attrnames | jq -R --argjson fqdns "$fqdns" '$fqdns[.]' -r

@_a_fqdn:
  # just _nixos_fqdns | gum filter --placeholder "Pick a host..."
  # just _nixos_fqdns | fzf --reverse
  just _nixos_fqdns | fzf

@_some_fqdns:
  # just _nixos_fqdns | gum filter --placeholder "Pick a host..." --no-limit
  # just _nixos_fqdns | fzf --reverse --multi
  just _nixos_fqdns | fzf --multi

@remote-current:
  # remote-host # slow
  test ! -s .remote.toml || \
  printf "%s\n" "$(tomlq <.remote.toml '.hosts | to_entries[] | select(.value.default==true) | .value.host' -r)"

@remote-list:
  tomlq <.remote.toml '.hosts | to_entries[] | "remote-set \(.key+1) # -> \(.value.host)"' -r

@remote-set remote=`just remote-current >&2 && just _a_remote_label`:
  remote-set "{{remote}}"

motd:
  #!/usr/bin/env -S bash -euo pipefail
  printf "%s\n" "Current remote: $(just remote-current)"
  just remote-list
  printf "\n"
  todos=$(rg 'TODO' | wc -l)
  if [[ "$todos" -gt 10 ]]; then
    printf "%s\n" "There are $todos 'TODO's"
  fi

@_a_remote_label:
  just remote-list | gum filter --placeholder "Pick a remote..." | cut -d' ' -f2

@_some_remote_labels:
  just remote-list | gum filter --placeholder "Pick remotes..." --no-limit | cut -d' ' -f2

@_some_remote_label_names:
  just remote-list | gum filter --placeholder "Pick remotes..." --no-limit | tr ' ' '\t' | cut -f2,5

@_a_remote_fqdn:
  just remote-list | gum filter --placeholder "Pick a remote..." | cut -d' ' -f5-

@_some_remote_fqdns:
  just remote-list | gum filter --placeholder "Pick remote..." --no-limit | cut -d' ' -f5-

_remote_ensure $hostname=`just _a_fqdn`:
  #!/usr/bin/env -S bash -euo pipefail
  if test ! -s .remote.toml; then
    remote-init "$hostname"
  elif tomlq <.remote.toml '.hosts | to_entries[] |.value.host' -r | grep -q "$hostname"; then
    true
  else
    remote-add "$hostname"
  fi

@_remote_label_from_fqdn hostname=`just _a_fqdn`:
  tomlq <.remote.toml '.hosts | to_entries[] | select(.value.host == "{{ hostname }}") | .key+1' -r

remote-mprocs *cmd:
  #!/usr/bin/env -S bash -euo pipefail
  declare -a cmd=("$@")
  if [[ "${#cmd[@]}" -eq 0 ]]; then
    cmd=($(just --summary --unsorted | xargs printf "just %s\n" | gum filter --placeholder "Pick a recipie..."))
    [[ "${#cmd[@]}" -gt 0 ]]
  fi
  escaped_cmd="$(printf "%q " "${cmd[@]}")"
  declare -a labels=()
  declare -a names=()
  label_names=$(just _some_remote_label_names | grep .)
  readarray -td $'\n' labels < <( cut -f1 <<<"$label_names" )
  readarray -td $'\n' names  < <( cut -f2 <<<"$label_names" )
  # TODO: when tmux exits the output is cleared
  # printf "remote --label=%q \"env NIX_NO_NOM=1 ${escaped_cmd//%/%%}\"\n" "${labels[@]}" | xargs -d'\n' mprocs --names "$(IFS=','; printf "%s" "${labels[*]}")"
  printf "tmux new \"remote --label=%q ${escaped_cmd//%/%%} ; read\" \; set-option destroy-unattached\n" "${labels[@]}" | xargs -d'\n' mprocs --names "$(IFS=','; printf "%s" "${names[*]}")"

@remote-mprocs-init +$cmd=`printf "just %s\n" $(just --summary --unsorted) | gum filter --placeholder "Pick a recipie..."`:
  just _some_fqdns | xe -s 'just _remote_ensure "$1"; just _remote_label_from_fqdn "$1"' | sed -E 's/(.*)/remote --label="\1" "env NIX_NO_NOM=1 $cmd"/g' | xargs -d'\n' mprocs

# TODO: support multiple labels, run in tmux
#remote label=`just _a_remote_label` cmd=`printf "just %s\n" $(just --summary --unsorted) | gum filter --placeholder "Pick a recipie..."`:
#  remote --label={{label}} {{cmd}}

# TODO: packaged as nix-web
@gorgon:
  # https://codeberg.org/gorgon/gorgon/src/branch/main/nix-web
  # https://discourse.nixos.org/t/a-web-ui-for-the-nix-store-early-beta/35762
  nix run 'git+https://codeberg.org/gorgon/gorgon#nix-web'
