#!/usr/bin/env ruby
# frozen_string_literal: true

# SPDX-License-Identifier: GPL-3.0-only
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
#
# varunad-reaper — Auto-close byobu windows for completed worker sessions.
#
# Polls supervisor session manifests on a fixed interval. When a worker
# writes STATUS-COMPLETE.md to its scratch directory, starts a 60-second
# grace period so /retrospective + /archive can finish, validates the
# closure-gate content, then calls `byobu kill-window` to reap the window.
#
# Legacy: KILL-ME.md triggers immediate reap without grace or validation.
#
# ## Environment variables
#
# | Var                           | Default               | Purpose                                      |
# | ----------------------------- | --------------------- | -------------------------------------------- |
# | `VARUNA_REAPER_INTERVAL`      | `15`                  | seconds between poll cycles                  |
# | `VARUNA_REAPER_GRACE_SECONDS` | `60`                  | grace period after STATUS-COMPLETE detection |
# | `VARUNA_REAPER_SCRATCH_ROOT`  | `/mnt/claude-scratch` | root for manifest discovery                  |
# | `VARUNA_REAPER_ONCE`          | unset                 | when truthy, run one tick and exit 0         |
#
# ## Exit codes
#
# * `0` clean SIGINT / SIGTERM shutdown or `VARUNA_REAPER_ONCE` mode

require 'json'
require_relative '../lib/varuna'

DEFAULT_SCRATCH_ROOT = '/mnt/claude-scratch'

def load_sessions(scratch_root)
  Dir.glob(File.join(scratch_root, '*/supervisor/sessions/*.json')).filter_map do |path|
    data    = JSON.parse(File.read(path), symbolize_names: true)
    scratch = data[:scratch_dir]
    window  = data[:name]
    next unless scratch && window

    { scratch_dir: scratch, window_name: window }
  rescue StandardError => e
    warn "[varunad-reaper] manifest read error #{path}: #{e.message}"
    nil
  end
end

@running = true
['INT', 'TERM'].each { |sig| Signal.trap(sig) { @running = false } }

interval      = Integer(ENV.fetch('VARUNA_REAPER_INTERVAL', '15'))
grace_seconds = Integer(ENV.fetch('VARUNA_REAPER_GRACE_SECONDS', '60'))
scratch_root  = ENV.fetch('VARUNA_REAPER_SCRATCH_ROOT', DEFAULT_SCRATCH_ROOT)
run_once      = !ENV['VARUNA_REAPER_ONCE'].to_s.strip.empty?

events = Varuna::Events::EventLog.new
reaper = Varuna::Lifecycle::WindowReaper.new(grace_seconds: grace_seconds, events: events)

warn "[varunad-reaper] started — interval=#{interval}s grace=#{grace_seconds}s " \
     "scratch_root=#{scratch_root}"

loop do
  sessions = load_sessions(scratch_root)
  results  = reaper.tick(sessions: sessions)
  quiet_actions = [:skipped, :grace_pending].freeze
  results.each do |r|
    next if quiet_actions.include?(r[:action])

    warn "[varunad-reaper] #{r[:window_name]}: #{r[:action]} — #{r[:reason]}"
  end

  break if run_once
  break unless @running

  sleep interval
end

warn '[varunad-reaper] shutting down cleanly'
