#!/bin/bash -eu

MAGENTA='\u001b[35m'
BLUE='\033[0;34m'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

# supported SSL versions
SSL_VERSION_1_1_1w='1.1.1w'
SSL_VERSION_3_0_15='3.0.15'
SSL_VERSION_3_2_1='3.2.1'
SSL_VERSIONS=(
  ${SSL_VERSION_1_1_1w}
  ${SSL_VERSION_3_0_15}
  ${SSL_VERSION_3_2_1}
)

declare -A SSL_IMAGE_MAP
SSL_IMAGE_MAP[$SSL_VERSION_1_1_1w]="bullseye-slim:openssl-${SSL_VERSION_1_1_1w}"
SSL_IMAGE_MAP[$SSL_VERSION_3_0_15]="bookworm-slim:openssl-${SSL_VERSION_3_0_15}"
SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}"

# supported NGINX versions -- for binary distribution -- see: https://nginx.org/en/download.html
#   - most recent legacy version
#   - current stable version
#   - current mainline version
NGINX_VERSIONS=(
  1.28.3 # legacy
  1.30.2 # stable
  1.31.1 # mainline
)

# At the time of writing this, the following versions of libjwt are compatible:
#   * v1.0 - v1.12.0
#   * v1.12.1 - v1.14.0
#   * v1.15.0+
# And the following versions are available in common repos:
#   * Debian Bullseye (11):    1.10.2
#   * Debian Bookworm (12):    1.10.2
#   * Ubuntu Questing (25.10): 1.17.2
#   * Ubuntu Resolute (26.04): 1.17.2
#   * UBI8:                    1.12.1
# This compiles against each version prior to a breaking change and the latest release.
LIBJWT_VERSION_DEBIAN=1.10.2
LIBJWT_VERSION_UBUNTU=1.17.2
LIBJWT_VERSION_UBI8=1.12.1
LIBJWT_VERSION_LATEST=1.18.4
LIBJWT_VERSIONS=(
  ${LIBJWT_VERSION_DEBIAN}
  ${LIBJWT_VERSION_UBUNTU}
  ${LIBJWT_VERSION_UBI8}
  ${LIBJWT_VERSION_LATEST}
)

MODULE_VERSION=$(git describe --tags --abbrev=0 2>/dev/null)
SSL_VERSION=${SSL_VERSION:-$SSL_VERSION_3_0_15}
NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSIONS[-1]}}
LIBJWT_VERSION=${LIBJWT_VERSION:-${LIBJWT_VERSION_UBI8}}
IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt}
FULL_IMAGE_NAME=${ORG_NAME:-teslagov}/${IMAGE_NAME}
TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test"
TEST_COMPOSE_FILE='test/docker-compose-test.yml'

all() {
  build_module
  build_test
  test_all
}

build_base_image() {
  local image=${SSL_IMAGE_MAP[$SSL_VERSION]}
  local baseImage=${image%%:*}

  if [ -z ${image} ]; then
    echo "Base image not set for SSL version :${SSL_VERSION}"
    exit 1
  else
    printf "${MAGENTA}Building ${baseImage} base image for SSL ${SSL_VERSION}...${NC}\n"
    docker buildx build \
      --platform linux/amd64 \
      --build-arg BASE_IMAGE=debian:${baseImage} \
      --build-arg SSL_VERSION=${SSL_VERSION} \
      -f openssl.dockerfile \
      -t ${image} \
      .
  fi
}

build_module() {
  local baseImage=${SSL_IMAGE_MAP[$SSL_VERSION]}

  build_base_image

  printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}, libjwt ${LIBJWT_VERSION}...${NC}\n"
  docker buildx build \
    --platform linux/amd64 \
    -f nginx.dockerfile \
    -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \
    --build-arg BASE_IMAGE=${baseImage} \
    --build-arg MODULE_VERSION=${MODULE_VERSION} \
    --build-arg NGINX_VERSION=${NGINX_VERSION} \
    --build-arg LIBJWT_VERSION=${LIBJWT_VERSION} \
    .

  if [ "$?" -ne 0 ]; then
    printf "${RED}✘ Build failed ${NC}\n"
  else
    printf "${GREEN}✔ Successfully built NGINX module ${NC}\n"
  fi
}

rebuild_module() {
  docker rmi -f $(docker images --filter=label=stage=ngx_http_auth_jwt_builder --quiet) 2> /dev/null || true

  build_module
}

start() {
  local port=$(get_port)

  printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n"
  docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME}:${NGINX_VERSION} >/dev/null
}

stop() {
  docker stop "${IMAGE_NAME}" >/dev/null
}

cp_bin() {
  local destDir=bin
  local stopContainer=0;
  local moduleFileName="ngx_http_auth_jwt_module_${MODULE_VERSION}.so"

  if [ "$(docker container inspect -f '{{.State.Running}}' ${IMAGE_NAME} | true)" != "true" ]; then
    start
    stopContainer=1
  fi

  printf "${MAGENTA}Copying binaries to: ${destDir}${NC}\n"
  rm -rf ${destDir}/*
  mkdir -p ${destDir}
  docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \
    usr/lib/nginx/modules/ngx_http_auth_jwt_module.so \
    usr/local/lib/libjansson.so.* \
    usr/local/lib/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null

  mv "${destDir}/usr/lib/nginx/modules/ngx_http_auth_jwt_module.so" "${destDir}/usr/lib/nginx/modules/${moduleFileName}"

  if [ $stopContainer ]; then
    printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n"
    stop
  fi
}

make_release() {
  local moduleFileName="ngx_http_auth_jwt_module_${MODULE_VERSION}.so"

  printf "${MAGENTA}Making release for version ${MODULE_VERSION} for NGINX ${NGINX_VERSION}...${NC}\n"

  rebuild_module
  rebuild_test
  test --no-build
  cp_bin

  mkdir -p release
  tar -czvf release/ngx-http-auth-jwt-module-${MODULE_VERSION}_libjwt-${LIBJWT_VERSION}_nginx-${NGINX_VERSION}.tgz \
    README.md \
    -C bin/usr/lib/nginx/modules ${moduleFileName} > /dev/null
}

# Create releases for all NGINX versions defined in `NGINX_VERSIONS`.
make_releases() {
  rm -rf release/*

  for NGINX_VERSION in ${NGINX_VERSIONS[@]}; do
    for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do
      export NGINX_VERSION LIBJWT_VERSION
      make_release
    done
  done
}

build_test() {
  local dockerArgs=${1:-}
  local port=$(get_port)
  local sslPort=$(get_port $((port + 1)))
  local runnerBaseImage=${SSL_IMAGE_MAP[$SSL_VERSION]}

  export TEST_CONTAINER_NAME_PREFIX
  export FULL_IMAGE_NAME
  export NGINX_VERSION

  printf "${MAGENTA}Building test NGINX & runner using port ${port}...${NC}\n"
  docker compose \
    -p ${TEST_CONTAINER_NAME_PREFIX} \
    -f ${TEST_COMPOSE_FILE} \
    build \
    --build-arg RUNNER_BASE_IMAGE=${runnerBaseImage} \
    --build-arg PORT=${port} \
    --build-arg SSL_PORT=${sslPort} \
    --build-arg platform="linux/amd64" \
     ${dockerArgs}
}

rebuild_test() {
  build_test --no-cache
}

test_all() {
  for SSL_VERSION in "${SSL_VERSIONS[@]}"; do
    for NGINX_VERSION in "${NGINX_VERSIONS[@]}"; do
      for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do
        export SSL_VERSION NGINX_VERSION LIBJWT_VERSION
        test
      done
    done
  done
}

test() {
  if [[ ! "$*" =~ --no-build ]]; then
    build_module
    build_test
  fi

  trap 'test_cleanup' 0

  printf "${MAGENTA}Running tests...${NC}\n"
  printf "\n"
  printf "Executing tests with the following options:\n"
  printf "  SSL Version:     ${SSL_VERSION}\n"
  printf "  LIBJWT Version:  ${LIBJWT_VERSION}\n"
  printf "  NGINX Version:   ${NGINX_VERSION}\n"

  docker compose \
    -p ${TEST_CONTAINER_NAME_PREFIX} \
    -f ${TEST_COMPOSE_FILE} up --abort-on-container-exit --exit-code-from runner
}

test_cleanup() {
  docker compose \
    -p ${TEST_CONTAINER_NAME_PREFIX} \
    -f ${TEST_COMPOSE_FILE} down
}

get_port() {
  startPort=${1:-8000}
  endPort=$((startPort + 100))

  for p in $(seq ${startPort} ${endPort}); do
    if [ "$(uname)" == "Darwin" ]; then
      if ! lsof -i -P | grep LISTEN | grep -q ":${p} "; then
        echo ${p}
        break
      fi
    elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
      if ! ss -ln | grep -q ":${p} "; then
        echo ${p}
        break
      fi
    fi
  done
}

extract_version() {
  if [ $# -eq 0 ]; then
    printf "Usage: ./scripts extract_version <path/to/ngx_http_auth_jwt_module.so>\n"
    printf "Output: Prints the module version string embedded in the .so file\n"
    exit 1
  fi

  if [ ! -f "$1" ]; then
    printf "${RED}Error: File not found: $1${NC}\n" >&2
    exit 1
  fi

  # Extract the version string using strings and grep
  # The version format is: ngx_http_auth_jwt_module_version=<VERSION>
  version=$(strings "$1" 2>/dev/null | grep '^ngx_http_auth_jwt_module_version=' | cut -d= -f2)

  if [ -z "$version" ]; then
    printf "${RED}No version metadata found in: $1${NC}\n" >&2
    printf "unknown\n"
    exit 1
  else
    printf "${GREEN}${version}${NC}\n"
    exit 0
  fi
}

if [ $# -eq 0 ]; then
  all
else
  fn=$1
  shift

  ${fn} "$@"
fi
