#!/usr/bin/env bash
# timer-pick — TUI time picker
# Install: ~/.config/scripts/timer-pick  (companion: timer-run in same dir)

SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

set -uo pipefail

ESC=$'\e'; CSI="${ESC}["
cup()  { printf "${CSI}%d;%dH" "$1" "$2"; }
cls()  { printf "${CSI}2J${CSI}H"; }
hide() { printf "${CSI}?25l"; }
show() { printf "${CSI}?25h"; }

b="${CSI}1m"  ; d="${CSI}2m"  ; r="${CSI}0m"
cy="${CSI}96m"; gn="${CSI}92m"
yl="${CSI}93m"; wh="${CSI}97m"; gy="${CSI}90m"
rv="${CSI}7m"

FIELDS=(HH MM SS)
MAX=(99 59 59)
VALUES=(0 0 0)
ACTIVE=1
LABEL=""

TW=34; IW=32; CW=10
BR=2;  BC=2

hl() { printf '─%.0s' $(seq 1 "$1"); }

redraw() {
  local row=$BR col=$BC

  cup $row $col; printf "${d}╭$(hl $IW)╮${r}"

  (( row++ )); cup $row $col
  printf "${d}│${r}${b}${cy}  ⏱  Set Timer%-*s${r}${d}│${r}" $(( IW - 14 )) ""

  (( row++ )); cup $row $col
  printf "${d}├$(hl $CW)┬$(hl $CW)┬$(hl $CW)┤${r}"

  # labels
  (( row++ )); cup $row $col; printf "${d}│${r}"
  for i in 0 1 2; do
    local lpad=$(( (CW - 2) / 2 )) rpad=$(( CW - 2 - (CW-2)/2 ))
    (( i == ACTIVE )) \
      && printf "%*s${yl}${b}${FIELDS[$i]}${r}%*s" $lpad "" $rpad "" \
      || printf "%*s${gy}${FIELDS[$i]}${r}%*s"      $lpad "" $rpad ""
    (( i < 2 )) && printf "${d}│${r}"
  done; printf "${d}│${r}"

  # up arrows
  (( row++ )); cup $row $col; printf "${d}│${r}"
  for i in 0 1 2; do
    local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
    (( i == ACTIVE )) \
      && printf "%*s${yl}${b}▲${r}%*s" $lpad "" $rpad "" \
      || printf "%*s${gy}▲${r}%*s"      $lpad "" $rpad ""
    (( i < 2 )) && printf "${d}│${r}"
  done; printf "${d}│${r}"

  # values
  (( row++ )); cup $row $col; printf "${d}│${r}"
  for i in 0 1 2; do
    local vstr; printf -v vstr '%02d' "${VALUES[$i]}"
    local lpad=$(( (CW-4) / 2 )) rpad=$(( CW - 4 - (CW-4)/2 ))
    (( i == ACTIVE )) \
      && printf "%*s${rv}${wh}${b} %s ${r}%*s" $lpad "" "$vstr" $rpad "" \
      || printf "%*s${d} %s ${r}%*s"             $lpad "" "$vstr" $rpad ""
    (( i < 2 )) && printf "${d}│${r}"
  done; printf "${d}│${r}"

  # down arrows
  (( row++ )); cup $row $col; printf "${d}│${r}"
  for i in 0 1 2; do
    local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
    (( i == ACTIVE )) \
      && printf "%*s${yl}${b}▼${r}%*s" $lpad "" $rpad "" \
      || printf "%*s${gy}▼${r}%*s"      $lpad "" $rpad ""
    (( i < 2 )) && printf "${d}│${r}"
  done; printf "${d}│${r}"

  (( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"

  (( row++ )); cup $row $col
  local ld="${LABEL:-(optional)}"
  printf "${d}│${r} ${gy}label:${r} ${cy}%-*s${r}${d}│${r}" $(( IW - 8 )) "$ld"

  (( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"

  (( row++ )); cup $row $col
  printf "${d}│${r} ${gy}h/l${r} field  ${gy}j/k${r} value  ${gy}[N]j/k${r}   ${d}│${r}"
  (( row++ )); cup $row $col
  printf "${d}│${r} ${gy}i${r} label  ${gy}0/G${r} min/max  ${gn}↵${r} start  ${d}│${r}"

  (( row++ )); cup $row $col; printf "${d}╰$(hl $IW)╯${r}"

  cup $(( row + 1 )) 1
}

read_label() {
  show
  local lrow=$(( BR + 9 )) lcol=$(( BC + 9 ))
  cup $lrow $lcol; printf "${CSI}K"
  local buf="" ch
  while IFS= read -rsn1 ch; do
    case "$ch" in
      $'\n'|$'\r'|$'\e') break ;;
      $'\x7f'|$'\b')
        [[ -n $buf ]] && buf="${buf%?}"
        cup $lrow $lcol; printf "${CSI}K%s" "$buf" ;;
      *) [[ ${#buf} -lt $(( IW - 10 )) ]] && buf+="$ch" && printf "%s" "$ch" ;;
    esac
  done
  LABEL="$buf"; hide
}

cleanup() {
  # only reset tty if we actually changed it
  [[ "${TTY_SAVED:-0}" == "1" ]] && stty sane 2>/dev/null
  show
  cls
  tput cup 0 0 2>/dev/null
}

main() {
  # Wait for kitty to fully attach the pty — critical when launched via Hyprland
  # keybind where the window and pty aren't synchronised at script start.
  local tries=0
  until [[ -t 0 && -t 1 ]]; do
    sleep 0.05
    (( tries++ ))
    (( tries > 40 )) && exit 1   # give up after 2s
  done

  hide; cls
  stty -echo -icanon min 1 time 0
  TTY_SAVED=1
  trap cleanup EXIT INT TERM

  redraw

  local ch seq count_buf=""

  while true; do
    IFS= read -rsn1 ch || continue
    if [[ $ch == $'\e' ]]; then
      IFS= read -rsn2 -t 0.05 seq || seq=""
      ch="${ch}${seq}"
    fi

    if [[ $ch =~ ^[1-9]$ ]]; then count_buf+="$ch"; redraw; continue; fi
    local N=1
    [[ -n $count_buf && $count_buf =~ ^[0-9]+$ ]] && N=$count_buf

    case "$ch" in
      $'\t'|$'\e[C'|l)   ACTIVE=$(( (ACTIVE + 1) % 3 )); count_buf="" ;;
      $'\e[Z'|$'\e[D'|h) ACTIVE=$(( (ACTIVE + 2) % 3 )); count_buf="" ;;
      '^') ACTIVE=0; count_buf="" ;;
      '$') ACTIVE=2; count_buf="" ;;

      $'\e[A'|k)
        local mx=${MAX[$ACTIVE]} v=${VALUES[$ACTIVE]}
        local nv=$(( v + N )); (( nv > mx )) && nv=$mx
        VALUES[$ACTIVE]=$nv; count_buf="" ;;

      $'\e[B'|j)
        local v=${VALUES[$ACTIVE]}
        local nv=$(( v - N )); (( nv < 0 )) && nv=0
        VALUES[$ACTIVE]=$nv; count_buf="" ;;

      0)
        if [[ -n $count_buf ]]; then count_buf+="0"; redraw; continue; fi
        VALUES[$ACTIVE]=0; count_buf="" ;;
      G) VALUES[$ACTIVE]=${MAX[$ACTIVE]}; count_buf="" ;;

      i) count_buf=""; read_label ;;

      ''|$'\n')
        count_buf=""
        local total=$(( VALUES[0]*3600 + VALUES[1]*60 + VALUES[2] ))
        if (( total == 0 )); then redraw; continue; fi
        TTY_SAVED=0
        stty sane 2>/dev/null; show; cls; tput cup 0 0 2>/dev/null
        # SIG_IGN for SIGHUP is inherited through exec, eliminating the race
        # between fork() and setsid() creating its own session. Without this,
        # kitty closing the pty on exit sends SIGHUP to the foreground process
        # group (which includes the child in non-interactive bash) before setsid
        # has a chance to move it to a new session.
        ( trap '' SIGHUP; exec setsid bash "$SCRIPTS_DIR/timer-run" "$total" "$LABEL" </dev/null >/dev/null 2>&1 ) &
        exit 0 ;;

      q|Q) exit 0 ;;
      *) count_buf="" ;;
    esac

    redraw
  done
}

main
