class Tryouts::CLI::TTYStatusDisplay

Encapsulates TTY manipulation for live status display with fallback

def available?

def available?
  @available
end

def check_tty_availability

def check_tty_availability
  # Check if we have a real TTY
  return false unless @io.respond_to?(:tty?) && @io.tty?
  # Check if we can get screen dimensions
  return false unless TTY::Screen.width > 0 && TTY::Screen.height > STATUS_LINES + 5
  # Check if TERM environment variable suggests terminal capabilities
  term = ENV.fetch('TERM', nil)
  return false if term.nil? || term == 'dumb'
  # Check if we're likely in a CI environment (common CI env vars)
  ci_vars = %w[CI CONTINUOUS_INTEGRATION BUILD_NUMBER JENKINS_URL GITHUB_ACTIONS]
  return false if ci_vars.any? { |var| ENV[var] }
  true
rescue StandardError => ex
  # If any TTY detection fails, assume not available
  if @show_debug
    @io.puts "TTY detection failed: #{ex.message}"
  end
  false
end

def cleanup!

Explicit cleanup method for manual invocation
def cleanup!
  return unless @available
  with_terminal_safety do
    if @status_active
      # Clear the status area completely
      @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES + 1)
      STATUS_LINES.times do |i|
        @io.print @cursor.clear_line
        @io.print @cursor.down(1) if i < STATUS_LINES - 1
      end
    end
    # Reset cursor to a safe position (start of line below cleared area)
    @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES)
    @io.print "\n"
    @io.flush
  end
  @status_active = false
end

def clear_status_area

def clear_status_area
  return unless @available && @status_active
  with_terminal_safety do
    # Move to status area and clear it completely - start from first status line
    @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES + 1)
    # Clear each line thoroughly
    STATUS_LINES.times do |i|
      @io.print @cursor.clear_line
      @io.print @cursor.down(1) if i < STATUS_LINES - 1  # Don't go down after last line
    end
    # Move cursor to a clean area for final output - position it well above the cleared area
    # This ensures no interference with the cleared status content
    target_row = TTY::Screen.height - STATUS_LINES - 2  # Leave some buffer space
    @io.print @cursor.move_to(0, target_row)
    @io.print "\n"  # Add a clean line for final output to start
    @io.flush
  end
  @status_active = false
end

def debug_log(message)

def debug_log(message)
  return unless @show_debug
  # Use stderr to avoid interfering with TTY operations on stdout
  $stderr.puts "TTY DEBUG: #{message}"
end

def format_timing(elapsed_time)

def format_timing(elapsed_time)
  if elapsed_time < 0.001
    "#{(elapsed_time * 1_000_000).round}μs"
  elsif elapsed_time < 1
    "#{(elapsed_time * 1000).round}ms"
  else
    "#{elapsed_time.round(2)}s"
  end
end

def get_cursor_position

def get_cursor_position
  # Simple approximation - in a real terminal this would query cursor position
  # For now, return reasonable defaults
  [10, 0]
end

def initialize(io = $stdout, options = {})

def initialize(io = $stdout, options = {})
  @io         = io
  @available  = check_tty_availability
  @show_debug = options.fetch(:debug, false)
  @cleanup_registered = false
  return unless @available
  @cursor        = TTY::Cursor
  @pastel        = Pastel.new
  @status_active = false
  @original_cursor_position = nil
  register_cleanup_handlers
end

def register_cleanup_handlers

Register cleanup handlers for various termination scenarios
def register_cleanup_handlers
  return if @cleanup_registered
  # Handle common termination signals
  %w[INT TERM QUIT HUP].each do |signal|
    begin
      Signal.trap(signal) do
        cleanup!
        # Re-raise the signal with default handler
        Signal.trap(signal, 'DEFAULT')
        Process.kill(signal, Process.pid)
      end
    rescue ArgumentError
      # Signal not supported on this platform, skip it
      debug_log("Signal #{signal} not supported, skipping cleanup handler")
    end
  end
  # Register at_exit handler as final fallback
  at_exit { cleanup! }
  @cleanup_registered = true
  debug_log('TTY cleanup handlers registered')
end

def reserve_status_area

def reserve_status_area
  return unless @available && !@status_active
  with_terminal_safety do
    # Store original cursor position if possible
    @original_cursor_position = get_cursor_position
    # Simply print empty lines to push content up and make room at bottom
    STATUS_LINES.times { @io.print "\n" }
    @status_active = true
  end
end

def update_status(state)

def update_status(state)
  return unless @available && @status_active
  with_terminal_safety do
    # Move to status area (bottom of screen) and update in place
    current_row, current_col = get_cursor_position
    # Move to status area at bottom
    @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES + 1)
    # Clear and write status content
    STATUS_LINES.times do
      @io.print @cursor.clear_line
      @io.print @cursor.down(1) unless STATUS_LINES == 1
    end
    # Move back to start of status area and write content
    @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES + 1)
    write_status_content(state)
    # Move cursor back to where content should continue (just before status area)
    @io.print @cursor.move_to(0, TTY::Screen.height - STATUS_LINES)
    @io.flush
  end
end

def with_terminal_safety

Wrap terminal operations with exception handling
def with_terminal_safety
  yield
rescue StandardError => e
  debug_log("Terminal operation failed: #{e.message}")
  # Attempt basic cleanup on any terminal operation failure
  begin
    @io.print "\n" if @io.respond_to?(:print)
    @io.flush if @io.respond_to?(:flush)
  rescue StandardError
    # If even basic cleanup fails, there's nothing more we can do
  end
  @status_active = false
end

def write_scrolling(text)

def write_scrolling(text)
  return @io.print(text) unless @available
  # Always write content normally - the status will be updated separately
  @io.print text
end

def write_status_content(state)

def write_status_content(state)
  return unless @available
  # Line 1: Empty separator line
  @io.print "\n"
  # Line 2: Current progress
  if state.current_file
    current_info  = "Running: #{state.current_file}"
    current_info += " → #{state.current_test}" if state.current_test
    @io.print current_info
  else
    @io.print 'Ready'
  end
  @io.print "\n"
  # Line 3: Test counts
  parts = []
  parts << @pastel.green("#{state.passed} passed") if state.passed > 0
  parts << @pastel.red("#{state.failed} failed") if state.failed > 0
  parts << @pastel.yellow("#{state.errors} errors") if state.errors > 0
  if parts.any?
    @io.print "Tests: #{parts.join(', ')}"
  else
    @io.print 'Tests: 0 run'
  end
  @io.print "\n"
  # Line 4: File progress
  files_info  = "Files: #{state.files_completed}"
  files_info += "/#{state.total_files}" if state.total_files > 0
  files_info += ' completed'
  @io.print files_info
  @io.print "\n"
  # Line 5: Timing
  @io.print "Time: #{format_timing(state.elapsed_time)}"
end