#!/usr/bin/env bash
# shellcheck shell=bash
set -euo pipefail

addon_slug="${SCRUTINY_HA_ADDON_SLUG:-scrutiny}"
data_dir="${SCRUTINY_INFLUXDB_DATA_DIR:-/data/influxdb}"
backup_dir="${SCRUTINY_INFLUXDB_BACKUP_DIR:-/share/${addon_slug}}"
marker_file="${data_dir}/.scrutiny-influxdb-2.9-preflight-complete"
marker_tmp="${marker_file}.tmp"
backup_file="${backup_dir}/influxdb-pre-2.9.tar.gz"
temporary_backup="${backup_file}.tmp"
archive_root="$(basename "$data_dir")"
validation_method="content-sha256-v1"
source_manifest=""
archive_manifest=""

log() {
	local level="$1"
	local message="$2"
	printf 'time="%s" level=%s msg="%s" type=ha-influxdb-upgrade-preflight\n' \
		"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$message"
}

cleanup() {
	rm -f "$temporary_backup" "$marker_tmp"
	if [[ -n "$source_manifest" ]]; then rm -f "$source_manifest"; fi
	if [[ -n "$archive_manifest" ]]; then rm -f "$archive_manifest"; fi
}
trap cleanup EXIT

has_existing_data() {
	if [[ -e "${data_dir}/influxd.bolt" || -e "${data_dir}/influxd.sqlite" ]]; then
		return 0
	fi

	if [[ -d "${data_dir}/engine" ]] && find "${data_dir}/engine" -mindepth 1 -print -quit | grep -q .; then
		return 0
	fi

	return 1
}

validate_archive() {
	local archive="$1"
	[[ -s "$archive" ]] || return 1
	gzip -t "$archive" >/dev/null 2>&1 || return 1
	tar -tzf "$archive" >/dev/null 2>&1
}

reset_manifests() {
	rm -f "$source_manifest" "$archive_manifest"
	source_manifest=""
	archive_manifest=""
}

validate_backup_against_source() {
	local archive="$1"
	local relative_path
	local source_path
	local archive_member
	local listing
	local source_checksum
	local archive_checksum
	local source_target
	local archive_target

	validate_archive "$archive" || return 1
	source_manifest="$(mktemp)"
	archive_manifest="$(mktemp)"

	(
		cd "$data_dir"
		find . -mindepth 1 -print | sed -e 's#^\./##' -e 's#/$##' | LC_ALL=C sort
	) >"$source_manifest"

	tar -tzf "$archive" |
		sed -e "s#^${archive_root}/##" -e '/^$/d' -e 's#/$##' |
		LC_ALL=C sort >"$archive_manifest"

	if ! cmp -s "$source_manifest" "$archive_manifest"; then
		reset_manifests
		return 1
	fi

	while IFS= read -r relative_path; do
		source_path="${data_dir}/${relative_path}"
		archive_member="${archive_root}/${relative_path}"
		listing="$(tar -tvzf "$archive" -- "$archive_member" 2>/dev/null)" || {
			reset_manifests
			return 1
		}

		if [[ -L "$source_path" ]]; then
			[[ "${listing:0:1}" == "l" ]] || {
				reset_manifests
				return 1
			}
			source_target="$(readlink "$source_path")"
			archive_target="${listing##* -> }"
			[[ "$source_target" == "$archive_target" ]] || {
				reset_manifests
				return 1
			}
		elif [[ -f "$source_path" ]]; then
			[[ "${listing:0:1}" == "-" || "${listing:0:1}" == "h" ]] || {
				reset_manifests
				return 1
			}
			source_checksum="$(sha256sum "$source_path" | awk '{print $1}')"
			archive_checksum="$(tar -xOzf "$archive" -- "$archive_member" | sha256sum | awk '{print $1}')"
			[[ "$source_checksum" == "$archive_checksum" ]] || {
				reset_manifests
				return 1
			}
		elif [[ -d "$source_path" ]]; then
			[[ "${listing:0:1}" == "d" ]] || {
				reset_manifests
				return 1
			}
		else
			reset_manifests
			return 1
		fi
	done <"$source_manifest"

	reset_manifests
}

marker_value() {
	local key="$1"
	awk -F= -v key="$key" '$1 == key {sub(/^[^=]*=/, ""); print; exit}' "$marker_file"
}

write_marker() {
	local state="$1"
	local checksum="${2:-}"
	local validation="${3:-}"

	{
		printf 'state=%s\n' "$state"
		if [[ -n "$validation" ]]; then printf 'validation=%s\n' "$validation"; fi
		if [[ -n "$checksum" ]]; then printf 'backup_sha256=%s\n' "$checksum"; fi
	} >"$marker_tmp"
	mv "$marker_tmp" "$marker_file"
}

validate_existing_marker() {
	local state
	local validation
	local expected_checksum
	local actual_checksum

	state="$(marker_value state)"
	validation="$(marker_value validation)"

	case "$state" in
	no-legacy-data)
		if [[ -n "$validation" && "$validation" != "no-data-v1" ]]; then
			log error "no-data migration marker uses an unknown validation method"
			return 1
		fi
		if [[ -z "$validation" ]]; then
			write_marker no-legacy-data "" no-data-v1
			log warning "upgraded no-data migration marker with explicit validation state"
		fi
		return 0
		;;
	legacy-backup)
		if [[ "$validation" != "$validation_method" ]]; then
			log error "legacy backup marker lacks content-level validation proof"
			return 1
		fi
		expected_checksum="$(marker_value backup_sha256)"
		if [[ -z "$expected_checksum" ]] || ! validate_archive "$backup_file"; then
			log error "legacy backup marker exists but its archive cannot be verified"
			return 1
		fi
		actual_checksum="$(sha256sum "$backup_file" | awk '{print $1}')"
		if [[ "$actual_checksum" != "$expected_checksum" ]]; then
			log error "legacy backup checksum does not match the migration marker"
			return 1
		fi
		return 0
		;;
	*)
		log error "legacy migration marker lacks content-level validation proof"
		return 1
		;;
	esac
}

mkdir -p "$data_dir"

if [[ -f "$marker_file" ]]; then
	if validate_existing_marker; then exit 0; fi
	exit 1
fi

if ! has_existing_data; then
	write_marker no-legacy-data "" no-data-v1
	log info "no existing InfluxDB data detected; recorded no-data migration state"
	exit 0
fi

mkdir -p "$backup_dir"

if [[ -e "$backup_file" ]] && validate_backup_against_source "$backup_file"; then
	log info "using existing content-verified InfluxDB backup at ${backup_file}"
else
	if [[ -e "$backup_file" ]]; then
		log warning "existing InfluxDB backup is stale, incomplete, or invalid and will be atomically replaced"
	fi

	log warning "creating offline InfluxDB backup at ${backup_file}"
	tar -C "$(dirname "$data_dir")" -czf "$temporary_backup" "$archive_root"

	if ! validate_backup_against_source "$temporary_backup"; then
		log error "created InfluxDB backup failed content-level validation"
		exit 1
	fi

	mv -f "$temporary_backup" "$backup_file"
	log info "InfluxDB backup created and content-verified at ${backup_file}"
fi

backup_checksum="$(sha256sum "$backup_file" | awk '{print $1}')"
write_marker legacy-backup "$backup_checksum" "$validation_method"
log info "InfluxDB 2.9 migration marker created with content-validation method and backup checksum"
