#! /bin/bash

# Run a few quick pre-commit hooks to ensure the code is in a good state before committing.

# Fail on errors
set -euo pipefail

# Unset git environment variables that might interfere with git commands
# Git sets GIT_DIR when running hooks, which can cause git rev-parse to return
# incorrect paths when called from subdirectories (especially in worktrees)
unset GIT_DIR
unset GIT_WORK_TREE

repo_root=$(git rev-parse --show-toplevel)
current_dir=$(pwd)

if [ -z "$repo_root" ]; then
    echo "Not a git repository"
    exit 1
fi

# Change to repo root to ensure git commands work correctly
cd "$repo_root"

# Check if any files are partially staged (some changes staged, some not)
# We'll compare the diff between working tree and index vs HEAD and index
partially_staged_files=()
while IFS= read -r file; do
    if [ -n "$file" ]; then
        # Check if file has both staged and unstaged changes
        if git diff --quiet HEAD -- "$file" 2>/dev/null; then
            # File is the same as HEAD, skip
            continue
        fi
        if git diff --quiet -- "$file" 2>/dev/null; then
            # No unstaged changes, file is fully staged
            continue
        fi
        if git diff --cached --quiet -- "$file" 2>/dev/null; then
            # No staged changes, file is fully unstaged
            continue
        fi
        # File has both staged and unstaged changes
        partially_staged_files+=("$file")
    fi
done < <(git diff --name-only HEAD)

# Get list of files that are staged
staged_files=()
while IFS= read -r file; do
    if [ -n "$file" ]; then
        staged_files+=("$file")
    fi
done < <(git diff --cached --name-only)

# Capture checksums of partially staged files before linting
checksum_file_before=$(mktemp)
if [ ${#partially_staged_files[@]} -gt 0 ]; then
    for file in "${partially_staged_files[@]}"; do
        if [ -f "$file" ]; then
            shasum "$file" 2>/dev/null >> "$checksum_file_before"
        fi
    done
fi

# Run lint and sync
./cmd.sh lint --diff
./cmd.sh sync:dependencies

# Check if lint/sync modified any partially staged files
if [ ${#partially_staged_files[@]} -gt 0 ]; then
    checksum_file_after=$(mktemp)
    for file in "${partially_staged_files[@]}"; do
        if [ -f "$file" ]; then
            shasum "$file" 2>/dev/null >> "$checksum_file_after"
        fi
    done

    # Compare checksums to find modified files
    # Since we iterate over the same files in the same order, we can compare line by line
    modified_partially_staged_files=()

    # Read both files simultaneously
    line_num=0
    while IFS= read -r line_before <&3 && IFS= read -r line_after <&4; do
        line_num=$((line_num + 1))

        if [ -z "$line_before" ]; then
            continue
        fi

        # If the lines differ, the file was modified
        if [ "$line_before" != "$line_after" ]; then
            # Extract filename from the before line (everything after the first two spaces)
            file_name=$(echo "$line_before" | sed 's/^[^ ]*  //')
            modified_partially_staged_files+=("$file_name")
        fi
    done 3< "$checksum_file_before" 4< "$checksum_file_after"

    rm -f "$checksum_file_after"

    if [ ${#modified_partially_staged_files[@]} -gt 0 ]; then
        echo "❌ Error: Linting modified partially staged files. This is not supported."
        echo "Partially staged files that were modified:"
        for file in "${modified_partially_staged_files[@]}"; do
            echo "  - $file"
        done
        echo ""
        echo "Please either:"
        echo "  1. Stage all changes: git add <files>"
        echo "  2. Unstage all changes: git restore --staged <files>"
        echo "  3. Commit without partially staged files"
        rm -f "$checksum_file_before"
        exit 1
    fi
fi

rm -f "$checksum_file_before"

# Check if lint/sync made any changes
if ! git diff --quiet || ! git diff --cached --quiet; then
    # Stage only the files that were already staged, but skip partially staged files
    # to avoid pulling in their unstaged changes
    for file in "${staged_files[@]}"; do
        # Check if this file is in the partially_staged_files array
        is_partially_staged=false
        if [ ${#partially_staged_files[@]} -gt 0 ]; then
            for partial_file in "${partially_staged_files[@]}"; do
                if [ "$file" = "$partial_file" ]; then
                    is_partially_staged=true
                    break
                fi
            done
        fi

        # Only restage if it's not partially staged
        if [ "$is_partially_staged" = false ]; then
            git add -- "$file" 2>/dev/null || true
        fi
    done
fi

cd "$current_dir"

echo "✓ Pre-commit hooks completed."
