#!/bin/bash
log.warn() { echo -e "[\e[33mWARN\e[0m]:  \e[1m$*\e[0m"; }
log.error()  { echo -e "[\e[31mERROR\e[0m]: \e[1m$*\e[0m"; }
log.info() { echo -e "[\e[96mINFO\e[0m]:  \e[1m$*\e[0m"; }
log.debug()  { echo -e "[\e[32mDEBUG\e[0m]: \e[1m$*\e[0m"; }

# 默认目录
# 判断当前用户是否为 root
if [[ "$EUID" -eq 0 ]]; then
    BASE_DIR_SYSTEM="/usr/share/gxde-k9/system/"
else
    BASE_DIR_SYSTEM="/usr/share/gxde-k9/"
fi

BASE_DIR_USER="$HOME/.local/share/GXDE/gxde-k9/"

PID_DIR="/tmp/GXDE/gxde-k9/$UID/"
LOCKFILE="${PID_DIR}/gxde-k9-daemon.lock"



# 打印帮助信息
print_help() {
    cat << EOF
Usage: $0 [options]

Options:
  --base-dir <path>    Specify the directory to monitor for .slimy .timer .shot scripts.
  --pid-dir <path>     Specify the location for the PID DIR.
  --termux             Use ${HOME}/gxde-k9/ as monitor directory to adapt termux.
  -h                   Show this help message and exit.

Description:
  K9 Lick Daemon gen 2 monitors a specified directory for .slimy scripts
  and executes them. It also supports .timer files with crontab-like schedules.
  By default, it monitors $SLIMY_DIR_SYSTEM and $TIMER_DIR_SYSTEM, as well as
  user-specific directories: $SLIMY_DIR_USER and $TIMER_DIR_USER.

Timer Example:
* * * * * | <command>
- - - - - -
| | | | | |
| | | | | +--- IMPORTANT: K9 Need an extra '|' to identify commands !!!!!!!!!!
| | | | +----- Day of week (0 - 7) (Sunday=0 or 7)
| | | +------- Month (1 - 12)
| | +--------- Day of month (1 - 31)
| +----------- Hour (0 - 23)
+------------- Minute (0 - 59)

EOF
    exit 0
}
# 自动创建多个目录（如果不存在）
create_directory_if_not_exists() {
    for dir_path in "$@"; do
        if [[ ! -d "$dir_path" ]]; then
            log.info "Directory $dir_path does not exist. Creating..."
            mkdir -p "$dir_path"
        fi
    done
}

function check_exist(){
    for arg in "$@";do
        if [[ ! -e "$arg" ]];then
            log.error "File or Directory does not exist: $arg "
        exit 1
        fi
    done
}


# 防止多次运行，带 PID 校验
check_lockfile() {
    if [[ -f "$LOCKFILE" ]]; then
        existing_pid=$(cat "$LOCKFILE")
        if [[ -n "$existing_pid" && -e "/proc/$existing_pid" ]]; then
            log.error "Daemon is already running with PID $existing_pid. Exiting."
            exit 1
        else
            log.info "Stale lockfile detected. Removing lockfile and continuing."
            rm -f "$LOCKFILE"
        fi
    fi
}

# 创建锁文件
create_lockfile() {
    create_directory_if_not_exists "$(dirname "$LOCKFILE")"
    echo $$ > "$LOCKFILE"
}

# 删除锁文件
remove_lockfile() {
    rm -f "$LOCKFILE"
    exit 0
}

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        --base-dir)
            shift
            if [[ -z "$1" ]]; then
                log.error "Error: --base-dir requires a directory path."
                exit 1
            fi
            BASE_DIR="$1"
            ;;
        --pid-dir)
            shift
            if [[ -z "$1" ]]; then
                log.error "Error: --pid-dir requires a directory path."
                exit 1
            fi
            PID_DIR="$1"
            LOCKFILE="$PID_DIR/gxde-k9-daemon.lock"
            ;;
        --termux)
            shift
            BASE_DIR=${HOME}/GXDE/gxde-k9/
            PID_DIR="${HOME}/GXDE/gxde-k9/$UID/"
            LOCKFILE="$PID_DIR/gxde-k9-daemon.lock"
            ;;
        -h|--help)
            print_help
            ;;
        *)
            log.error "Unknown option: $1"
            print_help
            ;;
    esac
    shift
done



# 使用用户指定的目录或默认目录
if [[ -n "$BASE_DIR" ]]; then
    BASE_DIR_SYSTEM="${BASE_DIR}"
fi

# 合成变量
    SLIMY_DIR_SYSTEM="${BASE_DIR_SYSTEM}/slimy/"
    TIMER_DIR_SYSTEM="${BASE_DIR_SYSTEM}/timer/"
    SHOT_DIR_SYSTEM="${BASE_DIR_SYSTEM}/shot/"
    EDGING_DIR_SYSTEM="${BASE_DIR_SYSTEM}/edging/"
    SLIMY_DIR="$SLIMY_DIR_SYSTEM"
    TIMER_DIR="$TIMER_DIR_SYSTEM"
    SHOT_DIR="$SHOT_DIR_SYSTEM"
    EDGING_DIR="$EDGING_DIR_SYSTEM"
if [[ -z "$BASE_DIR" ]]; then # 有指定的就不改了
    SLIMY_DIR_USER="$BASE_DIR_USER/slimy/"
    TIMER_DIR_USER="$BASE_DIR_USER/timer/"
    SHOT_DIR_USER="$BASE_DIR_USER/shot/"
    EDGING_DIR_USER="$BASE_DIR_USER/edging/"
fi

log.debug $BASE_DIR $BASE_DIR_SYSTEM $SLIMY_DIR
export SLIMY_DIR
export TIMER_DIR
export SHOT_DIR
export PID_DIR
export EDGING_DIR
export LOCKFILE

create_directory_if_not_exists ${SLIMY_DIR} ${TIMER_DIR} ${SHOT_DIR} ${SLIMY_DIR_USER} ${TIMER_DIR_USER} ${SHOT_DIR_USER} ${PID_DIR} ${EDGING_DIR_USER} ${EDGING_DIR}


# 检查并创建锁文件
check_lockfile
trap remove_lockfile SIGINT SIGTERM
create_lockfile


# 确保目录存在
if [[ ! -d "$SLIMY_DIR" ]]; then
    log.error "Error: Slimy directory $SLIMY_DIR does not exist!"
    remove_lockfile
fi

if [[ ! -d "$TIMER_DIR" ]]; then
    log.error "Error: Timer directory $TIMER_DIR does not exist!"
    remove_lockfile
fi

if [[ ! -d "$SHOT_DIR" ]]; then
    log.error "Error: Shot directory $SHOT_DIR does not exist!"
    remove_lockfile
fi

if [[ ! -d "$PID_DIR" ]]; then
    log.error "Error: PID directory $PID_DIR does not exist!"
    remove_lockfile
fi

if [[ ! -d "$EDGING_DIR" ]]; then
    log.error "Error: EDGING directory $EDGING_DIR does not exist!"
    remove_lockfile
fi


# 输出启动信息
log.info "--------------------------------------------------------------"
log.info "K9 Lick Daemon gen 2 is ready"
log.info "I am $(whoami)"
log.info "Watching slimy orders at $SLIMY_DIR | $SLIMY_DIR_USER"
log.info "Monitoring timers at $TIMER_DIR | $TIMER_DIR_USER"
log.info "Monitoring shots at $SHOT_DIR | $SHOT_DIR_USER"
log.info "Monitoring edgings at $EDGING_DIR | $EDGING_DIR_USER"
log.info "Lockfile location: $LOCKFILE"
log.info "--------------------------------------------------------------"

# 处理 cron 字段是否匹配
matches_cron_field() {
    local field="$1"
    local current_value="$2"
    if [[ "$field" == "*" ]]; then
        return 0  # 匹配所有值
    elif [[ "$field" == */* ]]; then
        # 如果是 "*/x" 的形式，例如 "*/5"，表示每 x 个单位匹配一次
        local step
        step=$(echo "$field" | cut -d'/' -f2)
        if (( current_value % step == 0 )); then
            return 0
        fi
    elif [[ "$field" == "$current_value" ]]; then
        return 0  # 完全匹配
    elif [[ "$field" == *"-"* ]]; then
        # 如果是范围形式，例如 "1-5" 表示匹配 1 到 5
        local start end
        start=$(echo "$field" | cut -d'-' -f1)
        end=$(echo "$field" | cut -d'-' -f2)
        if (( current_value >= start && current_value <= end )); then
            return 0
        fi
    elif [[ "$field" == *","* ]]; then
        # 如果是逗号分隔的多个值，例如 "1,2,5"
        IFS=',' read -ra values <<< "$field"
        for value in "${values[@]}"; do
            if [[ "$value" == "$current_value" ]]; then
                return 0
            fi
        done
    fi

    return 1  # 不匹配
}
# 解析 crontab-like 格式的定时器时间 (支持完整的 cron 语法)
is_cron_time_matched() {
    local cron_time="$1"
    local minute hour day month weekday

    # 将 cron 表达式分解成时间字段
    IFS=' ' read -r minute hour day month weekday <<< "$cron_time"

    # 获取当前的时间值
# 获取当前的时间值
local current_minute=$(date +'%M' | sed 's/^0//')  # 去掉前导零
local current_hour=$(date +'%H' | sed 's/^0//')    # 去掉前导零
local current_day=$(date +'%d' | sed 's/^0//')      # 去掉前导零
local current_month=$(date +'%m' | sed 's/^0//')    # 去掉前导零
local current_weekday=$(date +'%u')                  # 星期几没有前导零

    # 检查分钟字段
    if ! matches_cron_field "$minute" "$current_minute"; then return 1; fi

    # 检查小时字段
    if ! matches_cron_field "$hour" "$current_hour"; then return 1; fi

    # 检查日期字段
    if ! matches_cron_field "$day" "$current_day"; then return 1; fi

    # 检查月份字段
    if ! matches_cron_field "$month" "$current_month"; then return 1; fi

    # 检查星期几字段
    if ! matches_cron_field "$weekday" "$current_weekday"; then return 1; fi

    # 如果所有字段都匹配，则返回 true
    return 0
}


execute_timers_scripts_in_dir(){
    local dir_path="$1"
        for timer_file in "$dir_path"/*.timer ; do
            [[ -e "$timer_file" ]] || continue
            while IFS= read -r line; do
                schedule=$(echo "$line" | cut -d'|' -f1)
                command=$(echo "$line" | cut -d'|' -f2)
                # 如果 crontab 格式匹配当前时间
                if is_cron_time_matched "$schedule"; then
                    log.info "$(date +'%Y-%m-%d %H:%M:%S') - Running scheduled task: $command"
                    bash -c "$command" &
                fi
            done < "$timer_file"
        done
}

# 监控 timer 文件并执行任务
monitor_timers() {
    while true; do
    execute_timers_scripts_in_dir "$TIMER_DIR"
    # 如果用户目录存在，也执行其中的脚本
    if [[ -n "$TIMER_DIR_USER" ]]; then
    execute_timers_scripts_in_dir "$TIMER_DIR_USER"
    fi
        sleep 60
    done
}


# 运行所有 .slimy 脚本
execute_slimy_scripts_in_dir() {
    local dir_path="$1"

    # 查找所有 .slimy 文件并逐一执行
    for slimy_file in "$dir_path"/*.slimy; do
        # 检查文件是否存在，避免在目录为空时出错
        [[ -e "$slimy_file" ]] || continue
        
#        echo "$(date +'%Y-%m-%d %H:%M:%S') - Executing slimy script: $slimy_file"
        bash "$slimy_file" || log.error "$(date +'%Y-%m-%d %H:%M:%S') - Error executing slimy script: $slimy_file"
    done &
}


run_slimy_scripts() {
    # 执行指定目录中的所有 .slimy 脚本
    execute_slimy_scripts_in_dir "$SLIMY_DIR"

    # 如果用户目录存在，也执行其中的脚本
    if [[ -n "$SLIMY_DIR_USER" ]]; then
        execute_slimy_scripts_in_dir "$SLIMY_DIR_USER"
    fi
}

execute_shot_scripts_in_dir() {
    local dir_path="$1"
    for shot_file in "$dir_path"/*.shot ; do
        [[ -e "$shot_file" ]] || continue
        log.info "$(date +'%Y-%m-%d %H:%M:%S') - Executing one-shot script: $shot_file"
        bash "$shot_file" || log.error "$(date +'%Y-%m-%d %H:%M:%S') - Error executing one-shot script: $shot_file"
    done
}

run_shot_scripts() {
    # 执行指定目录中的所有 .shot 脚本
    execute_shot_scripts_in_dir "$SHOT_DIR"

    # 如果用户目录存在，也执行其中的脚本
    if [[ -n "$SHOT_DIR_USER" ]]; then
        execute_shot_scripts_in_dir "$SHOT_DIR_USER"
    fi
}

# 启动 slimy 监控 (每5秒执行一次)
watch_slimy_scripts() {
    while true; do
        run_slimy_scripts
        sleep 5
    done
}

edging_scripts_handler(){

source $1

# PID 文件目录（从环境变量 PID_DIR 获取，默认为用户配置目录）
PID_DIR="${PID_DIR:-/tmp/GXDE/gxde-k9/}"

# 自动生成 PROCESS_NAME 为当前 .edging 文件名（去掉扩展名）
PROCESS_NAME=$(basename "$1" .edging)

# ============ 脚本逻辑 ============

if [[ -n "$DONT_RUN_IF_SYSTEMD_EXIST" ]] && [ -d /run/systemd/system ]; then
    exit 0  # We don't need K9 to take over systemd work if DONT_RUN_IF_SYSTEMD_EXIST
fi


# 确保 PID 文件目录存在
mkdir -p "$PID_DIR" || {
    echo "[$PROCESS_NAME] 错误：无法创建 PID 文件目录 $PID_DIR！"
    exit 1
}

# 生成 PID 文件路径
PID_FILE="$PID_DIR/$PROCESS_NAME.pid"

# 检查 PID 文件是否存在且有效
if [[ -f "$PID_FILE" ]]; then
    PID=$(cat "$PID_FILE" 2>/dev/null)
    if [[ -n "$PID" && -e "/proc/$PID" ]]; then
            # 如果是命令，简单校验 PID 是否存活
#            echo "[$PROCESS_NAME] 检测到命令进程，PID: $PID 正在运行。"
            exit 0
    else
        echo "[$PROCESS_NAME] 无效的 PID 文件，清理：$PID_FILE"
        rm -f "$PID_FILE"
    fi
fi

# 如果 PID 文件不存在或无效，启动目标进程
echo "[$PROCESS_NAME] 目标进程未运行，正在启动：$TARGET_PROCESS"
bash -c "$TARGET_PROCESS" &
NEW_PID=$!
if [[ -n "$NEW_PID" && -e "/proc/$NEW_PID" ]]; then
    echo "$NEW_PID" > "$PID_FILE"
    echo "[$PROCESS_NAME] 目标进程启动成功，PID: $NEW_PID"
else
    echo "[$PROCESS_NAME] 错误：目标进程启动失败！"
    exit 1
fi
}

execute_edging_scripts_in_dir() {
    local dir_path="$1"

    # 查找所有 .slimy 文件并逐一执行
    for edging_file in "$dir_path"/*.edging; do
        # 检查文件是否存在，避免在目录为空时出错
        [[ -e "$edging_file" ]] || continue
        
        edging_scripts_handler "$edging_file" || log.error "$(date +'%Y-%m-%d %H:%M:%S') - Error executing edging script: $edging_file"
    done &
}
run_edging_scripts(){
    # 执行指定目录中的所有 .edging 脚本
    execute_edging_scripts_in_dir "$EDGING_DIR"

    # 如果用户目录存在，也执行其中的脚本
    if [[ -n "$SHOT_DIR_USER" ]]; then
        execute_edging_scripts_in_dir "$EDGING_DIR_USER"
    fi

}

watch_edging_scripts(){
    while true; do
        run_edging_scripts
        sleep 5
    done
}

# 只执行一次 shot 脚本
run_shot_scripts &
# 启动 slimy 
watch_slimy_scripts &
watch_edging_scripts &
# 启动定时任务监控
monitor_timers &

wait
