#!/usr/bin/env bash
#
# urgency action for todo.txt-cli
# 
# Calculates urgency scores for tasks based on Taskwarrior's urgency algorithm
# Adapted for todo.txt format with configurable coefficients
#

action=$1
shift

# Urgency coefficients (based on Taskwarrior defaults)
readonly COEFF_PRIORITY_A=6.0
readonly COEFF_PRIORITY_B=3.9
readonly COEFF_PRIORITY_C=1.8
readonly COEFF_DUE=12.0
readonly COEFF_AGE=2.0
readonly COEFF_PROJECT=1.0
readonly COEFF_NEXT=15.0
readonly COEFF_WAITING=-3.0
readonly COEFF_BLOCKED=-5.0

# Time horizons (days)
readonly DUE_HORIZON=14
readonly AGE_HORIZON=365

function usage() {
    echo "  $(basename $0) [subcommand] [options]"
    echo "    Calculate and display urgency scores for tasks."
    echo "    Based on Taskwarrior's urgency algorithm, adapted for todo.txt."
    echo ""
    echo "  Subcommands:"
    echo "    list (default)    List all tasks with urgency scores, sorted by urgency"
    echo "    next              Show highest-urgency non-waiting task"
    echo "    top -n N          Show top N tasks"
    echo "    raw               Output id|score|task for scripting"
    echo ""
    echo "  Options:"
    echo "    -n, --top N       Show top N tasks"
    echo "    --min X           Only show tasks with urgency >= X"
    echo "    --include-wait    Include waiting/thresholded tasks in next/list"
    echo ""
    echo "  Examples:"
    echo "    $TODO_SH $(basename $0)           # List all tasks with urgency scores"
    echo "    $TODO_SH $(basename $0) next      # Show next highest-urgency task"
    echo "    $TODO_SH $(basename $0) top -n 5  # Show top 5 tasks"
    echo "    $TODO_SH $(basename $0) --min 10  # Show only tasks with urgency >= 10"
    echo ""
    echo "  Urgency factors:"
    echo "    Priority: (A)=$COEFF_PRIORITY_A, (B)=$COEFF_PRIORITY_B, (C)=$COEFF_PRIORITY_C"
    echo "    Due dates: $COEFF_DUE (overdue=max, within ${DUE_HORIZON}d=scaled)"
    echo "    +next tag: $COEFF_NEXT"
    echo "    Projects: $COEFF_PROJECT per +project"
    echo "    Age: $COEFF_AGE (max at ${AGE_HORIZON}d)"
    echo "    Waiting: $COEFF_WAITING (wait: or t: in future)"
    echo "    Blocked: $COEFF_BLOCKED (+blocked tag)"
    echo ""
    exit
}

# Handle the special case of "usage"
[ "$action" = "usage" ] && usage

# Date utility functions using Python 3 for cross-platform compatibility
function today_epoch_days() {
    python3 - <<'PY'
import datetime
today = datetime.date.today()
epoch = datetime.date(1970, 1, 1)
print((today - epoch).days)
PY
}

function iso_to_epoch_days() {
    local iso_date="$1"
    python3 - <<PY
import datetime
try:
    date = datetime.datetime.strptime('$iso_date', '%Y-%m-%d').date()
    epoch = datetime.date(1970, 1, 1)
    print((date - epoch).days)
except:
    print(0)
PY
}

function days_until() {
    local iso_date="$1"
    local today_days=$(today_epoch_days)
    local target_days=$(iso_to_epoch_days "$iso_date")
    echo $((target_days - today_days))
}

function days_since() {
    local iso_date="$1"
    local today_days=$(today_epoch_days)
    local target_days=$(iso_to_epoch_days "$iso_date")
    echo $((today_days - target_days))
}

# Parse a task line and extract urgency factors
function parse_task() {
    local line="$1"
    local id task_text priority created_date due_date wait_date threshold_date
    local projects next_tag blocked_tag
    
    # Extract task ID and text
    id=$(echo "$line" | awk '{print $1}')
    task_text=$(echo "$line" | sed 's/^[0-9]* //')
    
    # Extract priority
    if [[ $task_text =~ ^\([A-Z]\) ]]; then
        priority=$(echo "$task_text" | grep -o '^([A-Z])' | tr -d '()')
    fi
    
    # Extract created date (after priority or at start)
    if [[ $task_text =~ [0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
        created_date=$(echo "$task_text" | grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' | head -1)
    fi
    
    # Extract due date
    if [[ $task_text =~ due:[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
        due_date=$(echo "$task_text" | grep -o 'due:[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' | cut -d: -f2)
    fi
    
    # Extract wait date
    if [[ $task_text =~ wait:[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
        wait_date=$(echo "$task_text" | grep -o 'wait:[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' | cut -d: -f2)
    fi
    
    # Extract threshold date
    if [[ $task_text =~ t:[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
        threshold_date=$(echo "$task_text" | grep -o 't:[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' | cut -d: -f2)
    fi
    
    # Extract projects
    projects=$(echo "$task_text" | grep -o '+[A-Za-z0-9._-]*' | wc -l)
    
    # Check for +next tag
    if [[ $task_text =~ \+next ]]; then
        next_tag=1
    else
        next_tag=0
    fi
    
    # Check for +blocked tag
    if [[ $task_text =~ \+blocked ]]; then
        blocked_tag=1
    else
        blocked_tag=0
    fi
    
    # Output parsed fields (pipe-separated for easy processing)
    echo "$id|$task_text|$priority|$created_date|$due_date|$wait_date|$threshold_date|$projects|$next_tag|$blocked_tag"
}

# Calculate urgency score for a task
function calculate_urgency() {
    local parsed="$1"
    local id task_text priority created_date due_date wait_date threshold_date projects next_tag blocked_tag
    
    IFS='|' read -r id task_text priority created_date due_date wait_date threshold_date projects next_tag blocked_tag <<< "$parsed"
    
    local urgency=0
    local is_waiting=0
    
    # Priority factor
    case "$priority" in
        A) urgency=$(echo "$urgency + $COEFF_PRIORITY_A" | bc -l) ;;
        B) urgency=$(echo "$urgency + $COEFF_PRIORITY_B" | bc -l) ;;
        C) urgency=$(echo "$urgency + $COEFF_PRIORITY_C" | bc -l) ;;
    esac
    
    # Due date factor
    if [[ -n "$due_date" ]]; then
        local days_until_due=$(days_until "$due_date")
        if (( days_until_due < 0 )); then
            # Overdue
            urgency=$(echo "$urgency + $COEFF_DUE" | bc -l)
        elif (( days_until_due <= DUE_HORIZON )); then
            # Due soon (scaled)
            local due_factor=$(echo "scale=4; (1 - ($days_until_due / $DUE_HORIZON)) * $COEFF_DUE" | bc -l)
            urgency=$(echo "$urgency + $due_factor" | bc -l)
        fi
    fi
    
    # Age factor
    if [[ -n "$created_date" ]]; then
        local age_days=$(days_since "$created_date")
        if (( age_days > 0 )); then
            local age_factor=$(echo "scale=4; ($age_days / $AGE_HORIZON) * $COEFF_AGE" | bc -l)
            # Cap at coefficient value
            age_factor=$(echo "if ($age_factor > $COEFF_AGE) $COEFF_AGE else $age_factor" | bc -l)
            urgency=$(echo "$urgency + $age_factor" | bc -l)
        fi
    fi
    
    # Project factor
    if (( projects > 0 )); then
        urgency=$(echo "$urgency + $COEFF_PROJECT" | bc -l)
    fi
    
    # +next tag factor
    if (( next_tag == 1 )); then
        urgency=$(echo "$urgency + $COEFF_NEXT" | bc -l)
    fi
    
    # Waiting factor (negative)
    if [[ -n "$wait_date" ]]; then
        local days_until_wait=$(days_until "$wait_date")
        if (( days_until_wait > 0 )); then
            urgency=$(echo "$urgency + $COEFF_WAITING" | bc -l)
            is_waiting=1
        fi
    fi
    
    if [[ -n "$threshold_date" ]]; then
        local days_until_threshold=$(days_until "$threshold_date")
        if (( days_until_threshold > 0 )); then
            urgency=$(echo "$urgency + $COEFF_WAITING" | bc -l)
            is_waiting=1
        fi
    fi
    
    # Blocked factor (negative)
    if (( blocked_tag == 1 )); then
        urgency=$(echo "$urgency + $COEFF_BLOCKED" | bc -l)
    fi
    
    # Output: id|urgency|is_waiting|task_text
    printf "%s|%.1f|%s|%s\n" "$id" "$urgency" "$is_waiting" "$task_text"
}

# Main processing function
function process_tasks() {
    local include_waiting="$1"
    local min_urgency="$2"
    local limit="$3"
    local output_format="$4"  # "list", "raw", "next"
    
    local temp_file=$(mktemp)
    local count=0
    
    # Get tasks from todo.txt and process them
    "$TODO_FULL_SH" -x list | while read -r line; do
        if [[ -n "$line" ]]; then
            parsed=$(parse_task "$line")
            scored=$(calculate_urgency "$parsed")
            echo "$scored" >> "$temp_file"
        fi
    done
    
    # Sort by urgency (descending), then by ID (ascending)
    # Process the sorted results
    while IFS='|' read -r id urgency is_waiting task_text; do
        # Filter by waiting status
        if [[ "$include_waiting" != "1" && "$is_waiting" == "1" ]]; then
            continue
        fi
        
        # Filter by minimum urgency
        if [[ -n "$min_urgency" ]]; then
            if (( $(echo "$urgency < $min_urgency" | bc -l) )); then
                continue
            fi
        fi
        
        # Apply limit
        if [[ -n "$limit" ]]; then
            if (( count >= limit )); then
                break
            fi
        fi
        
        # Output in requested format
        case "$output_format" in
            "raw")
                echo "$id|$urgency|$task_text"
                ;;
            "next"|"list")
                printf "%s %s  [u:%.1f]\n" "$id" "$task_text" "$urgency"
                ;;
        esac
        
        ((count++))
    done < <(sort -t'|' -k2,2nr -k1,1n "$temp_file")
    
    rm -f "$temp_file"
}

# Command line parsing

include_waiting=0
min_urgency=""
limit=""
output_format="list"
subcommand="list"

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        next)
            subcommand="next"
            if [[ "$output_format" == "list" ]]; then
                output_format="next"
                limit=1
            fi
            shift
            ;;
        top)
            subcommand="top"
            shift
            ;;
        raw)
            output_format="raw"
            shift
            ;;
        -n|--top)
            if [[ -n "$2" && "$2" != -* ]]; then
                limit="$2"
                shift 2
            else
                echo "Error: -n/--top requires a number" >&2
                exit 1
            fi
            ;;
        --min)
            if [[ -n "$2" && "$2" != -* ]]; then
                min_urgency="$2"
                shift 2
            else
                echo "Error: --min requires a number" >&2
                exit 1
            fi
            ;;
        --include-wait)
            include_waiting=1
            shift
            ;;
        -*)
            echo "Unknown option: $1" >&2
            usage
            ;;
        *)
            # If we get here with a non-option argument, it might be a positional argument
            echo "Unknown argument: $1" >&2
            usage
            ;;
    esac
done

# Execute based on subcommand
process_tasks "$include_waiting" "$min_urgency" "$limit" "$output_format"
