#!/bin/bash
# Full end-to-end verification for stompserver Debian package.
set -euo pipefail

LOGDIR=/tmp/stompserver-e2e-logs
mkdir -p "$LOGDIR"
exec > >(tee -a "$LOGDIR/full-run.log") 2>&1

section() {
  echo
  echo "================================================================"
  echo "== $1"
  echo "================================================================"
}

fail() {
  echo "FAIL: $1" | tee -a "$LOGDIR/failures.log"
  exit 1
}

pass() {
  echo "PASS: $1"
}

DEB=/root/stompserver.deb
test -f "$DEB" || DEB=/tmp/stompserver.deb
test -f "$DEB" || fail "package deb not found at /root/stompserver.deb or /tmp/stompserver.deb"

section "Environment"
date -Is
cat /etc/debian_version
uname -a
pass "environment captured"

section "Package install"
DEBIAN_FRONTEND=noninteractive dpkg -i "$DEB" 2>&1 | tee "$LOGDIR/dpkg-install.txt"
DEBIAN_FRONTEND=noninteractive apt-get install -y -f -qq 2>&1 | tee -a "$LOGDIR/dpkg-install.txt"
cat > /etc/stompserver/stompserver.conf <<'EOF'
---
:daemon: true
:working_dir: /var/lib/stompserver
:logdir: /var/log/stompserver
:pidfile: /var/run/stompserver/pid
:storage: /var/lib/stompserver/storage
:etcdir: /etc/stompserver
:queue: memory
:auth: false
:debug: false
:group: stompserver
:user: stompserver
:host: 127.0.0.1
:port: 61613
EOF
systemctl reset-failed stompserver.service 2>/dev/null || true
systemctl restart stompserver.service
sleep 2
dpkg -s stompserver | tee "$LOGDIR/dpkg-status.txt"
pass "package installed"

section "Package file inventory"
dpkg -L stompserver | tee "$LOGDIR/dpkg-list.txt"
test ! -e /etc/init.d/stompserver && pass "no SysV init script"
test -f /usr/lib/systemd/system/stompserver.service && pass "systemd unit installed"
test -f /usr/lib/tmpfiles.d/stompserver.conf && pass "tmpfiles snippet installed"
test -f /etc/stompserver/stompserver.conf && pass "default config installed"
test -f /etc/logrotate.d/stompserver && pass "logrotate config installed"
grep -q deb-systemd-invoke /etc/logrotate.d/stompserver && pass "logrotate uses deb-systemd-invoke"

section "Ownership and permissions"
stat -c '%n %U:%G %a' /var/lib/stompserver /var/log/stompserver /etc/stompserver /run/stompserver | tee "$LOGDIR/permissions.txt"
id stompserver | tee "$LOGDIR/stompserver-user.txt"

section "systemd unit file"
cat /usr/lib/systemd/system/stompserver.service | tee "$LOGDIR/stompserver.service"

section "Service state after install"
systemctl status stompserver.service --no-pager | tee "$LOGDIR/systemctl-status-initial.txt"
systemctl is-enabled stompserver.service | tee "$LOGDIR/systemctl-enabled.txt"
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active.txt"
ss -tlnp | grep 61613 | tee "$LOGDIR/ss-listen.txt"
test -f /run/stompserver/pid && pass "pidfile present"
cat /run/stompserver/pid | tee "$LOGDIR/pid.txt"

section "Journal logs since boot"
journalctl -u stompserver.service --no-pager -b | tee "$LOGDIR/journal-initial.txt"

section "Application log file"
ls -la /var/log/stompserver/ | tee "$LOGDIR/logdir-listing.txt"
if [ -f /var/log/stompserver/stompserver.log ]; then
  tail -50 /var/log/stompserver/stompserver.log | tee "$LOGDIR/app-log-initial.txt"
fi

section "Upstream Ruby unit tests (from source tree)"
export RUBYLIB=/usr/lib/ruby/vendor_ruby
ruby -ryaml -e '
  $LOAD_PATH.unshift("/tmp/stompserver-tests")
  Dir.chdir("/tmp/stompserver-tests") do
    YAML.load_file("ruby-test-files.yaml").each { |f| require f }
  end
' 2>&1 | tee "$LOGDIR/ruby-unit-tests.txt"
grep -q '0 failures, 0 errors' "$LOGDIR/ruby-unit-tests.txt" && pass "upstream unit tests passed"

section "STOMP protocol functional tests"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
install -m 0644 "$SCRIPT_DIR/stomp_client_lib.rb" /tmp/stomp_client_lib.rb

cat > /tmp/stomp_e2e.rb <<'RUBY'
require '/tmp/stomp_client_lib.rb'

def assert_eq(label, expected, actual)
  raise "#{label}: expected #{expected.inspect}, got #{actual.inspect}" unless expected == actual
  puts "  ok: #{label}"
end

def assert_match(label, pattern, actual)
  raise "#{label}: #{actual.inspect} does not match #{pattern.inspect}" unless actual =~ pattern
  puts "  ok: #{label}"
end

host = '127.0.0.1'
port = 61613

# 1) CONNECT / CONNECTED
c = StompClient.new(host, port)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
cmd, headers, body = c.read_frame
assert_eq('connect response command', 'CONNECTED', cmd)
assert_match('connect response session', /.+/, headers['session'])
c.send_frame('DISCONNECT')
c.close

# 2) Queue publish before subscribe (queued delivery)
c = StompClient.new(host, port)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
c.read_frame
c.send_frame('SEND', {'destination' => '/queue/e2e-queued'}, 'queued-first')
c.send_frame('SUBSCRIBE', {'destination' => '/queue/e2e-queued', 'id' => 'sub-1', 'ack' => 'auto'})
cmd, headers, body = c.read_frame
assert_eq('queued message command', 'MESSAGE', cmd)
assert_eq('queued message body', 'queued-first', body)
assert_eq('queued message destination', '/queue/e2e-queued', headers['destination'])
c.send_frame('DISCONNECT')
c.close

# 3) FIFO ordering
c = StompClient.new(host, port)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
c.read_frame
c.send_frame('SUBSCRIBE', {'destination' => '/queue/e2e-fifo', 'id' => 'sub-fifo', 'ack' => 'auto'})
%w[one two three].each do |msg|
  c.send_frame('SEND', {'destination' => '/queue/e2e-fifo'}, msg)
end
%w[one two three].each do |expected|
  _cmd, _headers, body = c.read_frame
  assert_eq('fifo body', expected, body)
end
c.send_frame('DISCONNECT')
c.close

# 4) Topic delivery to active subscriber
pub = StompClient.new(host, port)
sub = StompClient.new(host, port)
pub.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
sub.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
pub.read_frame
sub.read_frame
sub.send_frame('SUBSCRIBE', {'destination' => '/topic/e2e-news', 'id' => 'topic-sub-1', 'ack' => 'auto'})
sleep 0.2
pub.send_frame('SEND', {'destination' => '/topic/e2e-news'}, 'topic-ping')
cmd, headers, body = sub.read_frame
assert_eq('topic message command', 'MESSAGE', cmd)
assert_eq('topic message body', 'topic-ping', body)
pub.send_frame('DISCONNECT')
sub.send_frame('DISCONNECT')
pub.close
sub.close

# 5) Monitor queue stats (seed a queue so memory stats are non-empty)
seed = StompClient.new(host, port)
seed.send_frame('CONNECT', {'accept-version' => '1.0', 'host' => 'localhost'})
seed.read_frame
seed.send_frame('SEND', {'destination' => '/queue/e2e-monitor'}, 'stat-seed')
seed.send_frame('DISCONNECT')
seed.close

mon = StompClient.new(host, port)
mon.send_frame('CONNECT', {'accept-version' => '1.0', 'host' => 'localhost'})
mon.read_frame
mon.send_frame('SUBSCRIBE', {'destination' => '/queue/monitor', 'id' => 'mon-1', 'ack' => 'auto'})
_cmd, _headers, body = mon.read_frame(timeout: 12)
assert_match('monitor stats mention e2e-monitor', /e2e-monitor/, body)
assert_match('monitor stats mention enqueued', /enqueued/, body)
mon.send_frame('DISCONNECT')
mon.close

# 6) Invalid auth rejected when enabled (tested later after reconfigure)

puts 'STOMP functional tests completed successfully'
RUBY

ruby /tmp/stomp_e2e.rb 2>&1 | tee "$LOGDIR/stomp-functional.txt"
pass "STOMP functional tests"

section "Service restart preserves memory queue only within process"
systemctl restart stompserver.service
sleep 2
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active-after-restart.txt"
journalctl -u stompserver.service --no-pager -n 20 | tee "$LOGDIR/journal-after-restart.txt"
pass "service restart"

section "Switch to file queue backend and verify persistence across restart"
cp -a /etc/stompserver/stompserver.conf "$LOGDIR/stompserver.conf.memory.bak"
cat > /etc/stompserver/stompserver.conf <<'EOF'
---
:daemon: true
:working_dir: /var/lib/stompserver
:logdir: /var/log/stompserver
:pidfile: /var/run/stompserver/pid
:storage: /var/lib/stompserver/storage-file
:etcdir: /etc/stompserver
:queue: file
:auth: false
:debug: false
:group: stompserver
:user: stompserver
:host: 127.0.0.1
:port: 61613
EOF
systemctl restart stompserver.service
sleep 2
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active-file-queue.txt"
journalctl -u stompserver.service --no-pager -n 30 | tee "$LOGDIR/journal-file-queue.txt"

cat > /tmp/stomp_file_persist.rb <<'RUBY'
require '/tmp/stomp_client_lib.rb'
c = StompClient.new('127.0.0.1', 61613)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
c.read_frame
c.send_frame('SEND', {'destination' => '/queue/e2e-persist'}, 'persist-me-pls')
c.send_frame('DISCONNECT')
c.close
RUBY
ruby /tmp/stomp_file_persist.rb 2>&1 | tee "$LOGDIR/stomp-file-queue-send.txt"

systemctl restart stompserver.service
sleep 2
journalctl -u stompserver.service --no-pager -n 20 | tee "$LOGDIR/journal-after-file-restart.txt"

cat > /tmp/stomp_file_receive.rb <<'RUBY'
require '/tmp/stomp_client_lib.rb'
c = StompClient.new('127.0.0.1', 61613)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost')
c.read_frame
c.send_frame('SUBSCRIBE', {'destination' => '/queue/e2e-persist', 'id' => 'sub-persist', 'ack' => 'auto'})
cmd, headers, body = c.read_frame
raise "expected persisted message, got #{body.inspect}" unless body == 'persist-me-pls'
puts "  ok: file queue persisted message across restart"
c.send_frame('DISCONNECT')
c.close
RUBY
ruby /tmp/stomp_file_receive.rb 2>&1 | tee "$LOGDIR/stomp-file-queue-receive.txt"
grep -q 'file queue persisted message across restart' "$LOGDIR/stomp-file-queue-receive.txt" && pass "file queue persistence"
ls -la /var/lib/stompserver/storage-file | tee "$LOGDIR/file-queue-storage.txt"

section "DBM queue backend smoke test"
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ruby-dbm 2>&1 | tee "$LOGDIR/dbm-deps-install.txt"
systemctl reset-failed stompserver.service 2>/dev/null || true
cat > /etc/stompserver/stompserver.conf <<'EOF'
---
:daemon: true
:working_dir: /var/lib/stompserver
:logdir: /var/log/stompserver
:pidfile: /var/run/stompserver/pid
:storage: /var/lib/stompserver/storage-dbm
:etcdir: /etc/stompserver
:queue: dbm
:auth: false
:debug: false
:group: stompserver
:user: stompserver
:host: 127.0.0.1
:port: 61613
EOF
systemctl restart stompserver.service
sleep 2
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active-dbm.txt"
journalctl -u stompserver.service --no-pager -n 20 | tee "$LOGDIR/journal-dbm.txt"
ruby /tmp/stomp_e2e.rb 2>&1 | tee "$LOGDIR/stomp-dbm-functional.txt"
pass "dbm queue backend"

section "Authentication enforcement"
cp /usr/share/doc/stompserver/examples/passwd.example /etc/stompserver/.passwd
chown stompserver:stompserver /etc/stompserver/.passwd
chmod 0640 /etc/stompserver/.passwd
cat /etc/stompserver/.passwd | tee "$LOGDIR/passwd-file.txt"
cat > /etc/stompserver/stompserver.conf <<'EOF'
---
:daemon: true
:working_dir: /var/lib/stompserver
:logdir: /var/log/stompserver
:pidfile: /var/run/stompserver/pid
:storage: /var/lib/stompserver/storage
:etcdir: /etc/stompserver
:queue: memory
:auth: true
:debug: false
:group: stompserver
:user: stompserver
:host: 127.0.0.1
:port: 61613
EOF
systemctl restart stompserver.service
sleep 2
journalctl -u stompserver.service --no-pager -n 20 | tee "$LOGDIR/journal-auth-enabled.txt"

cat > /tmp/stomp_auth.rb <<'RUBY'
require '/tmp/stomp_client_lib.rb'

# bad credentials should fail
c = StompClient.new('127.0.0.1', 61613)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost', 'login' => 'bad', 'passcode' => 'bad')
cmd, headers, body = c.read_frame
raise 'expected ERROR for bad auth' unless cmd == 'ERROR'
puts '  ok: invalid credentials rejected'
c.close

# good credentials from passwd.example: testuser / testpass
c = StompClient.new('127.0.0.1', 61613)
c.send_frame('CONNECT', 'accept-version' => '1.0', 'host' => 'localhost', 'login' => 'testuser', 'passcode' => 'testpass')
cmd, headers, body = c.read_frame
raise "expected CONNECTED, got #{cmd}" unless cmd == 'CONNECTED'
c.send_frame('SEND', {'destination' => '/queue/e2e-auth'}, 'safe')
c.send_frame('DISCONNECT')
c.close
puts '  ok: valid credentials accepted'
RUBY
ruby /tmp/stomp_auth.rb 2>&1 | tee "$LOGDIR/stomp-auth.txt"
grep -q 'invalid credentials rejected' "$LOGDIR/stomp-auth.txt" && pass "auth rejects bad credentials"
grep -q 'valid credentials accepted' "$LOGDIR/stomp-auth.txt" && pass "auth accepts valid credentials"

section "logrotate dry-run"
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq logrotate 2>&1 | tee "$LOGDIR/logrotate-deps-install.txt"
logrotate -d /etc/logrotate.d/stompserver 2>&1 | tee "$LOGDIR/logrotate-dry-run.txt"
grep -q 'deb-systemd-invoke try-reload-or-restart stompserver.service' "$LOGDIR/logrotate-dry-run.txt" && pass "logrotate references systemd invoke"

section "Maintainer script remove and reinstall"
systemctl stop stompserver.service
DEBIAN_FRONTEND=noninteractive dpkg -r stompserver 2>&1 | tee "$LOGDIR/dpkg-remove.txt"
test ! -e /usr/lib/systemd/system/stompserver.service && pass "unit removed on package removal"
journalctl -u stompserver.service --no-pager -n 10 2>&1 | tee "$LOGDIR/journal-after-remove.txt" || true
DEBIAN_FRONTEND=noninteractive dpkg -i "$DEB" 2>&1 | tee "$LOGDIR/dpkg-reinstall.txt"
sleep 2
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active-reinstall.txt"
journalctl -u stompserver.service --no-pager -n 20 | tee "$LOGDIR/journal-after-reinstall.txt"
pass "remove and reinstall"

section "Lintian (inside Sid container)"
apt-get install -y -qq lintian >/dev/null
lintian -EvIiL +pedantic --suppress-tags bad-distribution-in-changes-file,changelog-distribution-does-not-match-changes-file,malformed-deb-archive "$DEB" 2>&1 | tee "$LOGDIR/lintian.txt"
test ! -s "$LOGDIR/lintian.txt" && pass "lintian clean"

section "Restore default memory-queue config"
cp "$LOGDIR/stompserver.conf.memory.bak" /etc/stompserver/stompserver.conf
systemctl restart stompserver.service
sleep 2
systemctl is-active stompserver.service | tee "$LOGDIR/systemctl-active-final.txt"

section "SUMMARY"
echo "All end-to-end checks completed."
echo "Logs stored in: $LOGDIR"
ls -la "$LOGDIR" | tee "$LOGDIR/INDEX.txt"