#     ____      ____
#    / __/___  / __/
#   / /_/_  / / /_
#  / __/ / /_/ __/
# /_/   /___/_/ key-bindings.nu
#
# - $FZF_TMUX               (default: 0)
# - $FZF_TMUX_OPTS
# - $FZF_TMUX_HEIGHT        (default: 40%)
# - $FZF_CTRL_T_COMMAND     (set to "" to disable)
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND     (set to "" to disable)
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND      (set to "" to disable)
# - $FZF_ALT_C_OPTS

# Code provided by @igor-ramazanov
# Source: https://github.com/junegunn/fzf/issues/4122#issuecomment-2607368316

# Merge default options in the same order as bash/zsh:
#   1. --height, --min-height, --bind=ctrl-z:ignore, $prepend
#   2. $FZF_DEFAULT_OPTS_FILE contents
#   3. $FZF_DEFAULT_OPTS, $append
def __fzf_defaults [prepend: string, append: string]: nothing -> string {
  let base = $"--height ($env.FZF_TMUX_HEIGHT? | default '40%') --min-height 20+ --bind=ctrl-z:ignore ($prepend)"
  let opts_file = if ($env.FZF_DEFAULT_OPTS_FILE? | default '' | is-not-empty) {
    try { open --raw ($env.FZF_DEFAULT_OPTS_FILE) | str trim } catch { '' }
  } else {
    ''
  }
  let default_opts = $env.FZF_DEFAULT_OPTS? | default ''
  $"($base) ($opts_file) ($default_opts) ($append)" | str trim
}

# Return the fzf command to use: fzf-tmux when inside tmux and
# FZF_TMUX is enabled or FZF_TMUX_OPTS is set, plain fzf otherwise.
def __fzfcmd []: nothing -> list<string> {
  let in_tmux = ($env.TMUX_PANE? | default '' | into string | is-not-empty)
  if $in_tmux {
    let fzf_tmux = ($env.FZF_TMUX? | default 0 | into string)
    let fzf_tmux_opts = ($env.FZF_TMUX_OPTS? | default '' | into string)
    if ($fzf_tmux != '0') or ($fzf_tmux_opts | is-not-empty) {
      let opts = if ($fzf_tmux_opts | is-not-empty) { $fzf_tmux_opts } else { $"-d($env.FZF_TMUX_HEIGHT? | default '40%')" }
      return ['fzf-tmux' ...(($opts | split row ' ' | where { $in != '' })) '--']
    }
  }
  ['fzf']
}


export-env {
  $env.FZF_CTRL_T_OPTS     = $env.FZF_CTRL_T_OPTS?     | default ""
  $env.FZF_CTRL_R_OPTS     = $env.FZF_CTRL_R_OPTS?     | default ""
  $env.FZF_ALT_C_OPTS      = $env.FZF_ALT_C_OPTS?      | default ""
}

# Directories
const alt_c = {
    name: fzf_dirs
    modifier: alt
    keycode: char_c
    mode: [emacs, vi_normal, vi_insert]
    event: [
      {
        send: executehostcommand
        cmd: "
          let fzf_opts = (__fzf_defaults '--reverse --walker=dir,follow,hidden --scheme=path' $'($env.FZF_ALT_C_OPTS) +m');
          let fzfcmd = (__fzfcmd);
          let fzf_args = ($fzfcmd | skip 1);
          let alt_c_cmd = ($env.FZF_ALT_C_COMMAND? | default null);
          let result = if ($alt_c_cmd == null) or ($alt_c_cmd | is-empty) {
            with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args }
          } else {
            let fzf_cmd_str = ($fzfcmd | str join ' ');
            let sh_cmd = [$alt_c_cmd '|' $fzf_cmd_str] | str join ' ';
            with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^sh -c $sh_cmd }
          };
          if ($result | is-not-empty) { cd $result };
        "
      }
    ]
}

# History
const ctrl_r = {
  name: fzf_history
  modifier: control
  keycode: char_r
  mode: [emacs, vi_insert, vi_normal]
  event: [
    {
      send: executehostcommand
      cmd: "commandline edit --replace (
        let fzf_opts = (__fzf_defaults '' $'--scheme=history --bind=ctrl-r:toggle-sort --wrap-sign \"\t↳ \" --highlight-line ($env.FZF_CTRL_R_OPTS) +m --read0');
        let fzfcmd = (__fzfcmd);
        let fzf_args = ($fzfcmd | skip 1);
        # reverse | uniq: show most recent first, deduplicate keeping the latest.
        # Nushell's `history` loads the full history as an in-memory table
        # (bounded by $env.config.history.max_size, default 100,000), so
        # reverse and uniq run on an already-materialized list. This is O(n)
        # but acceptable for typical history sizes; unlike bash/zsh `fc -r`,
        # there is no streaming primitive that would let fzf show the latest
        # entries before the full list is consumed.
        history
          | get command
          | reverse
          | uniq
          | str join (char -i 0)
          | with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args --query (commandline) }
          | decode utf-8
          | str trim
      )"
    }
  ]
}

# Files
const ctrl_t =  {
    name: fzf_files
    modifier: control
    keycode: char_t
    mode: [emacs, vi_normal, vi_insert]
    event: [
      {
        send: executehostcommand
        cmd: "
          let fzf_opts = (__fzf_defaults '--reverse --walker=file,dir,follow,hidden --scheme=path' $'($env.FZF_CTRL_T_OPTS) -m');
          let fzfcmd = (__fzfcmd);
          let fzf_args = ($fzfcmd | skip 1);
          let ctrl_t_cmd = ($env.FZF_CTRL_T_COMMAND? | default null);
          let result = if ($ctrl_t_cmd == null) or ($ctrl_t_cmd | is-empty) {
            with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args }
          } else {
            let fzf_cmd_str = ($fzfcmd | str join ' ');
            let sh_cmd = [$ctrl_t_cmd '|' $fzf_cmd_str] | str join ' ';
            with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^sh -c $sh_cmd }
          };
          let result = ($result | str replace --all (char newline) ' ' | str trim);
          commandline edit --append $result;
          commandline set-cursor --end
        "
      }
    ]
}

# Helper to check if a binding is enabled. A binding is disabled when
# the corresponding *_COMMAND variable is explicitly set to "".
# When not defined (null), the binding is enabled (using fzf's built-in walker).
def __fzf_binding_enabled [var_name: string]: nothing -> bool {
  let val = ($env | get -o $var_name)
  # null = not defined = enabled; "" = explicitly disabled
  $val == null or ($val | into string | is-not-empty)
}

# Update the $env.config
export-env {
  let fzf_names = ['fzf_files', 'fzf_dirs', 'fzf_history']
  # Filter out any existing fzf bindings, then re-add the enabled ones.
  # This allows re-sourcing to update bindings (e.g. after changing
  # FZF_CTRL_T_COMMAND) without creating duplicates.
  mut bindings = ($env.config.keybindings | where { |kb| $kb.name not-in $fzf_names })
  if (__fzf_binding_enabled 'FZF_ALT_C_COMMAND') { $bindings = ($bindings | append $alt_c) }
  if (__fzf_binding_enabled 'FZF_CTRL_R_COMMAND') { $bindings = ($bindings | append $ctrl_r) }
  if (__fzf_binding_enabled 'FZF_CTRL_T_COMMAND') { $bindings = ($bindings | append $ctrl_t) }
  $env.config.keybindings = $bindings
}
