#!/bin/bash
# Common library functions for linux-configuration scripts
# Source this file at the beginning of scripts that need shared functionality
#
# Usage: source "$(dirname "$(readlink -f "$0")")/../lib/common.sh"
# Or:    source "/path/to/scripts/lib/common.sh"

# Prevent multiple sourcing
[[ -n ${_LIB_COMMON_LOADED:-} ]] && return 0
_LIB_COMMON_LOADED=1

# =============================================================================
# LOGGING FUNCTIONS
# =============================================================================

# Log message with timestamp to stderr and optionally to a file
# Usage: log_message "message" [log_file]
log_message() {
	local msg="$1"
	local log_file="${2:-}"
	local formatted
	printf -v formatted '%(%Y-%m-%d %H:%M:%S)T - %s' -1 "$msg"
	echo "$formatted" >&2
	if [[ -n $log_file ]]; then
		echo "$formatted" >>"$log_file" 2>/dev/null || true
	fi
}

# Simple log with timestamp (no file output)
# Usage: log "message"
log() {
	local _ts
	printf -v _ts '%(%Y-%m-%d %H:%M:%S)T' -1
	printf '[%s] %s\n' "$_ts" "$*"
}

# =============================================================================
# SUDO / ROOT HANDLING
# =============================================================================

# Check if running as root, if not re-exec with sudo
# Usage: require_root "$@"
require_root() {
	if [[ $EUID -ne 0 ]]; then
		echo "This script requires root privileges."
		echo "Requesting sudo access..."
		exec sudo "$0" "$@"
	fi
}

# Get the actual user even when running with sudo
# Usage: ACTUAL_USER=$(get_actual_user)
get_actual_user() {
	echo "${SUDO_USER:-$USER}"
}

# Get the actual user's home directory
# Usage: USER_HOME=$(get_actual_user_home)
get_actual_user_home() {
	local user
	user=$(get_actual_user)
	if [[ $user == "root" ]]; then
		echo "/root"
	else
		echo "/home/$user"
	fi
}

# Set both ACTUAL_USER and USER_HOME variables (common pattern)
# Usage: set_actual_user_vars
#        echo "$ACTUAL_USER"   # => the actual user
#        echo "$USER_HOME"     # => /home/username
set_actual_user_vars() {
	ACTUAL_USER=$(get_actual_user)
	USER_HOME=$(get_actual_user_home)
	export ACTUAL_USER USER_HOME
}

# =============================================================================
# ARGUMENT PARSING HELPERS
# =============================================================================

# Parse common --interactive/-i and --help/-h flags
# Sets INTERACTIVE_MODE variable (exported for use by calling scripts)
# Usage: parse_common_args "$@"
#        shift "$COMMON_ARGS_SHIFT"
export INTERACTIVE_MODE=false
export COMMON_ARGS_SHIFT=0

parse_interactive_args() {
	INTERACTIVE_MODE=false
	COMMON_ARGS_SHIFT=0
	local script_name="${0##*/}"

	while [[ $# -gt 0 ]]; do
		case $1 in
		-i | --interactive)
			INTERACTIVE_MODE=true
			((COMMON_ARGS_SHIFT++))
			shift
			;;
		-h | --help)
			echo "Usage: $script_name [OPTIONS]"
			echo "Options:"
			echo "  -i, --interactive    Enable interactive prompts (default: auto-yes)"
			echo "  -h, --help          Show this help message"
			exit 0
			;;
		*)
			# Stop parsing at first unknown argument
			break
			;;
		esac
	done
}

# Handle common argument patterns for scripts with custom usage functions
# Usage: handle_arg_help_or_unknown "$1" usage_function err_function
# Returns: 0 if argument was handled (caller should continue), 1 if not our concern
# Exits: on -h/--help (exit 0) or unknown arg starting with - (exit 2)
handle_arg_help_or_unknown() {
	local arg="$1"
	local usage_fn="${2:-usage}"
	local err_fn="${3:-err}"

	case "$arg" in
	-h | --help)
		"$usage_fn"
		exit 0
		;;
	-*)
		"$err_fn" "Unknown argument: $arg"
		"$usage_fn"
		exit 2
		;;
	*)
		return 1 # Not a flag, let caller handle it
		;;
	esac
	return 0
}

# Initialize a setup script with common boilerplate
# Usage: init_setup_script "Script Title" "$@"
# This combines: parse_interactive_args, shift, require_root, print_setup_header
init_setup_script() {
	local title="$1"
	shift
	parse_interactive_args "$@"
	shift "$COMMON_ARGS_SHIFT"
	require_root "$@"
	print_setup_header "$title"
}

# =============================================================================
# FOCUS APP DETECTION (for digital wellbeing scripts)
# =============================================================================

# Default focus apps - can be overridden before calling is_focus_app_running
FOCUS_APPS_WINDOWS=(
	"Visual Studio Code"
	"VSCodium"
	"Cursor"
	"IntelliJ IDEA"
	"PyCharm"
	"WebStorm"
	"CLion"
	"Rider"
	"Sublime Text"
	"Blender"
	"Godot"
	"Unity"
	"Unreal Editor"
)

FOCUS_APPS_PROCESSES=(
	"steam_app_"
	"gamescope"
)

# Check if any focus app is running (window-based detection)
# Returns 0 if focus app found, 1 otherwise
# Echoes the name of the found app
is_focus_app_running() {
	# One xdotool call with a combined regex instead of N separate calls
	if command -v xdotool &>/dev/null && [[ ${#FOCUS_APPS_WINDOWS[@]} -gt 0 ]]; then
		local regex wid
		printf -v regex '%s|' "${FOCUS_APPS_WINDOWS[@]}"
		regex="${regex%|}"  # strip trailing |
		while IFS= read -r wid; do
			[[ -n $wid ]] || continue
			echo "focus app"
			return 0
		done < <(xdotool search --name "$regex" 2>/dev/null)
	fi

	# Check specific processes via /proc (no fork)
	local app comm
	for app in "${FOCUS_APPS_PROCESSES[@]}"; do
		for comm in /proc/[0-9]*/comm; do
			[[ -r $comm ]] || continue
			read -r _proc_comm < "$comm" 2>/dev/null || continue
			if [[ $_proc_comm == *"$app"* ]]; then
				echo "$_proc_comm"
				return 0
			fi
		done
	done

	return 1
}

# =============================================================================
# COMMAND AVAILABILITY
# =============================================================================

# Check if a command exists
# Usage: if require_command ffmpeg; then ...
require_command() {
	local cmd="$1"
	local pkg="${2:-$1}"
	if ! command -v "$cmd" >/dev/null 2>&1; then
		echo "Error: '$cmd' is not installed or not in PATH." >&2
		echo "Install with: sudo pacman -S $pkg" >&2
		return 1
	fi
	return 0
}

# Check for ImageMagick and display helpful installation message
# Usage: require_imagemagick [optional: "magick" or "convert"]
# Returns: Sets MAGICK_CMD variable to available command
require_imagemagick() {
	local preferred="${1:-}"

	if [[ $preferred == "magick" ]] || [[ -z $preferred ]]; then
		if command -v magick &>/dev/null; then
			MAGICK_CMD="magick"
			export MAGICK_CMD
			return 0
		fi
	fi

	if [[ $preferred == "convert" ]] || [[ -z $preferred ]]; then
		if command -v convert &>/dev/null; then
			MAGICK_CMD="convert"
			export MAGICK_CMD
			return 0
		fi
	fi

	echo "Error: ImageMagick is not installed." >&2
	echo "Install it with:" >&2
	echo "  Arch Linux: sudo pacman -S imagemagick" >&2
	echo "  Ubuntu/Debian: sudo apt install imagemagick" >&2
	return 1
}

# Install missing pacman packages
# Usage: install_missing_pacman_packages pkg1 pkg2 pkg3 ...
# Returns 0 if all packages installed successfully, 1 otherwise
install_missing_pacman_packages() {
	local packages=("$@")
	local missing=()

	for pkg in "${packages[@]}"; do
		if ! pacman -Qi "$pkg" >/dev/null 2>&1; then
			missing+=("$pkg")
		fi
	done

	if [[ ${#missing[@]} -eq 0 ]]; then
		echo "[INFO] All required packages are already installed."
		return 0
	fi

	echo "[INFO] Installing missing packages: ${missing[*]}"
	if ! sudo pacman -S --needed --noconfirm "${missing[@]}"; then
		echo "[ERROR] Failed to install packages" >&2
		return 1
	fi
	return 0
}

# =============================================================================
# NOTIFICATION
# =============================================================================

# Send desktop notification (fails silently if notify-send not available)
# Usage: notify "Title" "Message" [urgency: low/normal/critical] [timeout_ms]
notify() {
	local title="$1"
	local message="$2"
	local urgency="${3:-normal}"
	local timeout="${4:-5000}"

	if command -v notify-send &>/dev/null; then
		notify-send -u "$urgency" -t "$timeout" "$title" "$message" 2>/dev/null || true
	fi
}

# =============================================================================
# FILE/PATH UTILITIES
# =============================================================================

# Get the directory containing the calling script
# Usage: SCRIPT_DIR=$(get_script_dir)
get_script_dir() {
	dirname "$(readlink -f "${BASH_SOURCE[1]:-$0}")"
}

# Ensure a directory exists
# Usage: ensure_dir "/path/to/dir"
ensure_dir() {
	local dir="$1"
	if [[ ! -d $dir ]]; then
		mkdir -p "$dir"
	fi
}

# =============================================================================
# SYSTEMD HELPERS
# =============================================================================

# Internal helper for running systemctl with optional --user flag
_systemctl_cmd() {
	local user_flag="$1"
	shift
	if [[ $user_flag == "--user" ]]; then
		systemctl --user "$@"
	else
		systemctl "$@"
	fi
}

# Enable and start a systemd service (user or system)
# Usage: enable_service "service-name" [--user]
enable_service() {
	local service="$1"
	local user_flag="${2:-}"
	_systemctl_cmd "$user_flag" daemon-reload
	_systemctl_cmd "$user_flag" enable --now "$service"
}

# Check if a systemd service is active
# Usage: if is_service_active "service-name" [--user]; then ...
is_service_active() {
	_systemctl_cmd "${2:-}" is-active --quiet "$1"
}

# Check if a systemd service is enabled
# Usage: if is_service_enabled "service-name" [--user]; then ...
is_service_enabled() {
	_systemctl_cmd "${2:-}" is-enabled --quiet "$1" 2>/dev/null
}

# =============================================================================
# COLORED LOGGING (for scripts that need colored output)
# =============================================================================

# ANSI color codes
declare -g COLOR_RED='\033[1;31m'
declare -g COLOR_GREEN='\033[1;32m'
declare -g COLOR_YELLOW='\033[1;33m'
declare -g COLOR_BLUE='\033[1;34m'
declare -g COLOR_NC='\033[0m'

log_info() {
	printf "${COLOR_BLUE}[INFO]${COLOR_NC} %s\n" "$*"
}

log_ok() {
	printf "${COLOR_GREEN}[ OK ]${COLOR_NC} %s\n" "$*"
}

log_warn() {
	printf "${COLOR_YELLOW}[WARN]${COLOR_NC} %s\n" "$*" >&2
}

log_error() {
	printf "${COLOR_RED}[ERROR]${COLOR_NC} %s\n" "$*" >&2
}

# Alias for compatibility
warn() { log_warn "$@"; }
err() { log_error "$@"; }

# =============================================================================
# EFFICIENT TIME FUNCTIONS (zero-fork bash builtins)
# =============================================================================
# These functions use printf '%(...)'T' bash builtin (NO external commands)
# to avoid fork-storm anti-patterns in polling scripts.
# See: .github/skills/efficient-polling-scripts/SKILL.md

# Get current Unix timestamp (seconds since epoch)
# Usage: ts=$(get_timestamp)
# FORK-FREE: uses bash builtin printf %s (sec_since_epoch)
get_timestamp() {
	printf '%(%s)T' -1
}

# Get current date in YYYY-MM-DD format
# Usage: date=$(get_date)
get_date() {
	printf '%(%Y-%m-%d)T' -1
}

# Get current time in HH:MM:SS format
# Usage: time=$(get_time)
get_time() {
	printf '%(%H:%M:%S)T' -1
}

# Get current date-time in YYYY-MM-DD HH:MM:SS format
# Usage: dt=$(get_datetime)
get_datetime() {
	printf '%(%Y-%m-%d %H:%M:%S)T' -1
}

# Get day of week (1=Monday, 7=Sunday)
# Usage: dow=$(get_day_of_week)
get_day_of_week() {
	printf '%(%u)T' -1
}

# Get day name (Monday, Tuesday, ...)
# Usage: day=$(get_day_name)
get_day_name() {
	printf '%(%A)T' -1
}

# Get current hour (00-23)
# Usage: hour=$(get_hour)
get_hour() {
	printf '%(%H)T' -1
}

# Get current minute (00-59)
# Usage: minute=$(get_minute)
get_minute() {
	printf '%(%M)T' -1
}

# Get current second (00-59)
# Usage: second=$(get_second)
get_second() {
	printf '%(%S)T' -1
}

# Get Unix timestamp from boot (uptime in seconds)
# Usage: boot_seconds=$(get_uptime_seconds)
get_uptime_seconds() {
	read -r uptime_with_fraction _ < /proc/uptime
	printf '%.*f\n' 0 "$uptime_with_fraction"
}

# Get boot time in YYYY-MM-DD HH:MM:SS format
# Usage: boot_time=$(get_boot_datetime)
# Calculates: current_time - uptime_seconds
get_boot_datetime() {
	local uptime_seconds
	uptime_seconds=$(get_uptime_seconds)
	local boot_ts=$(($(get_timestamp) - uptime_seconds))
	printf '%(%Y-%m-%d %H:%M:%S)T' "$boot_ts"
}

# Get boot time date only (YYYY-MM-DD)
# Usage: boot_date=$(get_boot_date)
get_boot_date() {
	local uptime_seconds
	uptime_seconds=$(get_uptime_seconds)
	local boot_ts=$(($(get_timestamp) - uptime_seconds))
	printf '%(%Y-%m-%d)T' "$boot_ts"
}

# Get boot time hour only (00-23)
# Usage: boot_hour=$(get_boot_hour)
get_boot_hour() {
	local uptime_seconds
	uptime_seconds=$(get_uptime_seconds)
	local boot_ts=$(($(get_timestamp) - uptime_seconds))
	printf '%(%H)T' "$boot_ts"
}

# Check if current time is within a given hour range
# Usage: if is_hour_in_range 5 8; then ...  # 5AM-8AM
is_hour_in_range() {
	local start_hour=$1
	local end_hour=$2
	local current_hour
	current_hour=$(get_hour)
	local current_hour_num=$((10#$current_hour))
	[[ $current_hour_num -ge $start_hour ]] && [[ $current_hour_num -lt $end_hour ]]
}

# Check if current day is a specific day of week
# Usage: if is_day_of_week 1 5 6 7; then ...  # Monday, Friday, Saturday, Sunday
is_day_of_week() {
	local target_day
	target_day=$(get_day_of_week)
	for day in "$@"; do
		[[ $target_day -eq $day ]] && return 0
	done
	return 1
}

# =============================================================================
# INTERACTIVE PROMPTS
# =============================================================================

# Ask yes/no question, returns 0 for yes, 1 for no
# Usage: if ask_yes_no "Continue?"; then ...
ask_yes_no() {
	local prompt="$1"
	local ans
	read -r -p "$prompt [y/N]: " ans || true
	case "${ans:-}" in
	y | Y | yes | YES) return 0 ;;
	*) return 1 ;;
	esac
}

# Check if a command is available
# Usage: if has_cmd git; then ...
has_cmd() {
	command -v "$1" >/dev/null 2>&1
}

# =============================================================================
# STANDARD SETUP HEADER
# =============================================================================

# Print a standard setup header for scripts
# Usage: print_setup_header "Script Name"
print_setup_header() {
	local title="$1"
	local current_datetime
	current_datetime=$(get_datetime)
	echo "$title"
	printf '=%.0s' $(seq 1 ${#title})
	echo ""
	echo "Current Date: $current_datetime"
	echo "User: $USER"
	echo "Original user: $(get_actual_user)"
	if [[ $INTERACTIVE_MODE == "true" ]]; then
		echo "Mode: Interactive (prompts enabled)"
	else
		echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
	fi
}

# =============================================================================
# MOUNT/UNMOUNT HELPERS (for hosts guard and similar)
# =============================================================================

# Count mount layers for a path
# Usage: count=$(mount_layers_count "/etc/hosts")
mount_layers_count() {
	local target="$1"
	awk -v t="$target" '$5==t{c++} END{print c+0}' /proc/self/mountinfo 2>/dev/null || echo 0
}

# Collapse all bind mount layers for a path
# Usage: collapse_mounts "/etc/hosts" [max_iterations]
collapse_mounts() {
	local target="$1"
	local max_iter="${2:-20}"
	local i=0

	if has_cmd mountpoint; then
		while mountpoint -q "$target"; do
			umount -l "$target" >/dev/null 2>&1 || break
			i=$((i + 1))
			((i >= max_iter)) && break
		done
	else
		local cnt
		cnt=$(mount_layers_count "$target")
		while ((cnt > 1)); do
			umount -l "$target" >/dev/null 2>&1 || break
			i=$((i + 1))
			((i >= max_iter)) && break
			cnt=$(mount_layers_count "$target")
		done
	fi
}

# =============================================================================
# RESOLUTION/FORMAT VALIDATION
# =============================================================================

# Validate resolution format (WIDTHxHEIGHT)
# Usage: if validate_resolution "1920x1080"; then ...
validate_resolution() {
	local res="$1"
	[[ $res =~ ^[0-9]+x[0-9]+$ ]]
}

# Generate output filename with suffix
# Usage: output=$(generate_output_filename "input.jpg" "_resized")
generate_output_filename() {
	local input="$1"
	local suffix="$2"
	local ext="${3:-}"

	local basename dirname filename extension
	basename=$(basename "$input")
	dirname=$(dirname "$input")
	filename="${basename%.*}"
	extension="${basename##*.}"

	# Handle files without extension
	if [[ $filename == "$extension" ]]; then
		extension="${ext:-jpg}"
	fi

	echo "${dirname}/${filename}${suffix}.${extension}"
}
