#!/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)

get_deploy_env_file() {
  branch="$1"
  echo "/srv/jasonpoage.com/${branch}.env"
}

get_deploy_path() {
  branch="$1"
  echo "/srv/jasonpoage.com/expressjs-blog-${branch}"
}

get_log_file_path() {
  echo "/srv/jasonpoage.com/logs/post-receive-${TIMESTAMP}.log"
}

quick_deploy_expressjs_blog() {
    local branch="$1"
    local deploy_path="$2"
    local envfile="$3"
    echo "Performing quick deployment for '$branch' to '$deploy_path'..."
    
    # Remove old deployment and create fresh one
    [[ -d "$deploy_path" ]] && rm -rf "$deploy_path"
    
    # Clone the latest version
    git clone --branch "$branch" "$GIT_DIR" "$deploy_path" || {
        echo "Error: Failed to clone branch $branch to $deploy_path"
        return 1
    }
    
    cd "$deploy_path" || {
        echo "Error: Could not change directory to $deploy_path"
        return 1
    }
    
    # Update submodules
    git submodule update --init --recursive || {
        echo "Error: Failed to initialize submodules"
        return 1
    }
    
    # Set up environment file
    ln -f "$envfile" "$deploy_path/.env"
    
    # Install dependencies and build
    yarn
    yarn --production=false combine:css || {
        echo "Error: CSS combination failed"
        return 1
    }

    # Restart the service
    systemctl --user restart express-blog@"$branch".service
    
    echo "Quick deployment complete for branch '$branch'"
}

# Smart dependency installation (only if package files changed)
get_dependencies() {
  local oldrev="$1"
  local newrev="$2"
  local install_required=true
  
  if [[ -n "$oldrev" && "$oldrev" != "0000000000000000000000000000000000000000" ]]; then
      if ! git --git-dir="$GIT_DIR" 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)."
  fi

  if [[ "$install_required" == "true" ]]; then
      yarn
  fi
}

# Single function to handle all repo and submodule updates (force-push safe)
update_repo_and_submodules() {
    local branch="$1"
    local deploy_path="$2"
    
    echo "Updating repository and submodules (force-push safe)..."
    
    cd "$deploy_path" || {
        echo "Error: Could not change directory to $deploy_path"
        return 1
    }
    
    SAVED_GIT_DIR="$GIT_DIR"
    unset GIT_DIR

    # Update main repository - always fetch and hard reset (handles force pushes)
    echo "Fetching and updating main repository..."
    git fetch origin || {
        echo "Error: Failed to fetch from origin"
        return 1
    }
    
    git reset --hard "origin/$branch" || {
        echo "Error: Failed to reset to origin/$branch"
        return 1
    }
    
    # Update submodules - handle force pushes by resetting each submodule
    echo "Updating submodules..."
    git submodule update --init --recursive --force || {
      echo "Standard submodule update failed, cleaning and retrying..."
      return 1
    }
    
    echo "Repository and submodules updated successfully"

    export GIT_DIR="$SAVED_GIT_DIR"
    unset SAVED_GIT_DIR
}

# Conservative deployment function for production environments
conservative_deploy_expressjs_blog() {
    local branch="$1"
    local oldrev="${2:-}"
    local newrev="${3:-}"
    local deploy_path="${4:-}"
    local envfile="${5:-}"

    echo "Using conservative deployment method for production environment..."

    # Initialize the deployment directory if it doesn't exist
    if [[ ! -d "$deploy_path" ]]; then
        echo "Creating deployment directory..."
        git clone --branch "$branch" "$GIT_DIR" "$deploy_path" || {
            echo "Error: Failed to create deployment directory"
            return 1
        }
    fi

    # Update repository and submodules
    update_repo_and_submodules "$branch" "$deploy_path" || return 1
    
    cd "$deploy_path" || {
        echo "Error: Could not change directory to $deploy_path"
        return 1
    }
    
    # Set up environment file
    ln -f "$envfile" "$deploy_path/.env" || return 1

    # Install dependencies if needed
    get_dependencies "$oldrev" "$newrev" || return 1

    # Build CSS
    yarn combine:css "$oldrev" "$newrev" "$GIT_DIR" || return 1

    # Restart service
    systemctl --user restart express-blog@"$branch".service

    echo "Conservative deployment complete for branch '$branch'"
}

deploy_expressjs_blog() {
    local branch="$1"
    local oldrev="${2:-}"
    local newrev="${3:-}"

    # Define deployment path and env file path
    local deploy_path envfile
    deploy_path=$(get_deploy_path "$branch")
    envfile=$(get_deploy_env_file "$branch")

    case "$branch" in
      "testing")
          quick_deploy_expressjs_blog "$branch" "$deploy_path" "$envfile"
          ;;
      "staging"|"main")
          # For staging/production, use the preserving method (keep logs, update in place)
          conservative_deploy_expressjs_blog "$branch" "$oldrev" "$newrev" "$deploy_path" "$envfile"
          ;;
      *)
          echo "Branch '$branch' not configured for deployment. Skipping."
          return 0
          ;;
    esac
}

# Post-receive hook: Deploy the changes that were just pushed
while read -r oldrev newrev ref; do
  branch="${ref#refs/heads/}"
  
  echo "--- Post-receive deployment for branch: $branch (from $oldrev to $newrev) ---"
  
  # Only deploy branches we care about
  case "$branch" in
    "testing"|"staging"|"main")
      if deploy_expressjs_blog "$branch" "$oldrev" "$newrev"; then
        echo "Deployment of $branch complete."
      else
        echo "Deployment of $branch failed."
        # Note: We don't exit 1 here because the push has already been accepted
        # Log the failure but continue processing other branches
      fi
      ;;
    *)
      echo "Branch '$branch' not configured for deployment. Skipping."
      ;;
  esac
done

echo "Post-receive processing complete."
