#!/bin/bash
set -euo pipefail # -e: exit on error, -u: exit on unset var, -o pipefail: catch errors in pipes

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
MAIN_APP_BARE_REPO="/srv/jasonpoage.com/expressjs-blog.git"

# Function to wait for a service to become available
wait_for_service() {
  local url="$1"
  local timeout=30 # seconds
  local start_time=$(date +%s)
  echo "Waiting for service at $url to become available (timeout: ${timeout}s)..."
  while :; do
    if curl --silent --fail "$url" >/dev/null; then
      echo "Service at $url is up!"
      return 0
    fi
    current_time=$(date +%s)
    if ((current_time - start_time >= timeout)); then
      echo "Error: Service at $url did not become available within ${timeout}s."
      return 1
    fi
    sleep 1
  done
}

deploy_expressjs_blog() {
  local oldrev="$1"
  local newrev="$2"
  local ref="$3"
  local custom_path="${4:-}" # If custom_path is provided, this is a test deployment
  local custom_env="${5:-}"
  local branch="${ref#refs/heads/}"

  # Define deployment path and env file path
  local path="${custom_path:-/srv/jasonpoage.com/expressjs-blog-$branch}"
  local envfile="${custom_env:-/srv/jasonpoage.com/$branch.env}"

  # set -x # Enable command tracing for debugging

  if [[ "$branch" == "production" || "$branch" == "main" || "$branch" == "testing" ]]; then

    # Ensure the deployment directory exists and is clean for a fresh clone
    if [[ -d "$path" ]]; then
      echo "Deployment path '$path' exists, removing for fresh clone..."
      rm -rf "$path"
    fi
    mkdir -p "$(dirname "$path")" # Ensure parent directory exists

    echo "Cloning main application from bare repo '$MAIN_APP_BARE_REPO' to '$path'..."

    git clone --branch "$branch" "$MAIN_APP_BARE_REPO" "$path" || {
      echo "Error: Failed to clone main application to $path"
      return 1
    }

    cd "$path" || {
      echo "Error: Could not change directory to $path"
      return 1
    }

    echo "Ensuring content submodule is initialized and updated."
    git --work-tree="$path" submodule update --init --remote --depth=1 --checkout --recursive || {
      echo "Error: Failed to update submodules."
      return 1
    }

    # Handle .env file
    ln -f "$envfile" "$path/.env"

    update_node_pkgs || return 1
    combine_css "$oldrev" "$newrev" || return 1

    # Restart service (only for actual deployments)
    [[ -z "$custom_path" ]] && systemctl --user restart express-blog@"$branch".service

  fi # End of branch check
  # set +x # Disable command tracing
}

update_node_pkgs() {
  # Yarn install logic
  local install_required=true
  if [[ -z "$custom_path" && -n "$oldrev" && "$oldrev" != "0000000000000000000000000000000000000000" ]]; then
    # This block only runs for actual deployments (not test runs)
    # Use --git-dir for this diff against the bare repo's commits
    if ! git --git-dir="$MAIN_APP_BARE_REPO" diff-tree --name-only -r "$oldrev..$newrev" | grep -qE "(package\.json|yarn\.lock)$"; then
      echo "No changes detected in package.json or yarn.lock. Skipping yarn install."
      install_required=false
    else
      echo "Changes detected in package.json or yarn.lock. Running yarn install."
    fi
  else
    echo "Running yarn install (initial deployment or test environment)."
  fi

  if [[ "$install_required" == "true" ]]; then
    yarn || {
      echo "Error: yarn install failed."
      return 1
    }
  fi
}

run_postreceive_tests() {
  local branch="$1"
  local tmpdir pidfile logfile
  tmpdir=$(mktemp -d)
  pidfile="$tmpdir/test.pid"
  logfile="/srv/jasonpoage.com/logs/receive-$TIMESTAMP.log"
  # Trap to ensure cleanup even if script exits unexpectedly
  trap "kill $(cat "$pidfile" 2>/dev/null) 2>/dev/null || true; rm -rf \"$tmpdir\"" EXIT

  echo "Running post-receive tests for branch '$branch' in temporary environment."

  # This creates a proper working tree at $tmpdir, which is essential for submodules.
  git clone --branch "$branch" "$GIT_DIR" "$tmpdir" || {
    echo "Error: Failed to clone main application for tests to $tmpdir"
    return 1
  }

  cd "$tmpdir" || {
    echo "Error: Could not change directory to $tmpdir"
    return 1
  }

  echo "Initializing and updating submodules for test environment..."
  git --work-tree="$tmpdir" submodule update --init --recursive || {
    ls "$tmpdir" -a
    echo "Error: Failed to initialize/update submodules for test environment."
    return 1
  }

  # Copy the .env file for the test environment
  cp "/srv/jasonpoage.com/${branch}.env" "$tmpdir/.env" || {
    echo "Error: Failed to copy .env file for test environment."
    return 1
  }

  export TEST_PORT=4123
  export NODE_ENV=testing

  echo "Starting application for tests..."
  yarn
  combine_css || return 1

  nohup yarn start >>"$logfile" 2>&1 &
  echo $! >"$pidfile"

  # Wait for the application to become responsive
  if ! wait_for_service "http://127.0.0.1:$TEST_PORT"; then
    echo "Application did not start or respond for tests. Check logs in $logfile:"
    cat "$logfile" # Display logs on failure
    return 1
  fi

  run_tests || return 1

  kill "$(cat "$pidfile")" 2>/dev/null || true
  echo "Tests passed for branch '$branch' in temporary environment."
  return 0
}

run_tests() {
  echo "Running npm tests..."
  if ! npm run test:postreceive; then
    kill "$(cat "$pidfile")" 2>/dev/null || true
    echo "Tests failed for branch '$branch'. Application logs from $logfile:"
    cat "$logfile"
    return 1
  fi
}

combine_css() {
  set +u
  oldrev="$1"
  newrev="$2"
  set -u

  # For live deployments, pass the oldrev, newrev, and MAIN_APP_BARE_REPO
  yarn --production=false combine:css "$oldrev" "$newrev" "$MAIN_APP_BARE_REPO" || {
    echo "Error: yarn combine:css failed."
    return 1
  }
}

# Main script execution loop
while read -r oldrev newrev ref; do
  branch="${ref#refs/heads/}"
  echo "--- Processing push for branch: $branch (from $oldrev to $newrev) ---"
  if run_postreceive_tests "$branch"; then
    echo "Tests passed for $branch. Proceeding with deployment."
    # Pass all original variables to deploy_expressjs_blog for the actual deployment
    deploy_expressjs_blog "$oldrev" "$newrev" "$ref" "" "" # Empty custom_path/env for real deploy
    echo "Deployment of $branch complete."
  else
    echo "Post-receive tests failed for $branch. Deployment aborted."
    exit 1
  fi
done
