#!/usr/bin/env bash

brt_edit() {
  local file="${1:-}"
  if [[ -z "$file" ]]; then
    brt_error "Usage: brt -edit <file>"
    exit 1
  fi

  if [[ ! -t 0 || ! -t 1 ]]; then
    brt_error "Editor requires an interactive terminal"
    exit 1
  fi

  # Prefer real nano when available — same UX, fewer terminal quirks.
  if [[ -z "${BRT_EDITOR_FORCE:-}" ]] && command -v nano &>/dev/null; then
    nano "$file"
    return 0
  fi

  local tmp="${file}.brt_edit_$$"
  [[ -f "$file" ]] && cp "$file" "$tmp" || : > "$tmp"

  local -a lines=()
  local cur_line=1
  local cur_col=1
  local scroll_top=1
  local dirty=0
  local cols rows view_h
  local old_stty=""
  local prompt=""
  local prompt_reply=""
  local paste_buffer=""
  local EDITOR_PASTE_TEXT=""

  _load() {
    lines=()
    if [[ -s "$tmp" ]]; then
      while IFS= read -r line || [[ -n "$line" ]]; do
        lines+=("$line")
      done < "$tmp"
    fi
    [[ ${#lines[@]} -eq 0 ]] && lines=("")
    if (( cur_line < 1 )); then cur_line=1; fi
    if (( cur_line > ${#lines[@]} )); then cur_line=${#lines[@]}; fi
    _clamp_col
  }

  _clamp_col() {
    local len=${#lines[$((cur_line - 1))]}
    if (( cur_col < 1 )); then cur_col=1; fi
    if (( cur_col > len + 1 )); then cur_col=$((len + 1)); fi
  }

  _save() {
    if ((${#lines[@]} == 1 && -z "${lines[0]}")); then
      : > "$file"
    else
      printf '%s\n' "${lines[@]}" > "$file"
    fi
    cp "$file" "$tmp"
    dirty=0
  }

  _term_size() {
    cols=$(tput cols 2>/dev/null || echo 80)
    rows=$(tput lines 2>/dev/null || echo 24)
    view_h=$((rows - 5))
    if (( view_h < 1 )); then view_h=1; fi
  }

  _ensure_visible() {
    _term_size
    if (( cur_line < scroll_top )); then
      scroll_top=$cur_line
    elif (( cur_line >= scroll_top + view_h )); then
      scroll_top=$((cur_line - view_h + 1))
    fi
    if (( scroll_top < 1 )); then scroll_top=1; fi
  }

  _insert_text() {
    local text="$1"
    [[ -z "$text" ]] && return 0
    text="${text//$'\r'/}"
    text="${text%$'\n'}"

    local line="${lines[$((cur_line - 1))]}"
    local before="${line:0:cur_col - 1}"
    local after="${line:cur_col - 1}"
    local -a paste_lines=()
    local remaining last

    if [[ "$text" == *$'\n'* ]]; then
      remaining="$text"
      while [[ "$remaining" == *$'\n'* ]]; do
        paste_lines+=("${remaining%%$'\n'*}")
        remaining="${remaining#*$'\n'}"
      done
      paste_lines+=("$remaining")
    else
      paste_lines=("$text")
    fi

    paste_lines[0]="${before}${paste_lines[0]}"
    last=$((${#paste_lines[@]} - 1))
    paste_lines[$last]="${paste_lines[$last]}${after}"

    local head=("${lines[@]:0:$((cur_line - 1))}")
    local tail=("${lines[@]:cur_line}")
    lines=("${head[@]}" "${paste_lines[@]}" "${tail[@]}")

    cur_line=$((cur_line + last))
    cur_col=$((${#paste_lines[$last]} - ${#after} + 1))
    dirty=1
  }

  _clipboard_copy() {
    local text="$1"
    [[ -z "$text" ]] && return 0
    if command -v pbcopy &>/dev/null; then
      printf '%s' "$text" | pbcopy
    elif command -v wl-copy &>/dev/null; then
      printf '%s' "$text" | wl-copy
    elif command -v xclip &>/dev/null; then
      printf '%s' "$text" | xclip -selection clipboard
    elif command -v xsel &>/dev/null; then
      printf '%s' "$text" | xsel --clipboard --input
    fi
  }

  _clipboard_paste() {
    if command -v pbpaste &>/dev/null; then
      pbpaste
    elif command -v wl-paste &>/dev/null; then
      wl-paste 2>/dev/null
    elif command -v xclip &>/dev/null; then
      xclip -selection clipboard -o 2>/dev/null
    elif command -v xsel &>/dev/null; then
      xsel --clipboard --output 2>/dev/null
    fi
  }

  _copy_line() {
    paste_buffer="${lines[$((cur_line - 1))]}"
    _clipboard_copy "$paste_buffer"
  }

  _paste_system() {
    local text
    text=$(_clipboard_paste)
    [[ -n "$text" ]] && _insert_text "$text"
  }

  _paste_buffer() {
    [[ -n "$paste_buffer" ]] && _insert_text "$paste_buffer"
  }

  _insert_char() {
    local ch="$1"
    local line="${lines[$((cur_line - 1))]}"
    local before="${line:0:cur_col - 1}"
    local after="${line:cur_col - 1}"
    lines[$((cur_line - 1))]="${before}${ch}${after}"
    ((cur_col++)) || true
    dirty=1
  }

  _backspace() {
    if (( cur_col > 1 )); then
      local line="${lines[$((cur_line - 1))]}"
      lines[$((cur_line - 1))]="${line:0:cur_col - 2}${line:cur_col - 1}"
      ((cur_col--)) || true
      dirty=1
    elif (( cur_line > 1 )); then
      local prev="${lines[$((cur_line - 2))]}"
      local cur="${lines[$((cur_line - 1))]}"
      cur_col=$((${#prev} + 1))
      lines[$((cur_line - 2))]="${prev}${cur}"
      local nb=() na=()
      nb=("${lines[@]:0:$((cur_line - 1))}")
      na=("${lines[@]:cur_line}")
      lines=("${nb[@]}" "${na[@]}")
      ((cur_line--)) || true
      dirty=1
    fi
  }

  _delete_char() {
    local line="${lines[$((cur_line - 1))]}"
    local len=${#line}
    if (( cur_col <= len )); then
      lines[$((cur_line - 1))]="${line:0:cur_col - 1}${line:cur_col}"
      dirty=1
    elif (( cur_line < ${#lines[@]} )); then
      lines[$((cur_line - 1))]="${line}${lines[$cur_line]}"
      local nb=() na=()
      nb=("${lines[@]:0:cur_line}")
      na=("${lines[@]:$((cur_line + 1))}")
      lines=("${nb[@]}" "${na[@]}")
      dirty=1
    fi
  }

  _enter() {
    local line="${lines[$((cur_line - 1))]}"
    local before="${line:0:cur_col - 1}"
    local after="${line:cur_col - 1}"
    lines[$((cur_line - 1))]="$before"
    local head=() tail=()
    head=("${lines[@]:0:cur_line}")
    tail=("${lines[@]:cur_line}")
    lines=("${head[@]}" "$after" "${tail[@]}")
    ((cur_line++)) || true
    cur_col=1
    dirty=1
  }

  _cut_line() {
    paste_buffer="${lines[$((cur_line - 1))]}"
    _clipboard_copy "$paste_buffer"
    if ((${#lines[@]} <= 1)); then
      lines=("")
      cur_line=1
      cur_col=1
    else
      local nb=() na=()
      nb=("${lines[@]:0:$((cur_line - 1))}")
      na=("${lines[@]:cur_line}")
      lines=("${nb[@]}" "${na[@]}")
      if (( cur_line > ${#lines[@]} )); then cur_line=${#lines[@]}; fi
      cur_col=1
    fi
    dirty=1
  }

  _move_up() {
    if (( cur_line <= 1 )); then return 0; fi
    ((cur_line--)) || true
    _clamp_col
    _ensure_visible
  }

  _move_down() {
    if (( cur_line >= ${#lines[@]} )); then return 0; fi
    ((cur_line++)) || true
    _clamp_col
    _ensure_visible
  }

  _move_left() {
    if (( cur_col > 1 )); then
      ((cur_col--)) || true
    elif (( cur_line > 1 )); then
      ((cur_line--)) || true
      cur_col=$((${#lines[$((cur_line - 1))]} + 1))
    fi
    _ensure_visible
  }

  _move_right() {
    local len=${#lines[$((cur_line - 1))]}
    if (( cur_col <= len )); then
      ((cur_col++)) || true
    elif (( cur_line < ${#lines[@]} )); then
      ((cur_line++)) || true
      cur_col=1
    fi
    _ensure_visible
  }

  _move_home() { cur_col=1; }
  _move_end() { cur_col=$((${#lines[$((cur_line - 1))]} + 1)); }

  _tty_raw_on() {
    stty -echo -icanon min 1 time 0 -ixon 2>/dev/null || \
      stty -echo -icanon min 1 time 0 2>/dev/null || true
  }

  _tty_raw_off() {
    stty echo icanon 2>/dev/null || true
  }

  _decode_key_byte() {
    case "$1" in
      $'\x18') printf 'ctrl-x' ;;
      $'\x0f') printf 'ctrl-o' ;;
      $'\x07') printf 'ctrl-g' ;;
      $'\x0b') printf 'ctrl-k' ;;
      $'\x03') printf 'ctrl-c' ;;
      $'\x16') printf 'ctrl-v' ;;
      $'\x15') printf 'ctrl-u' ;;
      $'\x08'|$'\x7f') printf 'backspace' ;;
      $'\x04') printf 'delete' ;;
      $'\r'|$'\n') printf 'enter' ;;
      $'\t') printf '' ;;
      *)
        if [[ "$1" < $'\x20' ]]; then
          printf ''
        else
          printf '%s' "$1"
        fi
        ;;
    esac
  }

  _decode_csi() {
    local seq="$1"
    local last="${seq: -1}"
    case "$last" in
      A) printf 'up' ;;
      B) printf 'down' ;;
      C) printf 'right' ;;
      D) printf 'left' ;;
      H) printf 'home' ;;
      F) printf 'end' ;;
      ~)
        case "$seq" in
          1|'1'|'1;2'|'1;5') printf 'home' ;;
          2|'2'|'2;2'|'2;5') printf 'insert' ;;
          3|'3'|'3;2'|'3;5') printf 'delete' ;;
          4|'4'|'4;2'|'4;5') printf 'end' ;;
          5|'5'|'5;2'|'5;5') printf 'pageup' ;;
          6|'6'|'6;2'|'6;5') printf 'pagedown' ;;
          *) printf 'esc' ;;
        esac
        ;;
      *) printf 'esc' ;;
    esac
  }

  _read_bracketed_paste_content() {
    local content="" c pending=""
    EDITOR_PASTE_TEXT=""
    while IFS= read -rsn1 c; do
      if [[ -z "$pending" ]]; then
        if [[ "$c" == $'\e' ]]; then
          pending="$c"
          continue
        fi
        content+="$c"
        continue
      fi
      pending+="$c"
      if [[ "$pending" == $'\e[201~' ]]; then
        EDITOR_PASTE_TEXT="$content"
        printf 'paste'
        return 0
      fi
      if ((${#pending} >= 5)); then
        content+="$pending"
        pending=""
      fi
    done
    EDITOR_PASTE_TEXT="$content"
    printf 'paste'
  }

  _read_key() {
    local k k2 seq c i
    IFS= read -rsn1 k || return 1

    if [[ $k != $'\x1b' ]]; then
      _decode_key_byte "$k"
      return 0
    fi

    if ! IFS= read -rsn1 -t 1 k2; then
      printf 'esc'
      return 0
    fi

    case "$k2" in
      '[')
        seq=""
        for (( i=0; i<16; i++ )); do
          if ! IFS= read -rsn1 -t 1 c; then
            break
          fi
          seq+="$c"
          if [[ "$seq" == "200~" ]]; then
            _read_bracketed_paste_content
            return 0
          fi
          [[ "$c" =~ [A-Za-z~] ]] && break
        done
        _decode_csi "$seq"
        ;;
      O)
        if ! IFS= read -rsn1 -t 1 c; then
          printf 'esc'
          return 0
        fi
        case "$c" in
          A) printf 'up' ;;
          B) printf 'down' ;;
          C) printf 'right' ;;
          D) printf 'left' ;;
          H) printf 'home' ;;
          F) printf 'end' ;;
          *) printf 'esc' ;;
        esac
        ;;
      *)
        printf 'esc'
        ;;
    esac
  }

  _draw_help() {
    _tty_raw_off
    clear
    cat <<'HELP'
  brt editor — shortcuts

  Arrow keys       Move cursor
  Home / End       Start / end of line
  Enter            New line
  Backspace/Del    Delete character
  ^K               Cut line (copies to clipboard)
  ^C               Copy line to clipboard
  ^V               Paste from clipboard
  ^U               Paste last cut/copied line
  Cmd+V            Paste (terminal bracketed paste)
  ^O               Save file
  ^X               Exit (prompts if modified)
  ^G               This help

  Set BRT_EDITOR_FORCE=1 to use the built-in editor instead of nano.
  Press any key to return...
HELP
    IFS= read -rsn1 _ 2>/dev/null || read -r _ || true
    _tty_raw_on
  }

  _draw() {
    _term_size
    printf '\033[H\033[J\033[?25l'

    local mod=""
    if (( dirty )); then mod=" [Modified]"; fi
    printf '\033[7m  brt — %s%s\033[0m\n' "$file" "$mod"
    printf '%*s\n' "$cols" '' | tr ' ' '-'

    local i row=0
    for ((i = scroll_top; i < scroll_top + view_h; i++)); do
      if (( i > ${#lines[@]} )); then break; fi
      local marker=' '
      if (( i == cur_line )); then marker='>'; fi
      local text="${lines[$((i - 1))]}"
      if ((${#text} > cols - 8)); then
        text="${text:0:cols - 11}..."
      fi
      if (( i == cur_line )); then
        printf '\033[33m%s %4d \033[0m%s\n' "$marker" "$i" "$text"
      else
        printf '%s %4d  %s\n' "$marker" "$i" "$text"
      fi
      ((row++)) || true
    done

    while (( row < view_h )); do
      printf '~\n'
      ((row++)) || true
    done

    if [[ -n "$prompt" ]]; then
      printf '\033[7m%s\033[0m\n' "$prompt"
    else
      printf 'Line %d/%d  Col %d\n' "$cur_line" "${#lines[@]}" "$cur_col"
      printf '^\033[32mX\033[0m Exit  ^\033[32mO\033[0m Save  ^\033[32mC\033[0m Copy  ^\033[32mV\033[0m Paste  ^\033[32mG\033[0m Help\n'
    fi

    local disp_row=$((cur_line - scroll_top + 3))
    local disp_col=$((cur_col + 7))
    if (( disp_row >= 3 && disp_row < rows - 2 )); then
      printf '\033[%d;%dH\033[?25h' "$disp_row" "$disp_col"
    else
      printf '\033[?25h'
    fi
  }

  _quit_confirm() {
    if (( ! dirty )); then
      return 0
    fi
    _tty_raw_off
    prompt='Save modified buffer before exit? [Y/n]'
    _draw
    IFS= read -rsn1 prompt_reply || prompt_reply=""
    prompt=""
    _tty_raw_on
    case "$prompt_reply" in
      ''|y|Y)
        _save
        return 0
        ;;
      n|N) return 0 ;;
      *) return 1 ;;
    esac
  }

  _load

  if ! old_stty=$(stty -g 2>/dev/null); then
    brt_error "Could not configure terminal for editor"
    rm -f "$tmp"
    exit 1
  fi

  cleanup() {
    stty "$old_stty" 2>/dev/null || true
    printf '\033[?2004l\033[?25h\033[0m\n'
    clear
    rm -f "$tmp"
  }
  trap cleanup EXIT INT TERM

  _tty_raw_on
  printf '\033[?2004h'

  while true; do
    _ensure_visible
    _draw
    local key
    key=$(_read_key) || break
    key=${key:-}

    if [[ -n "$prompt" ]]; then
      continue
    fi

    case "$key" in
      up) _move_up ;;
      down) _move_down ;;
      left) _move_left ;;
      right) _move_right ;;
      home) _move_home ;;
      end) _move_end ;;
      pageup) _move_up; _move_up; _move_up; _move_up; _move_up ;;
      pagedown) _move_down; _move_down; _move_down; _move_down; _move_down ;;
      backspace) _backspace ;;
      delete|insert) _delete_char ;;
      enter) _enter ;;
      ctrl-o)
        _save
        prompt='File saved'
        _draw
        sleep 1
        prompt=''
        ;;
      ctrl-c) _copy_line ;;
      ctrl-v) _paste_system ;;
      ctrl-u) _paste_buffer ;;
      ctrl-k) _cut_line ;;
      ctrl-g) _draw_help ;;
      ctrl-x)
        if _quit_confirm; then
          break
        fi
        ;;
      paste) _insert_text "$EDITOR_PASTE_TEXT" ;;
      esc) ;;
      '')
        ;;
      *)
        _insert_char "$key"
        ;;
    esac
  done
}
