#!/usr/bin/env bash

# This script checks if a commit message follows the specified format.
# It's designed to be used as a 'commit-msg' git hook.

# Format:
# <type>: <short description>
#
# [optional]<body>
#
# [ref/close]: <issue identifier>

# --- Configuration ---
# An array of allowed commit types
ALLOWED_TYPES=(feat fix refactor perf style test docs build chore revert)

# --- Colors for Output ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# The first argument to the hook is the path to the file containing the commit message
COMMIT_MSG_FILE=$1

# --- Automated Commit Detection ---

# Read the first line (header) for initial checks
HEADER=$(head -n 1 "$COMMIT_MSG_FILE")

# Check for Merge commits (covers 'git merge' and PR merges from GitHub/GitLab)
# Examples: "Merge branch 'main' into ...", "Merge pull request #123 from ..."
MERGE_PATTERN="^Merge (remote-tracking )?(branch|pull request|tag) .*"
if [[ "$HEADER" =~ $MERGE_PATTERN ]]; then
  echo -e "${GREEN}Merge commit detected by message content. Skipping validation.${NC}"
  exit 0
fi

# Check for Revert commits
# Example: "Revert "feat: add new feature""
REVERT_PATTERN="^Revert \".*\""
if [[ "$HEADER" =~ $REVERT_PATTERN ]]; then
  echo -e "${GREEN}Revert commit detected by message content. Skipping validation.${NC}"
  exit 0
fi

# Check for Cherry-pick commits (this pattern appears at the end of the message)
# Example: "(cherry picked from commit deadbeef...)"
# We use grep -q to search the whole file quietly.
CHERRY_PICK_PATTERN="\(cherry picked from commit [a-f0-9]{7,40}\)"
if grep -qE "$CHERRY_PICK_PATTERN" "$COMMIT_MSG_FILE"; then
  echo -e "${GREEN}Cherry-pick detected by message content. Skipping validation.${NC}"
  exit 0
fi

# Check for Squash
# Example: "Squash commits ..."
SQUASH_PATTERN="^Squash .+"
if [[ "$HEADER" =~ $SQUASH_PATTERN ]]; then
  echo -e "${GREEN}Squash commit detected by message content. Skipping validation.${NC}"
  exit 0
fi

# --- Validation Functions ---

# Function to print an error message and exit
# Usage: error_exit "Your error message here"
error_exit() {
  # >&2 redirects echo to stderr
  echo -e "${RED}ERROR: $1${NC}" >&2
  echo -e "${YELLOW}Commit message format is incorrect. Aborting commit.${NC}" >&2
  exit 1
}

# --- Main Logic ---

# 1. Read the header (first line) of the commit message
HEADER=$(head -n 1 "$COMMIT_MSG_FILE")

# 2. Validate the header format: <type>: <description>
# Regex breakdown:
# ^(type1|type2|...)  - Starts with one of the allowed types
# :                    - Followed by a literal colon
# \s                   - Followed by a single space
# .+                   - Followed by one or more characters for the description
# $                    - End of the line
TYPES_REGEX=$(
  IFS="|"
  echo "${ALLOWED_TYPES[*]}"
)
HEADER_REGEX="^($TYPES_REGEX): .+$"

if ! [[ "$HEADER" =~ $HEADER_REGEX ]]; then
  error_exit "Invalid header format.\n\nHeader must be in the format: <type>: <short description>\nAllowed types: ${ALLOWED_TYPES[*]}\nExample: feat: add new user authentication feature"
fi

# Only validate footer if commit type is not chore
TYPE=$(echo "$HEADER" | cut -d':' -f1)
if [ "$TYPE" != "chore" ]; then
  # 3. Validate the footer (last line) of the commit message
  FOOTER=$(tail -n 1 "$COMMIT_MSG_FILE")

  # Regex breakdown:
  # ^(ref|close)         - Starts with 'ref' or 'close'
  # :                    - Followed by a literal colon
  # \s                   - Followed by a single space
  # N25B-                - Followed by the literal string 'N25B-'
  # [0-9]+               - Followed by one or more digits
  # $                    - End of the line
  FOOTER_REGEX="^(ref|close): N25B-[0-9]+$"

  if ! [[ "$FOOTER" =~ $FOOTER_REGEX ]]; then
    error_exit "Invalid footer format.\n\nFooter must be in the format: [ref/close]: <issue identifier>\nExample: ref: N25B-123"
  fi
fi

# 4. If the message has more than 2 lines, validate the separator
#    A blank line must exist between the header and the body.
LINE_COUNT=$(wc -l <"$COMMIT_MSG_FILE" | xargs) # xargs trims whitespace

# We only care if there is a body. Header + Footer = 2 lines.
# Header + Blank Line + Body... + Footer > 2 lines.
if [ "$LINE_COUNT" -gt 2 ]; then
  # Get the second line
  SECOND_LINE=$(sed -n '2p' "$COMMIT_MSG_FILE")

  # Check if the second line is NOT empty. If it's not, it's an error.
  if [ -n "$SECOND_LINE" ]; then
    error_exit "Missing blank line between header and body.\n\nThe second line of your commit message must be empty if a body is present."
  fi
fi

# If all checks pass, exit with success
echo -e "${GREEN}Commit message is valid.${NC}"
exit 0
