#!/bin/bash

# whatsdirty.sh - Check git repository status in subdirectories
# Shows which repositories have uncommitted changes and when they were last committed

# Configuration
CONFIG_FILE="$HOME/.config/whatsdirty.conf"
RED='\033[0;31m'
NC='\033[0m' # No Color

# === Configuration Management Functions ===

save_directory() {
    local dir="$1"
    mkdir -p "$(dirname "$CONFIG_FILE")"
    echo "$dir" > "$CONFIG_FILE"
}

load_directory() {
    if [ -f "$CONFIG_FILE" ]; then
        cat "$CONFIG_FILE"
    fi
}

validate_directory() {
    local dir="$1"
    # Use subshell to avoid changing current directory
    (cd "$dir" 2>/dev/null && pwd)
}

handle_directory_argument() {
    local arg="$1"
    
    if [ -z "$arg" ]; then
        # No argument - try to load from config
        local saved_dir
        saved_dir=$(load_directory)
        if [ -z "$saved_dir" ]; then
            echo "Error: No directory specified and no saved directory found." >&2
            echo "Usage: $0 <directory>" >&2
            echo "The specified directory will be saved for future use." >&2
            exit 1
        fi
        echo "$saved_dir"
    else
        # Argument provided - validate and save
        local canonical_dir
        canonical_dir=$(validate_directory "$arg")
        if [ -z "$canonical_dir" ]; then
            echo "Error: Directory '$arg' not found" >&2
            exit 1
        fi
        save_directory "$canonical_dir"
        echo "$canonical_dir"
    fi
}

# === Git Repository Functions ===

get_last_commit_hours() {
    local last_commit_timestamp
    last_commit_timestamp=$(git log -1 --format="%ct" 2>/dev/null)
    
    if [ -n "$last_commit_timestamp" ]; then
        local current_timestamp
        current_timestamp=$(date +%s)
        local diff_seconds=$((current_timestamp - last_commit_timestamp))
        
        # Convert to hours with decimal precision using awk (more portable than bc)
        local hours_ago
        hours_ago=$(awk "BEGIN {printf \"%.1f\", $diff_seconds / 3600}")
        
        # Ensure leading zero for values less than 1
        if [[ $hours_ago =~ ^\. ]]; then
            hours_ago="0$hours_ago"
        fi
        
        echo "$hours_ago"
    else
        echo ""
    fi
}

count_git_changes() {
    local staged
    staged=$(git diff --cached --numstat 2>/dev/null | wc -l)
    local unstaged
    unstaged=$(git diff --numstat 2>/dev/null | wc -l)
    local untracked
    untracked=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l)
    echo $((staged + unstaged + untracked))
}

get_changed_files_list() {
    local changed_files=""
    
    # Get staged files
    local 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
    local unstaged_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
    local untracked
    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
    if [ -n "$changed_files" ]; then
        echo -e "$changed_files" | sed '$ d'
    fi
}

analyze_repository() {
    local repo_path="$1"
    local repo_name
    repo_name=$(basename "$repo_path")
    
    cd "$repo_path" || return 1
    
    # Get commit info
    local hours_ago
    hours_ago=$(get_last_commit_hours)
    local hours_num
    local hours_label
    local hours_formatted
    
    if [ -n "$hours_ago" ]; then
        hours_num="$hours_ago"
        hours_label="hours"
        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 changes
    local total_changes
    total_changes=$(count_git_changes)
    
    # Determine status and collect data
    if [ "$total_changes" -eq 0 ]; then
        echo "C|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}"
    else
        local changed_files
        changed_files=$(get_changed_files_list)
        echo "D|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}|||${changed_files}"
    fi
}

# === Output Formatting Functions ===

print_header() {
    printf "%-40s %15s %-10s %-10s\n" "Repository" "Last Commit" "Changes" "Status"
    printf "%-40s %15s %-10s %-10s\n" "----------" "(hours)" "-------" "------"
}

print_dirty_repositories() {
    local temp_file="$1"
    
    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 _ _ changes repo hours_fmt _ file_list; do
            printf "%-40s %15s ${RED}%-10s${NC} ${RED}%-10s${NC}\n" "$repo" "$hours_fmt" "$changes" "Dirty"
            if [ -n "$file_list" ]; then
                echo -e "$file_list"
            fi
        done
    fi
}

print_clean_repositories() {
    local temp_file="$1"
    
    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 _ _ changes repo hours_fmt; do
            printf "%-40s %15s %-10s %-10s\n" "$repo" "$hours_fmt" "$changes" "Clean"
        done
    fi
}

# === Main Function ===

scan_repositories() {
    local scan_dir="$1"
    local temp_file="$2"
    
    for folder in "$scan_dir"/*; do
        [ ! -d "$folder" ] && continue
        
        if [ -d "$folder/.git" ]; then
            local repo_data
            repo_data=$(analyze_repository "$folder")
            if [ -n "$repo_data" ]; then
                echo "$repo_data" >> "$temp_file"
            fi
            cd "$scan_dir" || exit 1
        fi
    done
}

main() {
    # Handle directory argument
    local DIR
    DIR=$(handle_directory_argument "$1")
    
    # Validate directory still exists (for loaded configs)
    local validated_dir
    validated_dir=$(validate_directory "$DIR")
    if [ -z "$validated_dir" ]; then
        echo "Error: Saved directory '$DIR' no longer exists" >&2
        rm -f "$CONFIG_FILE"
        exit 1
    fi
    DIR="$validated_dir"
    
    # Create temp file for results
    local TEMP_FILE
    TEMP_FILE=$(mktemp)
    trap 'rm -f "$TEMP_FILE"' EXIT
    
    # Scan repositories
    scan_repositories "$DIR" "$TEMP_FILE"
    
    # Display results
    print_header
    print_dirty_repositories "$TEMP_FILE"
    print_clean_repositories "$TEMP_FILE"
}

# === Autocomplete Function ===

handle_autocomplete() {
    local cur_word="${COMP_WORDS[COMP_CWORD]}"
    
    # If we're completing the first argument after 'whatsdirty'
    if [ ${COMP_CWORD} -eq 1 ]; then
        # Suggest common directories and saved directory
        local saved_dir
        saved_dir=$(load_directory)
        local suggestions=""
        
        # Add saved directory if it exists
        if [ -n "$saved_dir" ] && [ -d "$saved_dir" ]; then
            suggestions="$saved_dir"
        fi
        
        # Add common development directories
        for dir in ~/code ~/projects ~/dev ~/src ~/workspace /home/*/code /home/*/projects; do
            if [ -d "$dir" ]; then
                # Only add if it contains git repositories
                if find "$dir" -maxdepth 2 -name ".git" -type d 2>/dev/null | head -1 | grep -q .; then
                    suggestions="$suggestions $dir"
                fi
            fi
        done
        
        # Directory completion for current word
        if [ -n "$cur_word" ]; then
            # Use mapfile for directory completion
            local dir_completions
            mapfile -t dir_completions < <(compgen -d -- "$cur_word")
            # Add our suggestions that match the current word
            local matching_suggestions
            mapfile -t matching_suggestions < <(echo "$suggestions" | tr ' ' '\n' | grep "^$cur_word" | sort -u)
            # Combine and deduplicate
            mapfile -t COMPREPLY < <(printf '%s\n' "${dir_completions[@]}" "${matching_suggestions[@]}" | sort -u)
        else
            # No current word, suggest all our directories plus general directory completion
            local dir_completions
            mapfile -t dir_completions < <(compgen -d)
            local all_suggestions
            mapfile -t all_suggestions < <(echo "$suggestions" | tr ' ' '\n' | sort -u)
            # Combine and deduplicate, but limit to reasonable number
            mapfile -t COMPREPLY < <(printf '%s\n' "${all_suggestions[@]}" "${dir_completions[@]}" | sort -u | head -20)
        fi
    fi
}

# Check if we're being called for autocomplete
if [ "$1" = "autocomplete" ]; then
    # This is called when the tool is being used for bash completion
    # The arguments after 'autocomplete' are the completion context
    shift
    
    # Set up COMP_WORDS and COMP_CWORD from arguments
    COMP_WORDS=("$@")
    COMP_CWORD=$(($# - 1))
    
    # Generate completions
    handle_autocomplete
    
    # Print completions, one per line
    printf '%s\n' "${COMPREPLY[@]}"
    exit 0
fi

# Check if we're being called to show version
if [ "$1" = "version" ] || [ "$1" = "--version" ] || [ "$1" = "-v" ] || [ "$1" = "v" ]; then
    echo "1.0.1"
    exit 0
fi

# Execute main function with all arguments
main "$@"