#!/bin/sh

# Copyright (C) 2006 OpenWrt.org
# Copyright 2022-2026 sirpdboy <herboy2008@gmail.com>

NAME=timecontrol
LOG_FILE="/var/log/timecontrol.log"
DEBUG=1

# 时长数据库目录
DURATION_DIR="/var/lib/timecontrol"
DURATION_DB="$DURATION_DIR/duration.db"
CONNECTION_LOG="$DURATION_DIR/connections.log"

# 状态文件
IDLIST="/var/$NAME.idlist"
STATUS_DB="$DURATION_DIR/status.db"

# 初始化目录
init_dirs() {
    mkdir -p "$DURATION_DIR"
    touch "$DURATION_DB" "$CONNECTION_LOG" "$STATUS_DB" 2>/dev/null
}

# 日志函数
dbg() {
    [ "$DEBUG" -eq 1 ] && {
        local d="$(date '+%Y-%m-%d %H:%M:%S')"
        echo "[$d] CTRL-DEBUG: $@" >> "$LOG_FILE"
    }
}

info() {
    local d="$(date '+%Y-%m-%d %H:%M:%S')"
    echo "[$d] CTRL-INFO: $@" >> "$LOG_FILE"
}

# 配置文件读取
config_t_get() {
    local index=${3:-0}
    local ret=$(uci -q get "${NAME}.@${1}[${index}].${2}")
    echo "${ret:=${4}}"
}

# 获取启用设备
get_enabled_devices() {
    uci show $NAME 2>/dev/null | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sort -n
}

# 时间检查函数
is_time_in_range() {
    local start_time=$1
    local end_time=$2
    local current_time=$(date +%H:%M)

    if [ "$start_time" = "$end_time" ]; then
        return 0
    elif [ "$start_time" \< "$end_time" ]; then
        [ "$current_time" \> "$start_time" ] && [ "$current_time" \< "$end_time" ] && return 0
    else
        [ "$current_time" \> "$start_time" ] || [ "$current_time" \< "$end_time" ] && return 0
    fi
    return 1
}

is_weekday_in_range() {
    local configured_weekdays=$1
    local current_weekday=$(date +%u)

    [ "$configured_weekdays" = "0" ] && return 0
    
    for ww in $(echo $configured_weekdays | sed 's/,/ /g'); do
        [ "$current_weekday" = "$ww" ] && return 0
    done
    return 1
}

# 时长管理
record_connection_time() {
    local target="$1"
    local action="$2"
    local timestamp=$(date +%s)
    
    echo "$timestamp,$target,$action" >> "$CONNECTION_LOG"
    
    if [ "$action" = "connect" ]; then
        if ! grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
            echo "$target,$timestamp,0,0" >> "$DURATION_DB"
            dbg "初始化时长: $target"
        fi
    elif [ "$action" = "disconnect" ]; then
        if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
            local last_connect=$(grep "^$target," "$DURATION_DB" | cut -d',' -f2)
            local total_used=$(grep "^$target," "$DURATION_DB" | cut -d',' -f3)
            local last_reset=$(grep "^$target," "$DURATION_DB" | cut -d',' -f4)
            
            local session_duration=$((timestamp - last_connect))
            local new_total=$((total_used + session_duration))
            
            sed -i "/^$target,/d" "$DURATION_DB"
            echo "$target,$timestamp,$new_total,$last_reset" >> "$DURATION_DB"
        fi
    fi
}

get_connection_time() {
    local target="$1"
    local current_time=$(date +%s)
    
    if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
        local last_connect=$(grep "^$target," "$DURATION_DB" | cut -d',' -f2)
        local total_used=$(grep "^$target," "$DURATION_DB" | cut -d',' -f3)
        
        if tail -n 5 "$CONNECTION_LOG" 2>/dev/null | grep -q "^[0-9]*,$target,connect$"; then
            local session_duration=$((current_time - last_connect))
            total_used=$((total_used + session_duration))
        fi
        
        echo "$total_used"
    else
        echo "0"
    fi
}

# 重置检查
should_reset_duration() {
    local target="$1"
    local reset_cycle="$2"
    local last_reset_file="$DURATION_DIR/last_reset_$target"
    
    [ ! -f "$last_reset_file" ] && return 0
    
    local last_reset=$(cat "$last_reset_file" 2>/dev/null)
    local current_time=$(date +%s)
    
    case "$reset_cycle" in
        "daily")
            local last_date=$(date -d "@$last_reset" +%Y%m%d 2>/dev/null || echo "0")
            local current_date=$(date +%Y%m%d)
            [ "$last_date" != "$current_date" ]
            ;;
        "weekly")
            local last_week=$(date -d "@$last_reset" +%Y%W 2>/dev/null || echo "0")
            local current_week=$(date +%Y%W)
            [ "$last_week" != "$current_week" ]
            ;;
        "monthly")
            local last_month=$(date -d "@$last_reset" +%Y%m 2>/dev/null || echo "0")
            local current_month=$(date +%Y%m)
            [ "$last_month" != "$current_month" ]
            ;;
        *)
            false
            ;;
    esac
}

reset_duration_counter() {
    local target="$1"
    local reset_cycle="$2"
    local current_time=$(date +%s)
    
    if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
        sed -i "/^$target,/d" "$DURATION_DB"
    fi
    
    echo "$target,$current_time,0,$current_time" >> "$DURATION_DB"
    echo "$current_time" > "$DURATION_DIR/last_reset_$target"
    
    info "重置时长: $target (周期: $reset_cycle)"
}

# 主检查函数
check_device_control() {
    local id="$1"
    local target=$(config_t_get device mac "$id")
    local time_mode=$(config_t_get device time_mode "$id" "period")
    local weekdays=$(config_t_get device week "$id" "0")
    
    # 检查星期
    is_weekday_in_range "$weekdays" || {
        dbg "星期不允许: $target"
        return 1
    }
    
    case "$time_mode" in
        "period")
            local start_time=$(config_t_get device timestart "$id" "00:00")
            local end_time=$(config_t_get device timeend "$id" "00:00")
            is_time_in_range "$start_time" "$end_time" || {
                dbg "时间段外: $target ($start_time-$end_time)"
                return 1
            }
            dbg "时间段内: $target"
            return 0
            ;;
            
        "duration")
            local duration=$(config_t_get device duration "$id" "60")
            local reset_cycle=$(config_t_get device reset_cycle "$id" "daily")
            
            # 重置检查
            if should_reset_duration "$target" "$reset_cycle"; then
                reset_duration_counter "$target" "$reset_cycle"
            fi
            
            # 记录连接
            local last_action=$(grep ",$target," "$CONNECTION_LOG" 2>/dev/null | tail -1 | cut -d',' -f3)
            if [ "$last_action" != "connect" ]; then
                record_connection_time "$target" "connect"
            fi
            
            # 检查时长
            local used_seconds=$(get_connection_time "$target")
            local used_minutes=$((used_seconds / 60))
            local max_minutes=$duration
            
            dbg "时长检查: $target 已用=${used_minutes}分钟, 限制=${max_minutes}分钟"
            
            if [ "$used_minutes" -ge "$max_minutes" ]; then
                dbg "已超时: $target"
                return 1
            fi
            dbg "未超时: $target"
            return 0
            ;;
            
        "combined")
            local start_time=$(config_t_get device timestart "$id" "00:00")
            local end_time=$(config_t_get device timeend "$id" "00:00")
            local use_duration=$(config_t_get device use_duration "$id" "0")
            
            # 时间段检查
            is_time_in_range "$start_time" "$end_time" || {
                dbg "时间段外: $target"
                return 1
            }
            
            # 时长检查
            if [ "$use_duration" = "1" ]; then
                local duration=$(config_t_get device duration "$id" "60")
                local reset_cycle=$(config_t_get device reset_cycle "$id" "daily")
                
                if should_reset_duration "$target" "$reset_cycle"; then
                    reset_duration_counter "$target" "$reset_cycle"
                fi
                
                local last_action=$(grep ",$target," "$CONNECTION_LOG" 2>/dev/null | tail -1 | cut -d',' -f3)
                if [ "$last_action" != "connect" ]; then
                    record_connection_time "$target" "connect"
                fi
                
                local used_seconds=$(get_connection_time "$target")
                local used_minutes=$((used_seconds / 60))
                local max_minutes=$duration
                
                if [ "$used_minutes" -ge "$max_minutes" ]; then
                    dbg "时间段内但已超时: $target"
                    return 1
                fi
            fi
            
            dbg "时间段内允许: $target"
            return 0
            ;;
            
        *)
            # 默认时间段控制
            local start_time=$(config_t_get device timestart "$id" "00:00")
            local end_time=$(config_t_get device timeend "$id" "00:00")
            is_time_in_range "$start_time" "$end_time"
            return $?
            ;;
    esac
}

# 更新设备状态
update_device_status() {
    local id="$1"
    local should_allow="$2"  # 0=允许, 1=禁止
    local target=$(config_t_get device mac "$id")
    local comment=$(config_t_get device comment "$id" "设备$id")
    
    # 检查当前状态
    local current_blocked=0
    if [ -f "$IDLIST" ] && grep -q "!${id}!" "$IDLIST" 2>/dev/null; then
        current_blocked=1
    fi
    
    dbg "设备状态: $target, 应该允许=$should_allow, 当前阻止=$current_blocked"
    
    if [ "$should_allow" -eq 0 ]; then
        # 应该允许
        if [ "$current_blocked" -eq 1 ]; then
            dbg "解除阻止: $target"
            timecontrol del "$id"
            sed -i "/!$id!/d" "$IDLIST" 2>/dev/null
            info "允许上网: $comment ($target)"
            record_connection_time "$target" "disconnect"
        fi
    else
        # 应该阻止
        if [ "$current_blocked" -eq 0 ]; then
            dbg "添加阻止: $target"
            timecontrol add "$id"
            if ! grep -q "!$id!" "$IDLIST" 2>/dev/null; then
                echo "!$id!" >> "$IDLIST"
            fi
            info "阻止上网: $comment ($target)"
            record_connection_time "$target" "disconnect"
        fi
    fi
}

# 主处理循环
main_loop() {
    info "时间控制守护进程启动"
    init_dirs
    while :; do
        dbg "开始检查设备"
        [ `uci show $NAME 2>/dev/null | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sort -n | wc -l` eq 0 ] && timecontrol stop && break
        for id in $(get_enabled_devices); do
            if check_device_control "$id"; then
                update_device_status "$id" 0  # 允许
            else
                update_device_status "$id" 1  # 阻止
            fi
        done
        
        sleep 60
    done
}

# 启动
main_loop
