module Rcurses
  module Input
    def getchr(t = nil, flush: true)
      begin
        # Reinforce raw mode on every call. External programs (e.g. hyperlist)
        # can leave the terminal in cooked/echo mode; raw! fixes it without
        # the save/restore cycle of getch's raw{} block.
        $stdin.raw!
        # 1) Read a byte (with optional timeout)
        #    Use IO.select for timeout (clean syscall, no background thread).
        if t
          return nil unless IO.select([$stdin], nil, nil, t)
        end
        c = $stdin.getc


        # 2) If it's ESC, read escape sequence byte-by-byte
        #    (greedy read_nonblock can swallow the NEXT keypress)
        seq = c
        if c == "\e"
          if IO.select([$stdin], nil, nil, 0.05)
            begin
              b = $stdin.read_nonblock(1)
              seq << b
              # CSI sequence: \e[ ... (letter or ~)
              # SS3 sequence: \eO (one more byte)
              if b == '[' || b == 'O'
                while IO.select([$stdin], nil, nil, 0.05)
                  b = $stdin.read_nonblock(1)
                  seq << b
                  # CSI terminates on a letter (A-Z, a-z) or ~
                  # SS3 terminates after one char
                  break if b =~ /[A-Za-z~]/
                end
              end
            rescue IO::WaitReadable, EOFError
            end
          end
        end



        # 3) Single ESC alone
        return "ESC" if seq == "\e"

        # 4) Shift‐TAB
        return "S-TAB" if seq == "\e[Z"

        # 5) Legacy single‐char shift‐arrows (your old working ones)
        case seq
        when "\e[a" then return "S-UP"
        when "\e[b" then return "S-DOWN"
        when "\e[c" then return "S-RIGHT"
        when "\e[d" then return "S-LEFT"
        end

        # 6) CSI style shift‐arrows (e.g. ESC [1;2A )
        if m = seq.match(/\A\e\[\d+;2([ABCD])\z/)
          return { 'A' => "S-UP", 'B' => "S-DOWN", 'C' => "S-RIGHT", 'D' => "S-LEFT" }[m[1]]
        end

        # 6b) CSI style ctrl-arrows (e.g. ESC [1;5A )
        if m = seq.match(/\A\e\[\d+;5([ABCD])\z/)
          return { 'A' => "C-UP", 'B' => "C-DOWN", 'C' => "C-RIGHT", 'D' => "C-LEFT" }[m[1]]
        end

        # 7) Plain arrows and Home/End (xterm style)
        if m = seq.match(/\A\e\[([ABCDHF])\z/)
          return { 'A' => "UP", 'B' => "DOWN", 'C' => "RIGHT", 'D' => "LEFT", 'H' => "HOME", 'F' => "END" }[m[1]]
        end

        # 8) CSI + '~' sequences (Ins, Del, Home, End, PgUp, PgDn, F5-F12)
        if seq.start_with?("\e[") && seq.end_with?("~")
          num = seq[/\d+(?=~)/].to_i
          return case num
                when 1, 7   then "HOME"
                when 2       then "INS"
                when 3       then "DEL"
                when 4, 8    then "END"
                when 5       then "PgUP"
                when 6       then "PgDOWN"
                when 15      then "F5"
                when 17      then "F6"
                when 18      then "F7"
                when 19      then "F8"
                when 20      then "F9"
                when 21      then "F10"
                when 23      then "F11"
                when 24      then "F12"
                else ""
                end
        end

        # 9) SS3 function keys F1-F4, Ctrl+arrows, and Home/End (application mode)
        if seq.start_with?("\eO") && seq.length == 3
          return case seq[2]
                when 'P' then "F1"
                when 'Q' then "F2"
                when 'R' then "F3"
                when 'S' then "F4"
                when 'H' then "HOME"      # SS3 Home (kitty/wezterm application mode)
                when 'F' then "END"       # SS3 End (kitty/wezterm application mode)
                when 'a' then "C-UP"      # Some terminals send ESC O a for Ctrl+Up
                when 'b' then "C-DOWN"    # Some terminals send ESC O b for Ctrl+Down
                when 'c' then "C-RIGHT"   # Some terminals send ESC O c for Ctrl+Right
                when 'd' then "C-LEFT"    # Some terminals send ESC O d for Ctrl+Left
                else ""
                end
        end

        # 10) Single / Ctrl-char mappings
        return case seq
              when "\r", "\n"     then "ENTER"
              when "\t"           then "TAB"
              when "\u007F", "\b" then "BACK"
              when "\u0000"       then "C-SPACE"
              when "\u0001"       then "C-A"
              when "\u0002"       then "C-B"
              when "\u0003"       then "C-C"
              when "\u0004"       then "C-D"
              when "\u0005"       then "C-E"
              when "\u0006"       then "C-F"
              when "\u0007"       then "C-G"
              when "\u0008"       then "C-H"
              when "\u000B"       then "C-K"
              when "\u000C"       then "C-L"
              when "\u000D"       then "C-M"
              when "\u000E"       then "C-N"
              when "\u000F"       then "C-O"
              when "\u0010"       then "C-P"
              when "\u0011"       then "C-Q"
              when "\u0012"       then "C-R"
              when "\u0013"       then "C-S"
              when "\u0014"       then "C-T"
              when "\u0015"       then "C-U"
              when "\u0016"       then "C-V"
              when "\u0018"       then "C-X"
              when "\u0019"       then "C-Y"
              when "\u001A"       then "C-Z"
              when "\u0017"       then "WBACK"
              when /\A[[:print:]]\z/ then seq
              else ""
              end
      ensure
        if flush
          while IO.select([$stdin], nil, nil, 0)
            begin
              $stdin.read_nonblock(4096)
            rescue IO::WaitReadable, EOFError
              break
            end
          end
        end
      end
    end
  end
end
