#!/bin/bash

# iterate through all subfolders in the current directory (or $1 if provided),
# and check:
# is it a git rep?
# if so is it clean (nothing to commit)?
#
# for each git repo print the name of the repo, the datestamp of the last commit, 
# the nubmer of changes since then, and whether it's clean or not.

# if no argument is provided, use the current directory
DIR="${1:-.}"

# make directory canonical
DIR=$(cd "$DIR" 2>/dev/null && pwd) || { echo "Error: Directory '$1' not found"; exit 1; }

# Color codes
RED='\033[0;31m'
NC='\033[0m' # No Color

# Temporary file to store repository data
TEMP_FILE=$(mktemp)
# shellcheck disable=SC2064
trap "rm -f $TEMP_FILE" EXIT

# iterate through all subfolders in DIR
for folder in "$DIR"/*; do
    # Skip if not a directory
    [ ! -d "$folder" ] && continue
    
    # Check if it's a git repository
    if [ -d "$folder/.git" ]; then
        # Get the folder name
        repo_name=$(basename "$folder")
        
        # Change to the repository directory
        cd "$folder" || continue
        
        # Get the last commit timestamp in seconds since epoch
        last_commit_timestamp=$(git log -1 --format="%ct" 2>/dev/null)
        
        if [ -n "$last_commit_timestamp" ]; then
            # Get current timestamp
            current_timestamp=$(date +%s)
            
            # Calculate difference in seconds
            diff_seconds=$((current_timestamp - last_commit_timestamp))
            
            # Convert to hours with decimal precision
            # Using bc for floating point arithmetic
            hours_ago=$(echo "scale=1; $diff_seconds / 3600" | bc)
            
            # Ensure leading zero for values less than 1
            if [[ $hours_ago =~ ^\. ]]; then
                hours_ago="0$hours_ago"
            fi
            
            # Format the hours number and "hours" label separately
            hours_num="$hours_ago"
            hours_label="hours"
            # Combine with right-justified formatting
            hours_formatted=$(printf "%8s %-6s" "$hours_num" "$hours_label")
        else
            hours_num="999999"  # Large number for sorting
            hours_label="commits"
            hours_formatted=$(printf "%8s %-7s" "No" "$hours_label")
        fi
        
        # Count uncommitted changes (staged and unstaged)
        # Count modified, deleted, and untracked files
        staged_changes=$(git diff --cached --numstat 2>/dev/null | wc -l)
        unstaged_changes=$(git diff --numstat 2>/dev/null | wc -l)
        untracked_files=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l)
        total_changes=$((staged_changes + unstaged_changes + untracked_files))
        
        # Store repo data with status prefix (D for dirty, C for clean)
        if [ $total_changes -eq 0 ]; then
            echo "C|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}" >> "$TEMP_FILE"
        else
            # For dirty repos, collect the list of changed files
            changed_files=""
            
            # Get staged files
            staged_files=$(git diff --cached --name-only 2>/dev/null)
            if [ -n "$staged_files" ]; then
                while IFS= read -r file; do
                    changed_files="${changed_files}    [staged] $file\n"
                done <<< "$staged_files"
            fi
            
            # Get unstaged modified files
            unstaged_files=$(git diff --name-only 2>/dev/null)
            if [ -n "$unstaged_files" ]; then
                while IFS= read -r file; do
                    changed_files="${changed_files}    [modified] $file\n"
                done <<< "$unstaged_files"
            fi
            
            # Get untracked files
            untracked=$(git ls-files --others --exclude-standard 2>/dev/null)
            if [ -n "$untracked" ]; then
                while IFS= read -r file; do
                    changed_files="${changed_files}    [untracked] $file\n"
                done <<< "$untracked"
            fi
            
            # Remove trailing newline
            changed_files=$(echo -e "$changed_files" | sed '$ d')
            
            # Store with a special delimiter (|||) to separate file list
            echo "D|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}|||${changed_files}" >> "$TEMP_FILE"
        fi
        
        # Return to the original directory
        cd "$DIR" || exit 1
    fi
done

# Print header
printf "%-40s %15s %-10s %-10s\n" "Repository" "Last Commit" "Changes" "Status"
printf "%-40s %15s %-10s %-10s\n" "----------" "(hours)" "-------" "------"

# Print dirty repositories first (sorted by changes, descending)
if grep -q "^D|" "$TEMP_FILE" 2>/dev/null; then
    printf "\n=== DIRTY REPOSITORIES ===\n\n"
    grep "^D|" "$TEMP_FILE" | sort -t'|' -k3,3nr | while IFS='|' read -r status hours changes repo hours_fmt separator file_list; do
        printf "%-40s %15s ${RED}%-10s${NC} ${RED}%-10s${NC}\n" "$repo" "$hours_fmt" "$changes" "Dirty"
        # Print the changed files if they exist
        if [ -n "$file_list" ]; then
            echo -e "$file_list"
        fi
    done
fi

# Print clean repositories (sorted by hours, ascending)
if grep -q "^C|" "$TEMP_FILE" 2>/dev/null; then
    printf "\n=== CLEAN REPOSITORIES ===\n\n"
    grep "^C|" "$TEMP_FILE" | sort -t'|' -k2,2n | while IFS='|' read -r status hours changes repo hours_fmt; do
        printf "%-40s %15s %-10s %-10s\n" "$repo" "$hours_fmt" "$changes" "Clean"
    done
fi