#!/bin/bash

STATE_DIR="/tmp/hypr-alt-tab"
STATE_FILE="$STATE_DIR/state"
TIME_FILE="$STATE_DIR/last_press"
CURRENT_INDEX_FILE="$STATE_DIR/current_index"
WINDOW_LIST_FILE="$STATE_DIR/window_list"

# Cooldown in seconds (time within which presses count as "continuous")
COOLDOWN=0.8

# Create state directory if it doesn't exist
mkdir -p "$STATE_DIR"

# Parse arguments
same_type=false
reverse=false
visible_only=false

for arg in "$@"; do
    case $arg in
        --same) same_type=true ;;
        --reverse) reverse=true ;;
        --visible) visible_only=true ;;
    esac
done

# Function to get window class
get_window_class() {
    local window="$1"
    hyprctl clients -j | jq -r --arg addr "$window" '.[] | select(.address == $addr) | .class'
}

# Function to get window title
get_window_title() {
    local window="$1"
    hyprctl clients -j | jq -r --arg addr "$window" '.[] | select(.address == $addr) | .title'
}

# Function to get window workspace ID
get_window_workspace() {
    local window="$1"
    hyprctl clients -j | jq -r --arg addr "$window" '.[] | select(.address == $addr) | .workspace.id'
}

# Function to get active workspaces (visible on monitors)
get_active_workspaces() {
    hyprctl monitors -j | jq -r '.[].activeWorkspace.id'
}

# Function to get all windows sorted by focus history (most recent first)
get_windows_by_focus() {
    hyprctl clients -j | jq -r 'sort_by(-.focusHistoryID) | .[].address'
}

# Function to get visible windows (on active workspaces)
get_visible_windows() {
    # Get all active workspace IDs
    mapfile -t active_workspaces < <(get_active_workspaces)
    
    if [[ ${#active_workspaces[@]} -eq 0 ]]; then
        return
    fi
    
    # Convert active workspaces to JSON array for jq query
    local workspaces_json="["
    for ws in "${active_workspaces[@]}"; do
        workspaces_json+="$ws,"
    done
    workspaces_json="${workspaces_json%,}]"
    
    # Get windows in active workspaces, sorted by focus history
    hyprctl clients -j | jq -r --argjson workspaces "$workspaces_json" '
        [.[] | select(.workspace.id as $ws | $workspaces | index($ws))] 
        | sort_by(-.focusHistoryID) 
        | .[].address
    '
}

# Function to get windows filtered by type (same class or title pattern)
get_same_type_windows() {
    local current_window="$1"
    local current_class=$(get_window_class "$current_window")
    local current_title=$(get_window_title "$current_window")
    
    # Get all windows with same class
    hyprctl clients -j | jq -r --arg class "$current_class" \
        '.[] | select(.class == $class) | .address'
}

# Function to get currently focused window
get_focused_window() {
    hyprctl activewindow -j | jq -r '.address'
}

# Function to check if window exists
window_exists() {
    local window="$1"
    [[ -n "$window" ]] && hyprctl clients -j | jq -r '.[].address' | grep -q "^${window}$"
}

# Function to validate and refresh window list
validate_window_list() {
    local windows=("$@")
    local valid_windows=()
    
    for window in "${windows[@]}"; do
        if window_exists "$window"; then
            valid_windows+=("$window")
        fi
    done
    
    printf '%s\n' "${valid_windows[@]}"
}

# Get currently focused window
current_focused=$(get_focused_window)

# Get appropriate window list based on mode
if [[ "$visible_only" == "true" ]]; then
    # Get only windows in active workspaces (visible on monitors)
    echo "Switching between visible windows only" >&2
    mapfile -t windows < <(get_visible_windows)
    
    # If no visible windows, fall back to all windows
    if [[ ${#windows[@]} -eq 0 ]]; then
        echo "No visible windows found, falling back to all windows" >&2
        mapfile -t windows < <(get_windows_by_focus)
    fi
    
    # Apply same-type filter if requested
    if [[ "$same_type" == "true" ]] && [[ -n "$current_focused" ]]; then
        # Get windows of the same type as current (from visible windows)
        mapfile -t same_type_windows < <(get_same_type_windows "$current_focused")
        
        # Filter visible windows to only include same-type windows
        local filtered_windows=()
        for visible_window in "${windows[@]}"; do
            for same_window in "${same_type_windows[@]}"; do
                if [[ "$visible_window" == "$same_window" ]]; then
                    filtered_windows+=("$visible_window")
                    break
                fi
            done
        done
        
        # If filtered list has windows, use it
        if [[ ${#filtered_windows[@]} -gt 0 ]]; then
            windows=("${filtered_windows[@]}")
        else
            echo "No visible windows of same type, using all visible windows" >&2
        fi
    fi
elif [[ "$same_type" == "true" ]] && [[ -n "$current_focused" ]]; then
    # Get windows of the same type as current (from all windows)
    mapfile -t windows < <(get_same_type_windows "$current_focused")
    
    # If no same-type windows or only current window, fall back to all windows
    if [[ ${#windows[@]} -le 1 ]]; then
        echo "No other windows of same type found, switching to all windows" >&2
        mapfile -t windows < <(get_windows_by_focus)
    fi
else
    # Get all windows
    mapfile -t windows < <(get_windows_by_focus)
fi

# Validate windows still exist
mapfile -t windows < <(validate_window_list "${windows[@]}")

# Exit if no windows or only one window
if [[ ${#windows[@]} -le 1 ]]; then
    echo "Not enough windows to switch" >&2
    exit 0
fi

# Get current time
current_time=$(date +%s.%N)

# Read last press time
last_press=0
[[ -f "$TIME_FILE" ]] && last_press=$(<"$TIME_FILE")

# Calculate time difference
time_diff=$(echo "$current_time - $last_press" | bc -l 2>/dev/null || echo "1")

# Check if we're continuing the Alt+Tab session
continuing_session=false
if (( $(echo "$time_diff <= $COOLDOWN" | bc -l 2>/dev/null || echo "0") )); then
    continuing_session=true
fi

# Find current window index in the list
current_index=-1
for i in "${!windows[@]}"; do
    if [[ "${windows[$i]}" == "$current_focused" ]]; then
        current_index=$i
        break
    fi
done

# If current window not in filtered list, start from beginning
if [[ $current_index -eq -1 ]]; then
    current_index=0
fi

# Calculate target index
if [[ "$continuing_session" == "true" ]] && [[ -f "$CURRENT_INDEX_FILE" ]] && [[ -f "$WINDOW_LIST_FILE" ]]; then
    # Read saved state
    saved_index=$(<"$CURRENT_INDEX_FILE")
    mapfile -t saved_windows < "$WINDOW_LIST_FILE"
    
    # Check if saved windows match current windows
    same_list=true
    if [[ ${#windows[@]} -ne ${#saved_windows[@]} ]]; then
        same_list=false
    else
        for i in "${!windows[@]}"; do
            if [[ "${windows[$i]}" != "${saved_windows[$i]}" ]]; then
                same_list=false
                break
            fi
        done
    fi
    
    if [[ "$same_list" == "true" ]]; then
        if [[ "$reverse" == "true" ]]; then
            target_index=$(( (saved_index - 1 + ${#windows[@]}) % ${#windows[@]} ))
        else
            target_index=$(( (saved_index + 1) % ${#windows[@]} ))
        fi
    else
        # List changed, recalculate from current
        if [[ "$reverse" == "true" ]]; then
            target_index=$(( (current_index - 1 + ${#windows[@]}) % ${#windows[@]} ))
        else
            target_index=$(( (current_index + 1) % ${#windows[@]} ))
        fi
    fi
else
    # Start new session - move from current window
    if [[ "$reverse" == "true" ]]; then
        target_index=$(( (current_index - 1 + ${#windows[@]}) % ${#windows[@]} ))
    else
        target_index=$(( (current_index + 1) % ${#windows[@]} ))
    fi
fi

# Save state
echo "$current_time" > "$TIME_FILE"
echo "$target_index" > "$CURRENT_INDEX_FILE"
printf '%s\n' "${windows[@]}" > "$WINDOW_LIST_FILE"

# Focus the target window
target_window="${windows[$target_index]}"
if window_exists "$target_window"; then
    echo "Switching to window: $target_window (Class: $(get_window_class "$target_window"), Workspace: $(get_window_workspace "$target_window"))" >&2
    hyprctl dispatch focuswindow "address:$target_window"
    hyprctl dispatch bringactivetotop
fi

# Visual feedback: dim inactive windows temporarily
hyprctl keyword decoration:dim_inactive true 2>/dev/null

# Clear any existing reset timer
[[ -f "$STATE_DIR/dim_timer" ]] && kill "$(<"$STATE_DIR/dim_timer")" 2>/dev/null || true

# Start timer to reset dimming
(
    sleep 1.2
    hyprctl keyword decoration:dim_inactive false 2>/dev/null
) &
echo $! > "$STATE_DIR/dim_timer"
