#!/bin/bash
set -e
set -o pipefail
set -u
#
# This test checks whether the environment variables for the bpipe plugin
# work correctly. Both the read- and the writeprogram use the set
# environment variables.
#
# It also covers the bpipe plugin's corrected-attributes resend (the
# fallback byte/block count reported when the plugin doesn't set an
# exact size), including that the corrected attributes reach the
# Director before the digest, that "status subscriptions accounting"
# reports the corrected allocated and logical sizes, and that a Virtual
# Full consolidation retains both values unchanged.
#

TestName="$(basename "$(pwd)")"
export TestName

#shellcheck source=../environment.in
. ./environment

#shellcheck source=../scripts/functions
. "${BAREOS_SCRIPTS_DIR}"/functions

start_test

refresh_and_wait_for_subscription_accounting()
{
  local report="$1" expected_chain_size="$2"

  cat <<END_OF_DATA >"$tmp/bconcmds"
@$out "${runner_tmp}/accounting-refresh-request.out"
refresh subscriptions accounting
END_OF_DATA
  run_bconsole

  for _ in {1..60}; do
    cat <<END_OF_DATA >"$tmp/bconcmds"
@$out "$report"
status subscriptions accounting client=bareos-fd fileset=SelfTest
END_OF_DATA
    run_bconsole
    if grep -qF "Background refresh worker: idle." "$report" \
      && grep -qF "Real (File.LStat-based) subscription accounting report" \
        "$report" \
      && grep -qE "^SelfTest@bareos-fd[[:space:]]+.*[[:space:]]${expected_chain_size}$" "$report"; then
      return 0
    fi
    sleep 1
  done

  echo "timed out waiting for subscription accounting refresh" >&2
  cat "${runner_tmp}/accounting-refresh-request.out" >&2
  cat "$report" >&2
  return 1
}

# Windows debug: show environment vars
if on_windows_host; then
  nssm get bareos_fd-bpipe-fd AppEnvironmentExtra
fi

full_log="${runner_tmp}/full.out"
incr_log="${runner_tmp}/incr.out"
restore_log="${runner_tmp}/restore.out"

desired_since_time=$(date "+%F %R:%S")
#yyyy-mm-dd hh:mm:ss

if [ "$(uname -s)" == FreeBSD ]; then
  desired_timestamp_of_since_time=$(date -j -f "%Y-%m-%d%n %H:%M:%S" "$desired_since_time" "+%s")
else
  desired_timestamp_of_since_time=$(date +%s --date="$desired_since_time")
fi
cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages


@$out $full_log
run job=backup-bareos-fd level=Full yes
wait
messages

@$out $incr_log
wait
run job=backup-bareos-fd level=Incremental since="$desired_since_time" yes
wait
messages

@$out $restore_log
restore jobid=1 client=bareos-fd all done yes
wait
messages

quit
END_OF_DATA

run_bconsole

check_log "$full_log"
check_log "$incr_log"
check_log "$restore_log"

payload="bareos bpipe accounting payload"
payload_bytes=${#payload}
payload_blocks=$(((payload_bytes + 511) / 512))

# The bpipe plugin did not report a corrected size/block count itself,
# so the FD's fallback counts it from the plugin stream instead. That
# per-file correction is only logged at debug level (not the job log,
# to avoid flooding it for plugins backing up many files needing
# correction) -- the catalog check below (via .sql query) is the
# behavioral proof that the fallback was applied correctly.

# The corrected attributes must reach the Director before the digest,
# otherwise the digest is dropped with a "not same File" warning.
expect_not_grep \
  "not same File" \
  "$full_log" \
  "bpipe digest was dropped: corrected attributes reached the Director after the digest"

printf '%s' "$payload" >"${runner_tmp}/expected-restored-data"
cmp "${runner_tmp}/expected-restored-data" "$tmp/readprogram.3. .dat"

run_bconsole - <<END_OF_DATA
@$out ${runner_tmp}/accounting.out
.sql query="SELECT count(*) || ':' || min(s.st_size) || ':' || min(s.st_blocks) AS accounting FROM file f JOIN path p USING (pathid) CROSS JOIN LATERAL decode_lstat(f.lstat, ARRAY['st_size','st_blocks']) s WHERE f.jobid=1 AND p.path='/' AND f.name='bpipe'"
END_OF_DATA

expect_grep \
  "1:${payload_bytes}:${payload_blocks}" \
  "${runner_tmp}/accounting.out" \
  "Catalog does not contain exactly one corrected bpipe File row"
refresh_and_wait_for_subscription_accounting \
  "${runner_tmp}/accounting-before-virtual-full.out" 2

# validate environment variables in readprogram / writeprogram
expected_output=(
  "BareosClientName" "bpipe-fd-fd" "bpipe-fd-fd" "bpipe-fd-fd"
  "BareosJobId" "1" "2" "3"
  "BareosJobLevel" "F" "I" " "
  "BareosSinceTime" "0" "$desired_timestamp_of_since_time" "0"
  "BareosJobType" "B" "B" "R"
  "BareosWhere" "" "" "$RestoreDirectory"
  "BareosReplace" " " " " "a"
  "my_env_variable" "still there" "still there" "still there"
)

for ((i = 0; i < ${#expected_output[@]}; i += 4)); do
  key="${expected_output[i + 0]}"
  readprogram_F_value="${expected_output[i + 1]}"
  readprogram_I_value="${expected_output[i + 2]}"
  writeprogram_value="${expected_output[i + 3]}"

  if [ -n "${readprogram_F_value}" ]; then
    if ! grep -q "${key}='${readprogram_F_value}'" "$tmp/readprogram.F.env"; then
      echo "Environment variable '$key' is wrong in readprogram, expected '${readprogram_F_value}'."
      cat "$tmp/readprogram.F.env"
      exit 2
    fi
  fi
  if [ -n "${readprogram_I_value}" ]; then
    if ! grep -q "${key}='${readprogram_I_value}'" "$tmp/readprogram.I.env"; then
      echo "Environment variable '$key' is wrong in readprogram, expected '${readprogram_I_value}'."
      cat "$tmp/readprogram.I.env"
      exit 2
    fi
  fi
  if [ -n "${writeprogram_value}" ]; then
    if ! grep -q "${key}='${writeprogram_value}'" "$tmp/writeprogram.env"; then
      echo "Environment variable '$key' is wrong in writeprogram, expected '${writeprogram_value}'."
      cat "$tmp/writeprogram.env"
      exit 2
    fi
  fi
done

if on_windows_host; then
  accounted_bytes=$payload_bytes
  accounting_rule=S
else
  accounted_bytes=$((payload_blocks * 512))
  accounting_rule=B
fi

expect_grep \
  "^SelfTest@bareos-fd[[:space:]]*1[[:space:]]*${accounted_bytes} B[[:space:]]*${payload_bytes} B[[:space:]]*${accounting_rule}[[:space:]]*2$" \
  "${runner_tmp}/accounting-before-virtual-full.out" \
  "Subscription accounting did not count the original bpipe backup correctly"

virtual_full_log="${runner_tmp}/virtual-full.out"
virtual_full_restore_log="${runner_tmp}/virtual-full-restore.out"

run_bconsole - <<END_OF_DATA
@$out ${virtual_full_log}
label volume=VirtualFull-0001 storage=File2 pool=VirtualFull
run job=backup-bareos-fd level=VirtualFull jobid=1,2 yes
wait
messages
@$out ${runner_tmp}/virtual-full-accounting.out
.sql query="SELECT count(*) || ':' || min(s.st_size) || ':' || min(s.st_blocks) AS accounting FROM file f JOIN path p USING (pathid) CROSS JOIN LATERAL decode_lstat(f.lstat, ARRAY['st_size','st_blocks']) s WHERE f.jobid=4 AND p.path='/' AND f.name='bpipe'"
@$out ${virtual_full_restore_log}
purge volume=Full-0001 yes
restore jobid=4 client=bareos-fd all done yes
wait
messages
END_OF_DATA

check_log "$virtual_full_log"
check_log "$virtual_full_restore_log"
refresh_and_wait_for_subscription_accounting \
  "${runner_tmp}/virtual-full-accounting.out" 1

expect_grep \
  "SD Files Written:[[:space:]]*1" \
  "$virtual_full_log" \
  "Virtual Full did not consolidate exactly one bpipe file"

expect_grep \
  "1:${payload_bytes}:${payload_blocks}" \
  "${runner_tmp}/virtual-full-accounting.out" \
  "Virtual Full catalog row does not retain corrected bpipe attributes"

expect_not_grep \
  "not same File" \
  "$virtual_full_log" \
  "Virtual Full dropped the bpipe digest: corrected attributes reached the Director after the digest"

expect_grep \
  "^SelfTest@bareos-fd[[:space:]]*1[[:space:]]*${accounted_bytes} B[[:space:]]*${payload_bytes} B[[:space:]]*${accounting_rule}[[:space:]]*1$" \
  "${runner_tmp}/virtual-full-accounting.out" \
  "Subscription accounting changed after bpipe consolidation"

cmp "${runner_tmp}/expected-restored-data" "$tmp/readprogram.5. .dat"

end_test
