#!/bin/sh

set -u

failures=0
tmpdir=""

log_ok() {
  printf 'PASS: %s\n' "$1"
}

log_fail() {
  printf 'FAIL: %s\n' "$1" >&2
  failures=$((failures + 1))
}

find_tool() {
  first_choice="$1"
  shift

  if [ -n "${first_choice}" ] && command -v "$first_choice" >/dev/null 2>&1; then
    printf '%s\n' "$first_choice"
    return 0
  fi

  for candidate in "$@"; do
    if command -v "$candidate" >/dev/null 2>&1; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done

  return 1
}

cleanup() {
  if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
    rm -rf "$tmpdir"
  fi
}

trap cleanup EXIT INT TERM HUP

printf 'Linux by Intent host readiness check (sh)\n'

if command -v sh >/dev/null 2>&1 && sh -c 'exit 0' >/dev/null 2>&1; then
  log_ok 'sh is available and executable'
else
  log_fail 'sh is missing or cannot execute commands'
fi

for tool in git gzip awk cmake meson rustc cargo rsync clang ninja bmake; do
  if command -v "$tool" >/dev/null 2>&1; then
    log_ok "$tool is installed"
  else
    log_fail "$tool is missing"
  fi
done

yacc_cmd=$(find_tool "${YACC-}" yacc byacc || true)
if [ -n "$yacc_cmd" ]; then
  log_ok "yacc-compatible parser generator found: $yacc_cmd"
else
  log_fail 'no yacc-compatible parser generator was found'
fi

cc_cmd=$(find_tool "${CC-}" cc clang gcc || true)
cxx_cmd=$(find_tool "${CXX-}" c++ clang++ g++ || true)

if [ -n "$cc_cmd" ]; then
  log_ok "C compiler found: $cc_cmd"
else
  log_fail 'no working C compiler command was found'
fi

if [ -n "$cxx_cmd" ]; then
  log_ok "C++ compiler found: $cxx_cmd"
else
  log_fail 'no working C++ compiler command was found'
fi

if [ -n "$cc_cmd" ] && [ -n "$cxx_cmd" ]; then
  tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t linuxbook-ready)

  cat > "$tmpdir/test.c" <<'EOF'
#include <stdio.h>

int main(void) {
  puts("c-ok");
  return 0;
}
EOF

  if "$cc_cmd" -o "$tmpdir/test-c" "$tmpdir/test.c" >/dev/null 2>&1 && "$tmpdir/test-c" >/dev/null 2>&1; then
    log_ok 'C compilation and execution succeeded'
  else
    log_fail 'C compilation or execution failed'
  fi

  cat > "$tmpdir/test.cpp" <<'EOF'
#include <iostream>

int main() {
  std::cout << "cpp-ok\n";
  return 0;
}
EOF

  if "$cxx_cmd" -o "$tmpdir/test-cpp" "$tmpdir/test.cpp" >/dev/null 2>&1 && "$tmpdir/test-cpp" >/dev/null 2>&1; then
    log_ok 'C++ compilation and execution succeeded'
  else
    log_fail 'C++ compilation or execution failed'
  fi

  cat > "$tmpdir/libcheck.c" <<'EOF'
int readiness_value(void) {
  return 42;
}
EOF

  cat > "$tmpdir/linkcheck.c" <<'EOF'
int readiness_value(void);

int main(void) {
  return readiness_value() == 42 ? 0 : 1;
}
EOF

  if "$cc_cmd" -c -o "$tmpdir/libcheck.o" "$tmpdir/libcheck.c" >/dev/null 2>&1 && \
    "$cc_cmd" -c -o "$tmpdir/linkcheck.o" "$tmpdir/linkcheck.c" >/dev/null 2>&1 && \
    "$cc_cmd" -o "$tmpdir/link-test" "$tmpdir/linkcheck.o" "$tmpdir/libcheck.o" >/dev/null 2>&1 && \
    "$tmpdir/link-test" >/dev/null 2>&1; then
    log_ok 'linking object files into an executable succeeded'
  else
    log_fail 'linking test failed'
  fi
fi

if [ "$failures" -eq 0 ]; then
  printf 'RESULT: system appears ready for the book\n'
  exit 0
fi

printf 'RESULT: system is not ready for the book (%s failure(s))\n' "$failures" >&2
exit 1
