#!/usr/bin/env bash

set -euo pipefail

infra_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
bootstrap="${infra_dir}/scripts/bootstrap.sh"
system_bash="$(command -v bash)"
system_cat="$(command -v cat)"
system_chmod="$(command -v chmod)"
system_grep="$(command -v grep)"
system_mkdir="$(command -v mkdir)"
system_mktemp="$(command -v mktemp)"
system_go="$(command -v go)"
system_python3="$(command -v python3)"
system_rm="$(command -v rm)"

temporary="$("${system_mktemp}" -d "${TMPDIR:-/tmp}/shared-book-infra-bootstrap.XXXXXX")"
trap '"${system_rm}" -rf "${temporary}"' EXIT

pass_count=0

fail() {
  printf 'not ok - %s\n' "$*" >&2
  exit 1
}

pass() {
  pass_count=$((pass_count + 1))
  printf 'ok %d - %s\n' "${pass_count}" "$1"
}

write_command() {
  local directory="$1" name="$2"
  "${system_mkdir}" -p "${directory}"
  "${system_cat}" >"${directory}/${name}"
  "${system_chmod}" +x "${directory}/${name}"
}

assert_contains() {
  local file="$1" expected="$2"
  if ! "${system_grep}" -Fq -- "${expected}" "${file}"; then
    printf '%s\n' "--- ${file}" >&2
    "${system_cat}" "${file}" >&2
    fail "expected output to contain: ${expected}"
  fi
}

assert_empty() {
  local file="$1"
  if [[ -s "${file}" ]]; then
    printf '%s\n' "--- ${file}" >&2
    "${system_cat}" "${file}" >&2
    fail "expected file to be empty"
  fi
}

run_bootstrap() {
  local case_dir="$1" fake_bin="$2"
  shift 2
  (
    export HOME="${case_dir}/home"
    export PATH="${fake_bin}"
    export BREW_LOG="${case_dir}/brew.log"
    "${system_mkdir}" -p "${HOME}"
    "${system_bash}" "${bootstrap}" "$@"
  ) >"${case_dir}/stdout" 2>"${case_dir}/stderr"
}

make_base_commands() {
  local fake_bin="$1"
  for command in cat chmod grep mkdir mktemp rm; do
    local variable="system_${command}"
    local target="${!variable}"
    write_command "${fake_bin}" "${command}" <<EOF
#!/bin/sh
exec "${target}" "\$@"
EOF
  done
  write_command "${fake_bin}" dirname <<'EOF'
#!/bin/sh
value=$1
case "$value" in
  */*) printf '%s\n' "${value%/*}" ;;
  *) printf '.\n' ;;
esac
EOF
  write_command "${fake_bin}" sed <<'EOF'
#!/bin/sh
exec /usr/bin/sed "$@"
EOF
}

probe_missing_tools() {
  local case_dir="${temporary}/missing-tools"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" uname <<'EOF'
#!/bin/sh
printf 'Linux\n'
EOF

  if run_bootstrap "${case_dir}" "${fake_bin}" --check-only; then
    fail "bootstrap --check-only accepted a missing toolchain"
  fi
  assert_contains "${case_dir}/stderr" "Missing required book tools:"
  assert_contains "${case_dir}/stderr" "pandoc"
  assert_contains "${case_dir}/stderr" "typst"
  assert_contains "${case_dir}/stderr" "python3"
  assert_contains "${case_dir}/stderr" "go"
  pass "missing tools fail with actionable diagnostics"
}

make_valid_go() {
  local fake_bin="$1"
  write_command "${fake_bin}" go <<'EOF'
#!/bin/sh
printf 'go version go1.25.9 darwin/arm64\n'
EOF
}

make_valid_book_tools() {
  local fake_bin="$1"
  write_command "${fake_bin}" pandoc <<'EOF'
#!/bin/sh
printf 'pandoc 3.10.2\n'
EOF
  write_command "${fake_bin}" typst <<'EOF'
#!/bin/sh
case "${1:-}" in
  --version) printf 'typst 0.15.1\n' ;;
  fonts) printf 'DejaVu Sans\n' ;;
  *) exit 2 ;;
esac
EOF
  write_command "${fake_bin}" python3 <<EOF
#!/bin/sh
exec "${system_python3}" "\$@"
EOF
  make_valid_go "${fake_bin}"
}

probe_missing_go() {
  local case_dir="${temporary}/missing-go"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" uname <<'EOF'
#!/bin/sh
printf 'Linux\n'
EOF
  write_command "${fake_bin}" pandoc <<'EOF'
#!/bin/sh
printf 'pandoc 3.10.2\n'
EOF
  write_command "${fake_bin}" typst <<'EOF'
#!/bin/sh
case "${1:-}" in
  --version) printf 'typst 0.15.1\n' ;;
  fonts) printf 'Noto Serif CJK SC\n' ;;
  *) exit 2 ;;
esac
EOF
  write_command "${fake_bin}" python3 <<EOF
#!/bin/sh
exec "${system_python3}" "\$@"
EOF

  if run_bootstrap "${case_dir}" "${fake_bin}" --check-only; then
    fail "bootstrap --check-only accepted a toolchain without Go"
  fi
  assert_contains "${case_dir}/stderr" "Missing required book tools:"
  assert_contains "${case_dir}/stderr" "go"
  pass "missing Go fails before the checker can raise a traceback"
}

probe_missing_cjk_font() {
  local case_dir="${temporary}/missing-font"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" uname <<'EOF'
#!/bin/sh
printf 'Linux\n'
EOF
  make_valid_book_tools "${fake_bin}"

  if run_bootstrap "${case_dir}" "${fake_bin}" --check-only; then
    fail "bootstrap --check-only accepted a toolchain without a CJK font"
  fi
  assert_contains "${case_dir}/stderr" "No supported Simplified Chinese font was found."
  assert_contains "${case_dir}/stderr" "Install Noto Serif CJK SC or Source Han Serif SC"
  pass "missing CJK font fails with installation guidance"
}

probe_old_go() {
  local case_dir="${temporary}/old-go"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" uname <<'EOF'
#!/bin/sh
printf 'Linux\n'
EOF
  write_command "${fake_bin}" pandoc <<'EOF'
#!/bin/sh
printf 'pandoc 3.10.2\n'
EOF
  write_command "${fake_bin}" typst <<'EOF'
#!/bin/sh
case "${1:-}" in
  --version) printf 'typst 0.15.1\n' ;;
  fonts) printf 'Noto Serif CJK SC\n' ;;
  *) exit 2 ;;
esac
EOF
  write_command "${fake_bin}" python3 <<EOF
#!/bin/sh
exec "${system_python3}" "\$@"
EOF
  write_command "${fake_bin}" go <<'EOF'
#!/bin/sh
printf 'go version go1.24.9 darwin/arm64\n'
EOF

  if run_bootstrap "${case_dir}" "${fake_bin}" --check-only; then
    fail "bootstrap --check-only accepted an unsupported Go version"
  fi
  assert_contains "${case_dir}/stderr" "Go 1.25.0 or newer is required; found 1.24.9."
  pass "old Go fails with the declared minimum version"
}

probe_noninteractive_does_not_call_brew() {
  local case_dir="${temporary}/noninteractive"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" uname <<'EOF'
#!/bin/sh
printf 'Darwin\n'
EOF
  write_command "${fake_bin}" brew <<'EOF'
#!/bin/sh
printf 'brew was called: %s\n' "$*" >>"${BREW_LOG:?}"
exit 99
EOF
  # Missing tools take the non-interactive macOS path before versions run.

  if run_bootstrap "${case_dir}" "${fake_bin}" </dev/null; then
    fail "non-interactive bootstrap unexpectedly succeeded"
  fi
  assert_contains "${case_dir}/stderr" "Non-interactive shell detected; no system changes were made."
  assert_empty "${case_dir}/brew.log"
  pass "non-interactive bootstrap never invokes brew"
}

probe_python_version_comparison() {
  local case_dir="${temporary}/version-comparison"
  local fake_bin="${case_dir}/bin"
  make_base_commands "${fake_bin}"
  write_command "${fake_bin}" python3 <<EOF
#!/bin/sh
exec "${system_python3}" "\$@"
EOF
  write_command "${fake_bin}" go <<EOF
#!/bin/sh
exec "${system_go}" "\$@"
EOF
  if ! PATH="${fake_bin}" "${system_bash}" -c \
    'source "$1"; version_ge 3.10 3.1 && ! version_ge 0.12 0.13' \
    _ "${infra_dir}/scripts/common.sh"; then
    fail "version comparison does not work without non-POSIX sort -V"
  fi
  pass "version comparison uses portable Python semantics"
}

printf '1..6\n'
probe_missing_tools
probe_missing_go
probe_missing_cjk_font
probe_old_go
probe_noninteractive_does_not_call_brew
probe_python_version_comparison
