#!/bin/bash

set -euo pipefail

OWN_FILENAME="$(basename $0)"
LAMBDA_EXTENSION_NAME="$OWN_FILENAME" # (external) extension name has to match the filename
TMPFILE=/tmp/$OWN_FILENAME

# Graceful Shutdown
_term() {
  echo "[${LAMBDA_EXTENSION_NAME}] Received EXIT"
  # forward EXIT to child procs and exit
  kill -TERM "$PID" 2>/dev/null
  echo "[${LAMBDA_EXTENSION_NAME}] Exiting"
  exit 0
}

forward_exit_and_wait() {
  trap _term EXIT
  wait "$PID"
  trap - EXIT
}

start_agent() {
  if [ -z "${AGENT_PID:-}" ]; then
    echo "[${LAMBDA_EXTENSION_NAME}] Starting Secrets Manager Agent."
    # Switching working directory to ensure that the logs are written to a folder that has write permissions.
    (cd /tmp && /opt/bin/secrets-manager-agent &)
    AGENT_PID=$!

    echo "[${LAMBDA_EXTENSION_NAME}] Checking if the Agent is serving requests."
    RETRIES=0
    MAX_RETRIES=200
    while true; do
      RESPONSE=$(curl -s http://localhost:2773/ping || echo "Agent has not started yet.")
      if [ "$RESPONSE" = "healthy" ]; then
        echo "[${LAMBDA_EXTENSION_NAME}] Agent has started."
        break
      else
        if [ $RETRIES -ge $MAX_RETRIES ]; then
          echo "[${LAMBDA_EXTENSION_NAME}] Agent failed to start after $MAX_RETRIES retries."
          exit 1
        fi
        echo "[${LAMBDA_EXTENSION_NAME}] Agent has not started yet, retrying in 100 milliseconds..."
        sleep 0.1 # Sleep for 100 milliseconds
      fi
    done
  else
    echo "[${LAMBDA_EXTENSION_NAME}] Agent already started, ignoring INVOKE event."
  fi
}


stop_agent() {
  if [ -n "$AGENT_PID" ]; then
    echo "[${LAMBDA_EXTENSION_NAME}] Stopping the Secrets Manager Agent."
    kill "$AGENT_PID" 2>/dev/null
    unset AGENT_PID
  else
    echo "[${LAMBDA_EXTENSION_NAME}] Agent not running. Nothing to stop."
  fi
}


# Initialization
# To run any extension processes that need to start before the runtime initializes, run them before the /register
echo "[${LAMBDA_EXTENSION_NAME}] Initialization"

# Registration
# The extension registration also signals to Lambda to start initializing the runtime.
HEADERS="$(mktemp)"
echo "[${LAMBDA_EXTENSION_NAME}] Registering at http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register"
curl -sS -LD "$HEADERS" -XPOST "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register" --header "Lambda-Extension-Name: ${LAMBDA_EXTENSION_NAME}" -d "{ \"events\": [\"INVOKE\", \"SHUTDOWN\"]}" > $TMPFILE

RESPONSE=$(<$TMPFILE)
HEADINFO=$(<$HEADERS)
# Extract Extension ID from response headers
EXTENSION_ID=$(grep -Fi Lambda-Extension-Identifier "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
echo "[${LAMBDA_EXTENSION_NAME}] Registration response: ${RESPONSE} with EXTENSION_ID  ${EXTENSION_ID}"

# Event processing
# Continuous loop to wait for events from Extensions API
while true
do
  echo "[${LAMBDA_EXTENSION_NAME}] Waiting for event. Get /next event from http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next"

  # Get an event. The HTTP request will block until one is received
  curl -sS -L -XGET "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next" --header "Lambda-Extension-Identifier: ${EXTENSION_ID}" > $TMPFILE &
  PID=$!
  forward_exit_and_wait

  EVENT_DATA=$(<$TMPFILE)
  if [[ $EVENT_DATA == *"INVOKE"* ]]; then
    echo "[extension: ${LAMBDA_EXTENSION_NAME}] Received INVOKE event."

    # Starting the Secrets Manager Agent
    # The agent is initialized AFTER the first Invoke phase, instead of right AFTER Init phase.
    # This prevents users from calling for secrets before the Init phase has finished.
    # Initializing the agent during Init would allow users to call and cache secrets before the snapshot phase (occurs last in Init), which in turn could store those values
    # in a snapshot of the sandbox for up to 14 days. https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
    # Implement retry logic in your application code, to accommodate delays in agent initialization.
    start_agent
  fi

  if [[ $EVENT_DATA == *"SHUTDOWN"* ]]; then
    echo "[extension: ${LAMBDA_EXTENSION_NAME}] Received SHUTDOWN event. Exiting."
    stop_agent
    exit 0 # Exit if we receive a SHUTDOWN event
  fi

done
