#!/usr/bin/env bash
# Foreground Omarchy ISO install UI supervisor.
#
# This process is the only owner of the visible install UI. It launches the
# actual installer as a non-interactive child, sends child output to the support
# log, renders progress while the child runs, then renders the final installed
# time + reboot prompt itself. There is deliberately no background dashboard.
set -euo pipefail

usage() {
  echo "Usage: $0 <log-file> <state-file> -- <installer command...>" >&2
  exit 2
}

(( $# >= 4 )) || usage
LOG_FILE="$1"
STATE_FILE="$2"
shift 2
[[ ${1:-} == "--" ]] || usage
shift
(( $# > 0 )) || usage
INSTALL_CMD=("$@")

TTY_PATH="${OMARCHY_DASHBOARD_TTY:-/dev/tty}"
LOGO_PATH="${OMARCHY_PATH:-/usr/share/omarchy}/logo.txt"
CSI=$'\033['
RESET="${CSI}0m"
DIM="${CSI}2m"
HIDE_CURSOR="${CSI}?25l"
SHOW_CURSOR="${CSI}?25h"
CLEAR="${CSI}2J${CSI}H"
CLEAR_LINE="${CSI}2K"
CLEAR_TO_END="${CSI}J"
GREEN="${CSI}32m"
DARK="${CSI}90m"

child_pid=""
child_pgid=""

whimsy=(
  "making Linux feel like home"
  "installing the good defaults"
  "putting the system in order"
  "setting up a civilized desktop"
  "clearing the path to Hyprland"
  "preparing the command line sanctuary"
  "teaching pacman the house rules"
  "laying down the Tokyo Night carpet"
  "arranging the sharp tools safely"
  "keeping the boot path clean"
  "giving Limine the keys"
  "making snapshots worth having"
  "saving future you a few hours"
  "choosing boring where boring wins"
  "leaving room for your dotfiles"
  "tightening the last few screws"
)

cleanup() {
  printf '%s%s' "$RESET" "$SHOW_CURSOR" >"$TTY_PATH" 2>/dev/null || true
}

stop_child() {
  [[ -n ${child_pid:-} ]] || return 0
  if kill -0 "$child_pid" 2>/dev/null; then
    if [[ -n ${child_pgid:-} ]]; then
      kill -- -"$child_pgid" 2>/dev/null || true
    fi
    kill "$child_pid" 2>/dev/null || true
    for _ in {1..20}; do
      kill -0 "$child_pid" 2>/dev/null || break
      sleep 0.05
    done
    if kill -0 "$child_pid" 2>/dev/null; then
      [[ -n ${child_pgid:-} ]] && kill -9 -- -"$child_pgid" 2>/dev/null || true
      kill -9 "$child_pid" 2>/dev/null || true
    fi
  fi
  wait "$child_pid" 2>/dev/null || true
}

interrupt() {
  local code="$1"
  stop_child
  cleanup
  exit "$code"
}

trap cleanup EXIT
trap 'interrupt 130' INT
trap 'interrupt 143' TERM

term_size() { stty size 2>/dev/null <"$TTY_PATH" || printf '24 80\n'; }
term_cols() { local size; size="$(term_size)"; printf '%s\n' "${size#* }"; }

repeat() {
  local char="$1" count="$2" out="" i
  for ((i=0; i<count; i++)); do out+="$char"; done
  printf '%s' "$out"
}

logo_width() {
  if [[ -f $LOGO_PATH ]]; then
    awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH"
  else
    printf '81\n'
  fi
}

logo_height() {
  if [[ -f $LOGO_PATH ]]; then
    wc -l <"$LOGO_PATH"
  else
    printf '1\n'
  fi
}

LOGO_WIDTH="$(logo_width)"
LOGO_HEIGHT="$(logo_height)"
(( LOGO_WIDTH > 0 )) || LOGO_WIDTH=81
(( LOGO_HEIGHT > 0 )) || LOGO_HEIGHT=1
CONTENT_WIDTH="$LOGO_WIDTH"
DYNAMIC_ROW=$((LOGO_HEIGHT + 3))
LAST_PCT=1

left_padding() {
  local width="${1:-$CONTENT_WIDTH}" cols pad
  cols="$(term_cols)"
  pad=$(( (cols - width) / 2 ))
  (( pad < 0 )) && pad=0
  printf '%*s' "$pad" ''
}

visible_len() {
  local text="$1"
  text="$(printf '%b' "$text" | sed -E $'s/\x1b\\[[0-9;?]*[A-Za-z]//g')"
  printf '%s' "${#text}"
}

blank_line() { printf '\r%s\n' "$CLEAR_LINE"; }

line_at() {
  local width="${1:-$CONTENT_WIDTH}" indent="${2:-0}"
  shift 2
  printf '\r%s%s%*s' "$CLEAR_LINE" "$(left_padding "$width")" "$indent" ''
  printf '%b' "$*"
}

center() {
  local text="$1" width="${2:-$CONTENT_WIDTH}" len inner_pad
  len="$(visible_len "$text")"
  inner_pad=$(( (width - len) / 2 ))
  (( inner_pad < 0 )) && inner_pad=0
  printf '\r%s%s%*s%b%s\n' "$CLEAR_LINE" "$(left_padding "$width")" "$inner_pad" '' "$text" "$RESET"
}

render_logo() {
  local pad
  pad="$(left_padding "$LOGO_WIDTH")"
  if [[ -f $LOGO_PATH ]]; then
    while IFS= read -r line; do
      printf '\r%s%s%b%s%b\n' "$CLEAR_LINE" "$pad" "$GREEN" "$line" "$RESET"
    done <"$LOGO_PATH"
  else
    center "${GREEN}Omarchy${RESET}" "$CONTENT_WIDTH"
  fi
}

progress_bar() {
  local pct="$1" width="${2:-34}" filled empty
  (( pct < 0 )) && pct=0
  (( pct > 100 )) && pct=100
  filled=$(( pct * width / 100 ))
  empty=$(( width - filled ))
  printf '%s%s%s%s%s %3d%%' "$GREEN" "$(repeat █ "$filled")" "$DARK" "$(repeat ░ "$empty")" "$RESET" "$pct"
}

install_progress() {
  [[ -f $STATE_FILE ]] || { printf '1'; return; }
  python - "$STATE_FILE" <<'PY' 2>/dev/null || printf '1'
import json, sys, time
path = sys.argv[1]
try:
    state = json.load(open(path))
except Exception:
    print(1, end='')
    raise SystemExit
phase = state.get('current_phase') or 'Starting installation'
if state.get('finished_at') or phase == 'Installation complete':
    print(100, end='')
    raise SystemExit
weights = [
    ('Starting installation', 1, 2, 2),
    ('Preparing live environment', 2, 6, 5),
    ('Verifying protected mounts', 6, 8, 3),
    ('Installing Arch + Omarchy', 8, 82, 60),
    ('Installing base system', 8, 82, 60),
    ('Configuring protected boot', 82, 86, 8),
    ('Finalizing in chroot', 86, 98, 90),
    ('Configuring login', 98, 99, 3),
    ('Validating protected boot setup', 99, 100, 5),
    ('Validating boot setup', 99, 100, 5),
]
start, end, expected = 1, 99, 90
for name, s, e, seconds in weights:
    if phase == name:
        start, end, expected = s, e, seconds
        break
phase_started = state.get('phase_started_at') or state.get('started_at') or time.time()
elapsed = max(0, time.time() - float(phase_started))
span = max(0, end - start)
active = min(max(span - 1, 0), int((elapsed / max(expected, 1)) * span))
print(min(100, start + active), end='')
PY
}

current_whimsy() {
  local frame="$1" pct="$2"
  printf '%s' "${whimsy[$(( (pct / 8 + frame / 8) % ${#whimsy[@]} ))]}"
}

render_static() {
  printf '%s' "$CLEAR"
  blank_line

  if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
    if timeout 8s tte -i "$LOGO_PATH" --canvas-width 0 --anchor-text c --frame-rate 920 laseretch >"$TTY_PATH" 2>/dev/null; then
      blank_line
      return
    fi
    printf '%s' "$CLEAR"
    blank_line
  fi

  render_logo
  blank_line
}

render_dynamic() {
  local frame="$1" pct spin
  pct="$(install_progress)"
  [[ $pct =~ ^[0-9]+$ ]] || pct=$LAST_PCT
  (( pct > 100 )) && pct=100
  (( pct < LAST_PCT )) && pct=$LAST_PCT
  LAST_PCT=$pct
  spin=("◐" "◓" "◑" "◒")

  printf '%s%d;1H' "$CSI" "$DYNAMIC_ROW"
  center "Installing Omarchy" "$CONTENT_WIDTH"
  blank_line
  line_at "$CONTENT_WIDTH" $(( (CONTENT_WIDTH - 40) / 2 )) ""
  progress_bar "$pct" 34
  printf '\n'
  blank_line
  center "${GREEN}${spin[$((frame % 4))]} $(current_whimsy "$frame" "$pct") ${RESET}" "$CONTENT_WIDTH"
  blank_line
  center "${DIM}Logs are being saved for support${RESET}" "$CONTENT_WIDTH"
  printf '%s' "$CLEAR_TO_END"
}

install_duration() {
  local path line duration
  for path in /mnt/var/log/omarchy-install.log "$LOG_FILE"; do
    [[ -f $path ]] || continue
    for prefix in Total Omarchy; do
      line="$(grep "^$prefix:" "$path" 2>/dev/null | tail -n 1 || true)"
      [[ -n $line ]] || continue
      duration="${line#*:}"
      duration="${duration#${duration%%[![:space:]]*}}"
      [[ -n $duration ]] && { printf '%s' "$duration"; return 0; }
    done
  done
  return 0
}

render_finish() {
  local duration message cols
  cols="$(term_cols)"
  printf '%s%s' "$SHOW_CURSOR" "$CLEAR" >"$TTY_PATH"
  if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
    timeout 8s tte -i "$LOGO_PATH" --canvas-width 0 --anchor-text c --frame-rate 920 laseretch >"$TTY_PATH" 2>/dev/null || true
  else
    render_logo >"$TTY_PATH"
  fi
  duration="$(install_duration || true)"
  message="Finished installing"
  [[ -n $duration ]] && message="Installed in $duration"
  {
    blank_line
    center "$message" "$cols"
    blank_line
  } >"$TTY_PATH"
}

reboot_prompt() {
  local prompt_pad
  prompt_pad=$(( ( $(term_cols) - 18 ) / 2 ))
  (( prompt_pad < 0 )) && prompt_pad=0
  gum confirm \
    --padding "0 0 0 $prompt_pad" \
    --show-help=false \
    --default \
    --affirmative "Reboot Now" \
    --negative "" \
    "" <"$TTY_PATH" >"$TTY_PATH" 2>&1
}

launch_child() {
  mkdir -p "$(dirname "$LOG_FILE")"
  # Child is non-interactive. All visible UI belongs to this supervisor.
  setsid "${INSTALL_CMD[@]}" </dev/null > >(sed -u 's/\x1b\[[0-9;]*[a-zA-Z]//g' >>"$LOG_FILE") 2>&1 &
  child_pid=$!
  child_pgid=$child_pid
}

[[ -e $TTY_PATH ]] || exit 2
printf '%s' "$HIDE_CURSOR" >"$TTY_PATH"
render_static >"$TTY_PATH"
launch_child

frame=0
while jobs -pr | grep -qx "$child_pid"; do
  render_dynamic "$frame" >"$TTY_PATH"
  frame=$((frame + 1))
  sleep 0.5
done

set +e
wait "$child_pid"
status=$?
set -e
child_pid=""
child_pgid=""

if (( status != 0 )); then
  printf '%s%s' "$SHOW_CURSOR" "$CLEAR" >"$TTY_PATH"
  exit "$status"
fi

render_finish
if [[ ${OMARCHY_UI_AUTO_REBOOT:-} == "no" ]]; then
  exit 0
fi

if reboot_prompt; then
  printf '%s' "$CLEAR" >"$TTY_PATH"
  reboot 2>/dev/null || systemctl reboot 2>/dev/null || true
fi

exit 0
