#!/usr/bin/env bats
# tests/test_install_awg_failsoft.sh — Phase 5.7 Item 2: AWG mesh fail-soft.
#
# Covers:
#   1. install.sh wraps install_amneziawg invocation in a soft call:
#      on failure → writes awg=failed_at_setup to channels-status.env,
#      install continues (no die).
#   2. install.sh does NOT wrap internals of install_amneziawg itself —
#      the AWG module's internal die() calls still produce errors.
#   3. healthcheck.sh check 21 treats awg=failed_at_setup as degraded (WARN),
#      not fatal (FAIL) — does not increment FAIL counter.
#   4. healthcheck.sh check 21 still FAIL when awg is the ONLY channel
#      and all others are also failed (overall=failed path unchanged).
#   5. Bats simulation: install.sh AWG section continues when
#      install_amneziawg exits non-zero (awg-quick shim fails).

setup() {
	REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/.." && pwd)"
	TMP="$(mktemp -d)"
	INSTALL="$REPO_ROOT/install.sh"
	HEALTHCHECK="$REPO_ROOT/healthcheck.sh"
}

teardown() {
	rm -rf "$TMP"
}

# ---------------------------------------------------------------------------
# 1. install.sh references install_amneziawg in a soft-wrapped form
#    (does not call it bare — must be wrapped or guarded so failure is caught)
# ---------------------------------------------------------------------------
@test "install.sh wraps install_amneziawg invocation with fail-soft handling" {
	# Presence of awg=failed_at_setup in the write block is the canonical
	# signal that a soft-wrapper was added around the AWG invocation.
	grep -q 'failed_at_setup' "$INSTALL"
}

@test "install.sh writes awg status to channels-status.env" {
	# The channels-status.env write block must include an 'awg' key
	grep -q "awg=" "$INSTALL"
}

# ---------------------------------------------------------------------------
# 2. lib/install-awg.sh internals still use die() — not softened
# ---------------------------------------------------------------------------
@test "lib/install-awg.sh still uses die for internal failures" {
	grep -q 'die ' "$REPO_ROOT/lib/install-awg.sh"
}

# ---------------------------------------------------------------------------
# 3. healthcheck check 21: awg=failed_at_setup treated as degraded (WARN),
#    not incrementing FAIL counter.
# ---------------------------------------------------------------------------
@test "healthcheck check 21 treats awg=failed_at_setup as degraded (not fatal)" {
	# Write a channels-status.env with awg=failed_at_setup but xray=active
	local state_dir="$TMP/state"
	mkdir -p "$state_dir"
	cat > "$state_dir/channels-status.env" <<'EOF'
# Generated by install.sh
xray=active
awg=failed_at_setup
hysteria2=skipped
naive=skipped
EOF
	local conf_dir="$TMP/conf"
	mkdir -p "$conf_dir"
	# Minimal docker-compose.yml to satisfy the early guard in healthcheck.sh
	cat > "$conf_dir/docker-compose.yml" <<'EOF'
version: "3"
services:
  caddy:
    image: caddy
EOF
	local install_env="$state_dir/install.env"
	printf 'PARTNER_DOMAIN=test.example\nNODE_ID=test-node\nTURNS_SUBDOMAIN=api\n' \
		> "$install_env"

	# Run only check 21 portion by sourcing a subset — instead, run the full
	# healthcheck with LOCAL_ONLY and capture FAIL count from output.
	# We check output does NOT contain "21.*FAIL" (it should WARN or OK).
	run bash -c "
		OXPULSE_EDGE_CONFIG_DIR='$conf_dir' \
		OXPULSE_EDGE_STATE_DIR='$state_dir' \
		SYSTEMD_DIR='$TMP/systemd' \
		bash '$HEALTHCHECK' --local 2>/dev/null
	" || true
	# Check 21 line: should be WARN (degraded) or OK, not FAIL
	[[ "$output" != *"21."*"FAIL"* ]]
}

# ---------------------------------------------------------------------------
# 4. healthcheck check 21 still FAILs when all channels are failed
#    (awg=failed_at_setup + xray=failed_at_render = overall=failed)
# ---------------------------------------------------------------------------
@test "healthcheck check 21 FAILs when all channels failed including awg" {
	local state_dir="$TMP/state2"
	mkdir -p "$state_dir"
	cat > "$state_dir/channels-status.env" <<'EOF'
# Generated by install.sh
xray=failed_at_render
awg=failed_at_setup
hysteria2=skipped
naive=skipped
EOF
	local conf_dir="$TMP/conf2"
	mkdir -p "$conf_dir"
	cat > "$conf_dir/docker-compose.yml" <<'EOF'
version: "3"
services:
  caddy:
    image: caddy
EOF
	local install_env="$state_dir/install.env"
	printf 'PARTNER_DOMAIN=test.example\nNODE_ID=test-node\nTURNS_SUBDOMAIN=api\n' \
		> "$install_env"

	run bash -c "
		OXPULSE_EDGE_CONFIG_DIR='$conf_dir' \
		OXPULSE_EDGE_STATE_DIR='$state_dir' \
		SYSTEMD_DIR='$TMP/systemd' \
		bash '$HEALTHCHECK' --local 2>/dev/null
	" || true
	# When all channels failed, check 21 should show FAIL
	[[ "$output" == *"21."*"FAIL"* ]] || [[ "$output" == *"overall=failed"* ]]
}

# ---------------------------------------------------------------------------
# 5. Behavioral: simulate install_amneziawg failure → install continues
#    and awg=failed_at_setup written to channels-status.env
# ---------------------------------------------------------------------------
@test "AWG install failure is soft-wrapped: install continues and status written" {
	local prefix_lib="$TMP/lib"
	local prefix_etc="$TMP/etc"
	mkdir -p "$prefix_lib" "$prefix_etc"

	# Minimal node-config.json (register path reads it)
	cat > "$prefix_etc/node-config.json" <<'EOF'
{"node_id":"test-node","partner_id":"test","domain":"test.example"}
EOF

	# Create a fake install_amneziawg that fails
	# Test: source lib/install-awg.sh, override install_amneziawg to fail,
	# then call the wrapper code added around the AWG invocation block.
	run bash -c "
		source '$REPO_ROOT/lib/install-awg.sh' 2>/dev/null || true
		source '$REPO_ROOT/lib/render-channel-lib.sh' 2>/dev/null || true

		# Override install_amneziawg to always fail
		install_amneziawg() { return 1; }
		configure_amneziawg() { return 0; }

		# Helper stubs matching install.sh environment
		log()  { echo \"LOG: \$*\"; }
		warn() { echo \"WARN: \$*\"; }
		die()  { echo \"DIE: \$*\" >&2; exit 1; }

		PREFIX_LIB='$prefix_lib'
		AWG_ALLOCATED_IP='10.100.0.42/32'
		AWG_MOTHERLY_PUBKEY='AAAA='
		DRY_RUN=0
		SFU_EDGE_ID='test-node'

		# Call the same soft-wrapped invocation pattern that install.sh uses
		_awg_status='skipped'
		if [[ -n \"\${AWG_ALLOCATED_IP:-}\" && -n \"\${AWG_MOTHERLY_PUBKEY:-}\" && \$DRY_RUN -eq 0 ]]; then
			if install_amneziawg; then
				configure_amneziawg
			else
				warn '[awg] install_amneziawg failed — marking awg=failed_at_setup'
				_awg_status='failed_at_setup'
			fi
		fi

		# Write channels-status with awg status
		mkdir -p '$prefix_lib'
		_chs_tmp=\$(mktemp '$prefix_lib/.chs-XXXXXX.tmp')
		printf 'xray=active\nawg=%s\nhysteria2=skipped\nnaive=skipped\n' \"\$_awg_status\" > \"\$_chs_tmp\"
		mv -f \"\$_chs_tmp\" '$prefix_lib/channels-status.env'

		echo \"AWG_STATUS=\$_awg_status\"
	"
	[ "$status" -eq 0 ]
	[[ "$output" == *"AWG_STATUS=failed_at_setup"* ]]
	[ -f "$prefix_lib/channels-status.env" ]
	grep -q 'awg=failed_at_setup' "$prefix_lib/channels-status.env"
}

# ---------------------------------------------------------------------------
# MAJOR 4: install_amneziawg with internal die() — subshell wraps exit call
# ---------------------------------------------------------------------------
@test "install.sh wraps install_amneziawg in subshell so internal die() does not exit parent" {
	# Verify the invocation is wrapped in a subshell: ( install_amneziawg )
	# This is the fix for MAJOR 4: die() inside install_amneziawg calls exit 1
	# which exits the parent shell unless wrapped in a subshell.
	grep -qE '\(\s*install_amneziawg\s*\)' "$INSTALL"
}

@test "AWG internal die() does not kill parent install when subshell-wrapped" {
	# Simulate: install_amneziawg calls die() internally (which calls exit 1).
	# With subshell wrapping ( install_amneziawg ), the parent shell must survive.
	run bash -c "
		set -euo pipefail
		log()  { echo \"LOG: \$*\"; }
		warn() { echo \"WARN: \$*\"; }
		die()  { echo \"DIE: \$*\" >&2; exit 1; }

		install_amneziawg() {
			# Simulates internal die() — exits the function's shell
			die 'amneziawg git clone failed'
		}

		_awg_status='skipped'
		AWG_ALLOCATED_IP='10.100.0.42/32'
		AWG_MOTHERLY_PUBKEY='AAAA='
		DRY_RUN=0

		if [[ -n \"\${AWG_ALLOCATED_IP:-}\" && -n \"\${AWG_MOTHERLY_PUBKEY:-}\" && \$DRY_RUN -eq 0 ]]; then
			if ( install_amneziawg ); then
				_awg_status='active'
			else
				warn '[awg] install_amneziawg failed — marking awg=failed_at_setup'
				_awg_status='failed_at_setup'
			fi
		fi

		echo \"AWG_STATUS=\$_awg_status\"
		echo 'INSTALL_CONTINUES'
	"
	[ "$status" -eq 0 ]
	[[ "$output" == *"AWG_STATUS=failed_at_setup"* ]]
	[[ "$output" == *"INSTALL_CONTINUES"* ]]
}
