use std/dirs

$env.config.keybindings = [
  {
    name: "next_dir",
    modifier: none,
    keycode: char_>,
    mode: vi_normal,
    event: {
      send: executehostcommand,
      cmd: "dirs next"
    }
  },
  {
    name: "prev_dir",
    modifier: none,
    keycode: char_<,
    mode: vi_normal,
    event: {
      send: executehostcommand,
      cmd: "dirs prev"
    }
  }
]

# change cwd
def --env d [q?: string = ~, --skip-nix-shell] {
  let dir = if ($q | path exists) {
    $q | path expand
  } else match $q {
    '-' => $env.OLDPWD,
    _ => (zoxide query $q)
  }

  if $dir == "" { error make -u { msg: "invalid path" } }

  cd $dir
  zoxide add $dir

  if "NVIM" in $env {
    send-to-nvim-cmd $env.NVIM $"<cmd>cd ($dir)<CR>"
  }

  if $skip_nix_shell {
    $dir | enter-nix-shell
  }
}

def enter-nix-shell []: path -> nothing {
  path split
  | skip # skip root dir
  | generate {|e, acc=""|
    let a = $acc + "/" + $e
    {out: $a, next: $a}
  }
  | _enter-nix-shell
}

def _enter-nix-shell []: list<path> -> nothing {
  let src = if ($in | is-empty) { return } else $in
  let car = $src | first
  let cdr = $src | skip

  if ($car | path join shell.nix | path exists) {
    nix-shell $car --command $"nu -e '($cdr) | _enter-nix-shell'"
  } else {
    $cdr | _enter-nix-shell
  }
}

def --env mkcd [dir: path] {
  mkdir $dir
  d $dir
}

def --wrapped ggrks [...q] {
  vivaldi https://startpage.com/sp/search?q=($q | str join +)
}

# $ "/path/to/dir" | path relative-to "/other/path" # "../../path/to/dir"
def "path relative-to" [base: path]: path -> path {
  let target = $in | path expand | path split
  let common = $target | zip $base | take while {|e| $e.0 == $e.1 } | length

  $base
  | path expand
  | path split
  | skip $common
  | each { ".." }
  | append ($target | skip $common)
  | path join
}

def "from env" []: [
  string -> record
  list<string> -> record
] {
  if { describe } == "string" {
    lines
  } else ()
  | parse "{1}={2}"
  | transpose --as-record --header-row
}

# for git
def --env setup-ssh [secret: path = ~/.ssh/id_ed25519] {
  ssh-agent -s
  | lines
  | take 2
  | str replace -r ";.*" ""
  | from env
  | load-env
  ssh-add $secret
}

def "path split" [] {
  path basename
  | split column '.'
  | transpose --ignore-titles
  | get column0
}

def "path stem" [] {
  path split | first
}

def "path ext" [] {
  path split | skip | str join '.'
}

def extract [fname: path] {
  match ($fname | path ext) {
    .zip => { unzip $fname }
    .tar.gz => { tar xzvf $fname }
    .tar.bz2 => { tar xjvf $fname }
    _ => { error make -u { msg: $"Unsupported file type: ($in)" } }
  }
}

def send-to-nvim-cmd [server: path, cmd: string] {
  nvim --server $server --remote-send $cmd
}

def --wrapped n [...rest: string] {
  if "NVIM" in $env {
    send-to-nvim-cmd $env.NVIM $"<cmd>tabe ($rest | str join ' ')<CR>"
    return
  }
  neovide ...$rest
}

def normalize-history [] {
  open $nu.history-path
  | tr -s ' '
  | str replace --all --regex ' $' ''
  | lines | reverse | uniq | reverse
  | save -f $nu.history-path
}

def peek-asm [file: path] {
  clang -O3 -march=native -masm=intel -S -o- $file
}

def wait-for-process-done [pid: string] {
  while (ps | get pid | find $pid | is-not-empty) {
    sleep 10sec
  }
}

def on-done [
  callback: closure,
  --pid (-p): int = 0,
  --name (-n): string = ""
] {
  let process = if $pid == 0 {
    pidof $name | split words | first
  } else if $name != "" {
    $pid | into string
  } else {
    error make -u { msg: "Usage: (-p <int>|-n <string>)" }
  }

  wait-for-process-done $process
  do $callback
}

def b []: nothing -> float {
  let full = cat /sys/class/power_supply/BAT0/energy_full | into int
  let now = cat /sys/class/power_supply/BAT0/energy_now | into int
  $now / $full * 100
}

def "bl get" []: nothing -> float {
  let full = cat /sys/class/backlight/amdgpu_bl1/max_brightness | into float
  let now = cat /sys/class/backlight/amdgpu_bl1/brightness | into float
  $now / $full * 100
}

def "bl set" [percent: float] {
  let full = cat /sys/class/backlight/amdgpu_bl1/max_brightness | into float
  $percent / 100 * $full | into int | save -f /sys/class/backlight/amdgpu_bl1/brightness
}

def nq [...rest] {
  nix search nixpkgs ...$rest --json --quiet
  | from json
  | transpose
  | get column1
  | reject version
}

def nix-package-query [context: string, --sync] {
  let cache_name = $"($nu.cache-dir)/nix-pkgs.txt"
  if not ($nu.cache-dir | path exists) { mkdir $nu.cache-dir }

  if not ($cache_name | path exists) or $sync {
    nix search nixpkgs ^ --json
    | from json
    | transpose
    | get column1.pname
    | tee { save $cache_name }
  } else {
    open $cache_name | lines
  }
  | collect
}

def nsh [...pkg: string@nix-package-query] {
  nix-shell --command nu -p ...$pkg
}

extern rad [sub?: string@rad-subcmd, subsub?: string@rad-subsubcmd]

def rad-subcmd [] {
  rad --help | parse --regex '  (?<value>[a-z]+) +(?<description>.*)'
}

def rad-subsubcmd [ctx: string] {
  match ($ctx | split words | last) {
    auth => [
      { value: "--alias", description: "When initializing an identity, sets the node alias" }
      { value: "--stdin", description: "Read passphrasee from stdin (default: false)" }
    ]
    checkout => [
      { value: "--remote", description: "Remote peer to checkout" }
      { value: "--no-confirm", description: "Don't ask for confirmation during checkout" }
    ]
    clone => [
      { value: "--bare", description: "Make a bare repository" }
      { value: "--scope", description: "Follow scope: `followed` or `all` (default: all)" }
      { value: "--seed", description: "Clone from this seed (may be specified multiple times)" }
      { value: "--timeout", description: "Timeout for fetching repository (default: 9)" }
    ]
    config => [
      show
      init
      edit
      get
      schema
      set
      unset
      push
      remove
    ]
    id => [
      list
      update
      edit
      show
      accept
      redact
    ]
    init => [
      { value: "--name", description: "Name of the repository" }
      { value: "--description", description: "Description of the repository" }
      { value: "--default-branch", description: "The default branch of the repository" }
      { value: "--scope", description: "Repository follow scope: `followed` or `all` (default: all)" }
      { value: "--private", description: "Set repository visibility to *private*" }
      { value: "--public", description: "Set repository visibility to *public*" }
      { value: "--existing", description: "Setup repository as an existing Radicle repository" }
      { value: "--set-upstream", description: "Setup the upstream of the default branch" }
      { value: "--setup-signing", description: "Setup the radicle key as a signing key for this repository" }
      { value: "--no-confirm", description: "Don't ask for confirmation during setup" }
      { value: "--no-seed", description: "Don't seed this repository after initializing it" }
      { value: "--verbose", description: "Verbose mode" }
    ]
    inbox => [
      list
      show
      clear
      { value: "--all", description: "Operate on all repositories" }
      { value: "--repo", description: "Operate on the given repository (default: rad .)" }
      { value: "--sort-by", description: "Sort by `id` or `timestamp` (default: timestamp)" }
      { value: "--reverse", description: "Reverse the list" }
      { value: "--show-unknown", description: "Show any updates that were not recognized" }
    ]
    inspect => [
      { value: "--rid", description: "Return the repository identifier (RID)" }
      { value: "--payload", description: "Inspect the repository's identity payload" }
      { value: "--refs", description: "Inspect the repository's refs on the local device" }
      { value: "--sigrefs", description: "Inspect the values of `rad/sigrefs` for all remotes of this repository" }
      { value: "--identity", description: "Inspect the identity document" }
      { value: "--visibility", description: "Inspect the repository's visibility" }
      { value: "--delegates", description: "Inspect the repository's delegates" }
      { value: "--policy", description: "Inspect the repository's seeding policy" }
      { value: "--history", description: "Show the history of the repository identity document" }
    ]
    issue => [
      delete
      edit
      list
      open
      react
      assign
      label
      comment
      show
      state
      cache
      { value: "--repo", description: "Operate on the given repository (default: cwd)" }
      { value: "--no-announce", description: "Don't announce issue to peers" }
      { value: "--header", description: "Show only the issue header, hiding the comments" }
      { value: "--quiet", description: "Don't print anything" }
    ]
    ls => [
      { value: "--private", description: "Show only private repositories" }
      { value: "--public", description: "Show only public repositories" }
      { value: "--seeded", description: "Show all seeded repositories" }
      { value: "--all", description: "Show all repositories in storage" }
      { value: "--verbose", description: "Verbose output" }
    ]
    node => [
      status
      start
      stop
      logs
      debug
      connect
      routing
      inventory
      events
      config
      db
    ]
    patch => [
      { value: "list", description: "List patched in the current repository. The default is --open." }
      { value: "show", description: "Shows information on the given patch." }
      { value: "diff", description: "Outputs the patch diff, using Radicle's diffing tool." }
      { value: "edit", description: "Edits a patch revision comment." }
      { value: "ready", description: "Mark a patch as ready to review." }
      { value: "review", description: "Review a patch. Indicate acceptance or rejection of a patch revision along with a comment." }
      { value: "archive", description: "Archive a patch." }
      { value: "set", description: "Set the currrent branch upstream to a patch reference." }
      { value: "update", description: "Updates a paatch to the current repository HEAD." }
      { value: "checkout", description: "Switch to a given patch, by creating a branch that points to the patch head." }
      { value: "comment", description: "Comment on a patch revision, optionally replying to an existing comment." }
      delete
      label
      redact
    ]
    clean => [
      { value: "--no-confirm", description: "Do not ask for confirmation before removal (default: false)" }
    ]
    self => [
      { value: "--did", description: "Show your DID" }
      { value: "--alias", description: "Show your Node alias" }
      { value: "--home", description: "Show your Radicle home" }
      { value: "--config", description: "Show the location of your configuration file" }
      { value: "--ssh-key", description: "Show your public key in OpenSSH format" }
      { value: "--ssh-fingerprint", description: "Show your public key fingerprint in OpenSSH format" }
    ]
    seed => [
      { value: "--fetch", description: "Fetch repository after updating seeding policy" }
      { value: "--no-fetch", description: "Fetch repository after updating seeding policy" }
      { value: "--from", description: "Fetch from the given node (may be specified multiple times)" }
      { value: "--timeout", description: "Fetch timeout in seconds (default: 9)" }
      { value: "--scope", description: "Peer follow scope for this repository" }
      { value: "--verbose", description: "Verbose output" }
    ]
    follow => [
      { value: "--alias", description: "Associate an alias to a followed peer" }
      { value: "--verbose", description: "Verbose output" }
    ]
    unfollow => [
      --verbose
    ]
    remote => [
      list
      add
      rm
    ]
    sync => [
      { value: "--sort-by", description: "Sort the table by column (options: nid, alias, status)" }
      { value: "--fetch", description: "Turn on fetching (default: true)" }
      { value: "--announce", description: "Turn on ref announcing (default: true)" }
      { value: "--inventory", description: "Turn on inventory announcing (default: false)" }
      { value: "--timeout", description: "How many seconds to wait while syncing" }
      { value: "--seed", description: "Sync with the given node (may be specified multiple times)" }
      { value: "--replicas", description: "Sync with a specific number of seeds" }
      { value: "--replicas-max", description: "Sync with an upper bound number of seeds" }
      { value: "--verbose", description: "Verbose output" }
      { value: "--debug", description: "Print debug information afer sync" }
    ]
  }
}