#!/usr/bin/env bats
# Phase 2 Task 6 — install.sh must expose render_with_opec() and
# use it for xray/coturn/naive call sites. The fallback branch (opec NOT on
# PATH) must produce byte-identical output to the Phase-1 golden fixtures.

setup() {
  cd "$BATS_TEST_DIRNAME/.."
  # shellcheck source=../channel-render-lib.sh
  source ./channel-render-lib.sh
}

set_frozen_vars() {
  PARTNER_ID=zvonilka
  DOMAIN=zvonilka.net
  BACKEND_ENDPOINT=192.9.243.148:5349
  BACKEND_HOST=192.9.243.148
  BACKEND_PORT=5349
  TURN_SECRET=test-turn-secret-deadbeef
  REALITY_UUID=d529dee6-3cdd-4079-95d1-f8801722147c
  REALITY_PUBLIC_KEY=U6ea044JJjgiCjQAnYEBqBBlkeSqrQaLq3lcjnN2EFk
  REALITY_SHORT_ID=abcd1234
  REALITY_SERVER_NAME=www.samsung.com
  REALITY_ENCRYPTION=mlkem768x25519plus.native.0rtt.fXgOoxcW
  TURNS_SUBDOMAIN=api-test01
  PUBLIC_IP=157.22.204.190
  PRIVATE_IP=
  EXTERNAL_IP_LINE=157.22.204.190
  IMAGE_VERSION=stable
  SFU_UDP_PORT=7878
  SFU_METRICS_PORT=9317
  SFU_EDGE_ID=zvonilka1
  OTEL_EXPORTER_OTLP_ENDPOINT=
  SFU_SIGNING_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAZiwaWp+FJ1sGprGGS69mq+sB6nhwOMi24xGSGfgdXNo=\n-----END PUBLIC KEY-----\n'
  RELAY_JWT_SECRET=test-relay-jwt-secret
  SIGNALING_SFU_SECRET=test-signaling-sfu-secret
  HYSTERIA2_SERVER=
  HYSTERIA2_PORT=51822
  HYSTERIA2_AUTH=
  HYSTERIA2_OBFS=
  HYSTERIA2_SOCKS_PORT=18891
  HY2_SERVER=
  HY2_AUTH_PASS=
  HY2_OBFS_PASS=
  HY2_LOCAL_LISTEN=
  HY2_REMOTE_BACKEND=
  NAIVE_SERVER=
  NAIVE_PORT=44433
  NAIVE_USER=
  NAIVE_PASS=
  NAIVE_SOCKS_PORT=18892
}

mirror_install_exports() {
  PARTNER_DOMAIN="$DOMAIN"
  export PARTNER_ID PARTNER_DOMAIN BACKEND_ENDPOINT BACKEND_HOST BACKEND_PORT \
         TURN_SECRET \
         REALITY_UUID REALITY_PUBLIC_KEY REALITY_SHORT_ID REALITY_SERVER_NAME \
         REALITY_ENCRYPTION TURNS_SUBDOMAIN \
         PUBLIC_IP PRIVATE_IP EXTERNAL_IP_LINE \
         IMAGE_VERSION \
         SFU_UDP_PORT SFU_METRICS_PORT SFU_EDGE_ID \
         OTEL_EXPORTER_OTLP_ENDPOINT \
         SFU_SIGNING_PUBLIC_KEY RELAY_JWT_SECRET SIGNALING_SFU_SECRET \
         HYSTERIA2_SERVER HYSTERIA2_PORT HYSTERIA2_AUTH HYSTERIA2_OBFS HYSTERIA2_SOCKS_PORT \
         NAIVE_SERVER NAIVE_PORT NAIVE_USER NAIVE_PASS NAIVE_SOCKS_PORT \
         HY2_SERVER HY2_AUTH_PASS HY2_OBFS_PASS HY2_LOCAL_LISTEN HY2_REMOTE_BACKEND
}

@test "install.sh exposes render_with_opec helper" {
  grep -qE 'render_with_opec\(\)' install.sh
}

@test "install.sh calls render_with_opec for xray" {
  grep -qE 'render_with_opec[[:space:]]+xray' install.sh
}

@test "install.sh calls render_with_opec for coturn" {
  grep -qE 'render_with_opec[[:space:]]+coturn' install.sh
}

@test "install.sh calls render_with_opec for naive" {
  grep -qE 'render_with_opec[[:space:]]+naive' install.sh
}

@test "install.sh calls render_with_opec for compose" {
  grep -qE 'render_with_opec[[:space:]]+compose' install.sh
}

@test "install.sh calls render_with_opec for caddy" {
  grep -qE 'render_with_opec[[:space:]]+caddy' install.sh
}

@test "install.sh no longer has bare render_template calls for stage templates" {
  ! grep -qE 'render_template[[:space:]]+"\$stage/' install.sh
}

# Phase 4.4 removed the bash render_template fallback branch entirely.
# `render_with_opec` now die's loudly if `opec` is not on PATH.
@test "render_with_opec hard-fails without opec on PATH" {
  set_frozen_vars
  mirror_install_exports
  eval "$(awk '/^render_with_opec\(\) \{/,/^\}/' install.sh)"
  # Provide a die stub so the test can observe the exit instead of inheriting bats' set -e.
  die() { echo "die: $*" >&2; return 1; }
  out=$(mktemp)
  run env PATH=/usr/bin:/bin bash -c "
    die() { echo \"die: \$*\" >&2; exit 1; }
    $(awk '/^render_with_opec\(\) \{/,/^\}/' install.sh)
    render_with_opec xray '$BATS_TEST_DIRNAME/fixtures/install-render/xray.tpl' '$out'
  "
  rm -f "$out"
  [ "$status" -ne 0 ]
  [[ "$output" == *"opec binary not on PATH"* ]]
}

@test "install.sh sources lib/install-preflight.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-preflight\.sh' install.sh
}

@test "install.sh sources lib/install-deps.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-deps\.sh' install.sh
}

@test "install.sh calls preflight_run instead of inline Step 1" {
    grep -qE '^[[:space:]]*preflight_run([[:space:]]|$)' install.sh
}

@test "install.sh calls deps_install instead of inline Step 2" {
    grep -qE '^[[:space:]]*deps_install([[:space:]]|$)' install.sh
}

@test "install.sh no longer inlines OS_FAMILY detection" {
    ! grep -qE '\*\" debian \"\*\|\*\" ubuntu \"\*\) OS_FAMILY=debian' install.sh
}

@test "install.sh sources lib/install-network.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-network\.sh' install.sh
}

@test "install.sh calls network_run instead of inline Step 3" {
    grep -qE '^[[:space:]]*network_run([[:space:]]|$)' install.sh
}

@test "install.sh no longer inlines _detect_public_ipv4" {
    ! grep -qE '^_detect_public_ipv4\(\)' install.sh
}

@test "install.sh no longer inlines _detect_region" {
    ! grep -qE '^_detect_region\(\)' install.sh
}

@test "install.sh unconditionally delegates reality-keygen to opec (Phase 4.8)" {
    # Phase 4.8: env flag retired; opec call must still exist, flag must not.
    ! grep -qE 'OPEC_SECRETS_REALITY_KEYGEN' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+reality-keygen' install.sh
}

@test "install.sh no longer references OPEC_SECRETS_REALITY_KEYGEN env flag (retired)" {
    # Phase 4.8 regression guard: bash fallback branch deleted, env escape hatch gone.
    ! grep -qE 'OPEC_SECRETS_REALITY_KEYGEN' install.sh
}

@test "install.sh OPEC path honors DRY_RUN (no side-effect invocation)" {
    # The OPEC dispatcher must not call 'opec secrets reality-keygen' in
    # dry-run mode. Encoded as: there is a DRY_RUN check near the opec call.
    grep -qE 'DRY_RUN[[:space:]]*-eq[[:space:]]*1' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+reality-keygen' install.sh
}

@test "install.sh OPEC path maps FORCE_KEYGEN to --rotate" {
    # --force-keygen / --rotate-identity flags set FORCE_KEYGEN=1; OPEC branch
    # must translate that to the --rotate flag so operator rotation works
    # regardless of which backend is active.
    grep -qE 'FORCE_KEYGEN[[:space:]]*-eq[[:space:]]*1.*--rotate|--rotate.*FORCE_KEYGEN' install.sh
}

@test "install.sh unconditionally delegates awg-keygen to opec (Phase 4.8)" {
    # Phase 4.8: env flag retired; opec call must still exist, flag must not.
    ! grep -qE 'OPEC_SECRETS_AWG_KEYGEN' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+awg-keygen' install.sh
}

@test "install.sh no longer references OPEC_SECRETS_AWG_KEYGEN env flag (retired)" {
    # Phase 4.8 regression guard: bash fallback (wg genkey / wg pubkey) deleted.
    ! grep -qE 'OPEC_SECRETS_AWG_KEYGEN' install.sh
}

@test "install.sh OPEC awg path honors DRY_RUN" {
    grep -qE 'dryrun-awg-pubkey-placeholder' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+awg-keygen' install.sh
}

@test "install.sh OPEC awg path maps FORCE_KEYGEN to --rotate" {
    grep -qE 'FORCE_KEYGEN[[:space:]]*-eq[[:space:]]*1.*--rotate|--rotate.*FORCE_KEYGEN' install.sh
}

# ---------------------------------------------------------------------------
# Phase 4.3c Task 4 — register POST delegation
# ---------------------------------------------------------------------------

@test "install.sh unconditionally delegates register to opec (Phase 4.8)" {
    # Phase 4.8: env flag retired; opec call must still exist, flag must not.
    ! grep -qE 'OPEC_SECRETS_REGISTER' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+register' install.sh
}

@test "install.sh no longer references OPEC_SECRETS_REGISTER env flag (retired)" {
    # Phase 4.8 regression guard: bash fallback (python+curl register) deleted.
    ! grep -qE 'OPEC_SECRETS_REGISTER' install.sh
}

@test "install.sh OPEC register path sets NODE_ID from env-file" {
    grep -qE '\. "\$tmp_cfg\.env"|source[[:space:]]+"\$tmp_cfg\.env"' install.sh
}

@test "install.sh OPEC register path honors DRY_RUN" {
    grep -qE 'DRY_RUN[[:space:]]*-eq[[:space:]]*1' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+register' install.sh
}

@test "install.sh MANUAL_CONFIG bypasses OPEC register" {
    # MANUAL_CONFIG branch must precede the opec register invocation.
    # Phase 4.8: OPEC_SECRETS_REGISTER gone; anchor on 'opec secrets register'.
    grep -nE 'MANUAL_CONFIG|opec[[:space:]]+secrets[[:space:]]+register' install.sh \
        | awk -F: 'NR==1{first=$2} END{exit (first ~ /MANUAL_CONFIG/) ? 0 : 1}'
}

@test "install.sh AWG keygen block precedes register dispatch" {
    # AWG_PRIV_PATH assignment must appear before opec secrets register
    # so AWG_PUB_PATH is ready when register is called.
    # Phase 4.8: OPEC_SECRETS_REGISTER and OPEC_SECRETS_AWG_KEYGEN gone;
    # anchor on the opec call lines directly.
    local awg_line reg_line
    awg_line=$(grep -nE 'AWG_PRIV_PATH="\$PREFIX_ETC' install.sh | head -1 | cut -d: -f1)
    reg_line=$(grep -nE 'opec[[:space:]]+secrets[[:space:]]+register' install.sh | head -1 | cut -d: -f1)
    [[ -n "$awg_line" && -n "$reg_line" && "$awg_line" -lt "$reg_line" ]]
}

@test "install.sh no longer guards extraction on OPEC_REGISTER_USED" {
    # BLOCKER fix: OPEC_REGISTER_USED was deleted in Phase 4.8; lock in the cleanup.
    ! grep -qE 'OPEC_REGISTER_USED' install.sh
}

@test "install.sh out-json capability probe block exists before opec register call" {
    # MAJOR #3: install.sh must probe for --out-json support before invoking opec
    # so pre-v0.12.48 binaries get re-downloaded rather than silently failing.
    # The probe pattern: run `opec secrets register --help` and grep for --out-json.
    grep -qE "opec secrets register.*--help.*grep.*out-json|grep.*out-json.*opec secrets register|--out-json.*pre-v0\.12" install.sh
}

@test "install.sh dry-run mock has edge_id inside awg block (not top-level sfu_edge_id)" {
    # MAJOR #2: backend returns edge_id inside the awg{} sub-object (AwgConfig).
    # install.sh must NOT use a top-level sfu_edge_id key — awg_extract is the
    # correct extractor. Verify the dry-run mock mirrors the real schema.
    python3 - install.sh <<'PYCHECK'
import re, sys
content = open(sys.argv[1]).read()
m = re.search(r'cat >"?\$tmp_cfg"? <<DRYJSON(.*?)DRYJSON', content, re.DOTALL)
if not m:
    print("dry-run JSON block not found", file=sys.stderr); sys.exit(1)
block = m.group(1)
# top-level sfu_edge_id must be gone
if '"sfu_edge_id"' in block:
    print("FAIL: top-level sfu_edge_id still present in dry-run mock", file=sys.stderr); sys.exit(1)
# edge_id and otel_endpoint must appear inside the awg block.
# Use a balanced-brace search: find "awg": { ... } allowing nested ${...}.
awg_start = block.find('"awg"')
if awg_start < 0:
    print("FAIL: no awg key in dry-run mock", file=sys.stderr); sys.exit(1)
brace_start = block.find('{', awg_start)
if brace_start < 0:
    print("FAIL: no { after awg key", file=sys.stderr); sys.exit(1)
depth, i, awg_block = 0, brace_start, ""
while i < len(block):
    ch = block[i]
    if ch == '{': depth += 1
    elif ch == '}':
        depth -= 1
        if depth == 0:
            awg_block = block[brace_start+1:i]
            break
    i += 1
if not awg_block:
    print("FAIL: could not extract awg{} block", file=sys.stderr); sys.exit(1)
if '"edge_id"' not in awg_block:
    print("FAIL: edge_id missing from awg{} block", file=sys.stderr); sys.exit(1)
if '"otel_endpoint"' not in awg_block:
    print("FAIL: otel_endpoint missing from awg{} block", file=sys.stderr); sys.exit(1)
print("OK")
PYCHECK
}

# ---------------------------------------------------------------------------
# Phase 4.3d Task — sfu-signing-key delegation
# ---------------------------------------------------------------------------

@test "install.sh unconditionally delegates sfu-signing-key to opec (Phase 4.8)" {
    # Phase 4.8: env flag retired; opec call must still exist, flag must not.
    ! grep -qE 'OPEC_SECRETS_SFU_KEY' install.sh
    grep -qE 'opec[[:space:]]+secrets[[:space:]]+sfu-signing-key' install.sh
}

@test "install.sh no longer references OPEC_SECRETS_SFU_KEY env flag (retired)" {
    # Phase 4.8 regression guard: bash fallback (curl /api/partner/keys) deleted.
    ! grep -qE 'OPEC_SECRETS_SFU_KEY' install.sh
}

@test "install.sh OPEC sfu-signing-key path warns on failure (not die)" {
    grep -qE 'warn.*sfu-signing-key' install.sh
}

# ---------------------------------------------------------------------------
# Phase 4.6 — healthcheck module delegation
# ---------------------------------------------------------------------------

@test "install.sh sources lib/install-healthcheck.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-healthcheck\.sh' install.sh
}

@test "install.sh calls healthcheck_run instead of inline [7/10]" {
    grep -qE '^[[:space:]]*healthcheck_run([[:space:]]|$)' install.sh
}

@test "install.sh no longer inlines [7/10] healthcheck log call" {
    ! grep -qE 'log.*\[7/10\].*waiting for healthcheck' install.sh
}

# ---------------------------------------------------------------------------
# Phase 4.7 — systemd module delegation
# ---------------------------------------------------------------------------

@test "install.sh sources lib/install-systemd.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-systemd\.sh' install.sh
}

@test "install.sh calls systemd_run instead of inline Step 8" {
    grep -qE '^[[:space:]]*systemd_run([[:space:]]|$)' install.sh
}

@test "install.sh no longer inlines [8/10] log" {
    ! grep -qE 'log[[:space:]]+"?\[8/10\] installing systemd unit' install.sh
}

# ---------------------------------------------------------------------------
# Phase 4.9 — args parsing module delegation
# ---------------------------------------------------------------------------

@test "install.sh sources lib/install-args.sh module" {
    grep -qE '_install_lib_source[[:space:]]+install-args\.sh' install.sh
}

@test "install.sh calls args_parse to process CLI arguments" {
    grep -qE '^[[:space:]]*args_parse[[:space:]]+"\$@"' install.sh
}

@test "install.sh no longer inlines the while \$# -gt 0 arg-parser loop" {
    ! grep -qE 'while[[:space:]]+\[\[[[:space:]]+\$#[[:space:]]+-gt[[:space:]]+0[[:space:]]+\]\]' install.sh
}
