#!/bin/bash

#   BAREOS® - Backup Archiving REcovery Open Sourced
#
#   Copyright (C) 2025-2026 Bareos GmbH & Co. KG
#
#   This program is Free Software; you can redistribute it and/or
#   modify it under the terms of version three of the GNU Affero General Public
#   License as published by the Free Software Foundation and included
#   in the file LICENSE.
#
#   This program is distributed in the hope that it will be useful, but
#   WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#   Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
#   02110-1301, USA.

set -e
set -o pipefail
set -u

#
# Create a full and and an incremental, and consolidate them
#  via a virtualfull.  Afterwards check that the virtualfull
#  worked via a restore.
#
# Also confirms "status subscriptions accounting" reports the same
# file count/byte total before and after the Virtual Full consolidation
# (the consolidated File rows must still de-duplicate to the same
# accounted totals).
#

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 "${test_home}/accounting-refresh-request.out"
refresh subscriptions accounting
END_OF_DATA
  run_bconsole "${tmp}/bconcmds"

  for _ in {1..60}; do
    cat <<END_OF_DATA >"${tmp}/bconcmds"
@$out ${report}
status subscriptions accounting client=bareos-fd fileset=SelfTest
@$out ${NULL_DEV}
.api 2
@$out $report.json
status subscriptions accounting client=bareos-fd fileset=SelfTest
END_OF_DATA
    run_bconsole "${tmp}/bconcmds"
    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 "${test_home}/accounting-refresh-request.out" >&2
  cat "${report}" >&2
  return 1
}

cat <<END_OF_DATA >"${tmp}/bconcmds"
@$out ${NULL_DEV}
messages
@$out ${test_home}/label.out
setdebug level=100 storage=File
label volume=TestVolume001 storage=File pool=Full
label volume=TestVolume002 storage=File pool=Incremental
label volume=TestVolume003 storage=File pool=VirtualFull
@$out ${test_home}/full.out
run job=$JobName level=Full yes
wait
messages
END_OF_DATA
run_bconsole "${tmp}/bconcmds"

check_log "${test_home}/full.out"

touch "${tmp}"/data/*.c
cat <<END_OF_DATA >"${tmp}/bconcmds"
@$out ${test_home}/incr.out
list jobs
run job=$JobName level=Incremental yes
wait
messages
END_OF_DATA
run_bconsole "${tmp}/bconcmds"

check_log "${test_home}/incr.out"
refresh_and_wait_for_subscription_accounting \
  "${test_home}/accounting-before-virtual-full.out" 2

cat <<END_OF_DATA >"${tmp}/bconcmds"
@$out ${test_home}/virtual.out
list jobs
run job=$JobName level=VirtualFull yes
wait
messages
@$out ${test_home}/status.out
list jobs
status director
status client
status storage=File
END_OF_DATA
run_bconsole "${tmp}/bconcmds"

check_log "${test_home}/virtual.out"
refresh_and_wait_for_subscription_accounting \
  "${test_home}/accounting-after-virtual-full.out" 1

cat <<END_OF_DATA >"${tmp}/bconcmds"
@# make sure we really restore the virtualfull
@$out ${test_home}/restore.out
purge volume=TestVolume001 yes
purge volume=TestVolume002 yes

restore client=bareos-fd fileset=SelfTest where=${test_home}/bareos-restores select all done
yes
wait
messages
END_OF_DATA
run_bconsole "${tmp}/bconcmds"

check_log "${test_home}/restore.out"

check_for_zombie_jobs storage=File

check_restore_diff "${BackupDirectory}" "${test_home}/bareos-restores"

read_accounting_totals()
{
  "${PYTHON_EXECUTABLE}" - "$1.json" <<'PY'
import json
import pathlib
import sys

text = pathlib.Path(sys.argv[1]).read_text()
result = json.JSONDecoder().raw_decode(text[text.index("{"):])[0]["result"]
row = result["accounting"][0]
print(":".join(str(row[key]) for key in ("files", "bytes", "rule", "logical_bytes")))
PY
}

if ! accounting_before=$(
  read_accounting_totals "${test_home}/accounting-before-virtual-full.out"
); then
  estat=1
elif ! accounting_after=$(
  read_accounting_totals "${test_home}/accounting-after-virtual-full.out"
); then
  estat=1
elif [[ "$accounting_before" != "$accounting_after" ]]; then
  echo "Subscription accounting changed after Virtual Full:" >&2
  echo "  before: ${accounting_before}" >&2
  echo "  after:  ${accounting_after}" >&2
  estat=1
fi

if on_windows_host; then
  expected_file_count=".."
else
  expected_file_count=87
fi

expect_grep "Consolidating JobIds 1,2 containing ${expected_file_count} files" \
  "${test_home}/virtual.out" \
  "Consolidation message was not emitted"

end_test
