#!/usr/bin/env sh
# Copyright (c) 2025 Hangzhou Guanwaii Technology Co., Ltd.
#
# This source code is licensed under the MIT License,
# which is located in the LICENSE file in the source tree's root directory.
#
# File: prepare-commit-msg
# Author: mingcheng <mingcheng@apache.org>
# File Created: 2025-11-07 12:22:17
#
# Modified By: mingcheng <mingcheng@apache.org>
# Last Modified: 2025-11-07 12:40:56
##

# Git hook: prepare-commit-msg
# Automatically generates commit messages using aigitcommit.
# Usage: Place this script in `.git/hooks/` and make it executable (chmod +x).

set -e

# ============================================================================
# Configuration and initialization
# ============================================================================

COMMIT_MSG_FILE=$1
COMMIT_MSG_TYPE=$2
COMMIT_SOURCE=$3

# Get repository root
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -z "$REPO_ROOT" ] || [ ! -d "$REPO_ROOT" ]; then
    echo "Error: Not in a valid Git repository."
    exit 1
fi

# ============================================================================
# Helper functions
# ============================================================================

# Check if aigitcommit is installed
check_aigitcommit() {
    if ! command -v aigitcommit >/dev/null 2>&1; then
        echo "Error: aigitcommit is not installed."
        echo "Please install it first: https://github.com/mingcheng/aigitcommit"
        exit 0
    fi
}

# Check if there are staged changes
has_staged_changes() {
    git diff --cached --quiet
    return $?
}

# Check if commit message file is effectively empty (only whitespace/comments)
is_message_empty() {
    ! grep -Eq '^[[:space:]]*[^#[:space:]]' "$COMMIT_MSG_FILE"
}

# Get the appropriate editor
get_editor() {
    local editor=""

    # Try git's configured editor first
    if editor=$(git var GIT_EDITOR 2>/dev/null); then
        if [ -n "$editor" ] && [ "$editor" != ":" ]; then
            printf '%s' "$editor"
            return 0
        fi
    fi

    # Fallback to environment variables
    if [ -n "$VISUAL" ]; then
        printf '%s' "$VISUAL"
        return 0
    fi

    if [ -n "$EDITOR" ]; then
        printf '%s' "$EDITOR"
        return 0
    fi

    # Last resort fallback
    printf '%s' "vi"
    return 0
}

# Generate commit message using aigitcommit
generate_commit_message() {
    local temp_file
    temp_file=$(mktemp)

    echo "🚀 Generating commit message using aigitcommit..."
    echo "This may take a few seconds..."

    if ! aigitcommit "$REPO_ROOT" --save "$temp_file" >/dev/null 2>&1; then
        echo "Error: aigitcommit failed to generate commit message."
        rm -f "$temp_file"
        exit 1
    fi

    # Preserve existing content and append to generated message
    cat "$COMMIT_MSG_FILE" >>"$temp_file"
    mv -f "$temp_file" "$COMMIT_MSG_FILE"
}

# ============================================================================
# Main logic
# ============================================================================

check_aigitcommit

# Determine if we should run aigitcommit and possibly open editor
SHOULD_RUN_AIGITCOMMIT=0
SHOULD_OPEN_EDITOR=0

case "$COMMIT_MSG_TYPE" in
    message)
        # git commit -m "..." was used
        if is_message_empty; then
            # git commit -m "" (empty message)
            # Generate message AND open editor for user to review/edit
            SHOULD_RUN_AIGITCOMMIT=1
            SHOULD_OPEN_EDITOR=1
        else
            # git commit -m "actual message"
            # Don't interfere with explicit non-empty messages
            exit 0
        fi
        ;;
    template|merge|squash|commit)
        # Don't run for: merge commits, squash commits, commit --amend, or template
        exit 0
        ;;
    "")
        # Plain git commit (interactive mode) - always run
        SHOULD_RUN_AIGITCOMMIT=1
        # Editor will be opened by git itself, no need to explicitly open it
        SHOULD_OPEN_EDITOR=0
        ;;
    *)
        # Unknown commit type, skip
        exit 0
        ;;
esac

# Verify there are staged changes
if has_staged_changes; then
    echo "No staged changes detected. Aborting commit."
    exit 1
fi

# Generate commit message if needed
if [ "$SHOULD_RUN_AIGITCOMMIT" -eq 1 ]; then
    generate_commit_message
fi

# Open editor manually if needed (for git commit -m "")
if [ "$SHOULD_OPEN_EDITOR" -eq 1 ]; then
    editor_cmd=$(get_editor)

    # Debug output (can be removed in production)
    # echo "DEBUG: Editor command: [$editor_cmd]" >&2

    if [ -z "$editor_cmd" ] || [ "$editor_cmd" = ":" ]; then
        echo "Error: Could not determine editor to use."
        exit 1
    fi

    # Use eval to properly handle editors with arguments (e.g., "code --wait")
    eval "$editor_cmd" '"$COMMIT_MSG_FILE"' || {
        echo "Error: Failed to open editor: $editor_cmd"
        exit 1
    }
fi

exit 0
