#!/bin/sh

CONFIG="led-scheduler"
SECTION="main"
LED_ROOT="/sys/class/leds"
CRON_FILE="/etc/crontabs/root"
CRON_BEGIN="# BEGIN luci-app-led-scheduler"
CRON_END="# END luci-app-led-scheduler"

log() { logger -t led-scheduler "$*"; }
uci_get() { uci -q get "${CONFIG}.${SECTION}.$1" 2>/dev/null; }

valid_led_name() {
	case "$1" in
		""|*/*|*".."*|*[!A-Za-z0-9:_.-]*) return 1 ;;
	esac
	return 0
}

available_led() {
	valid_led_name "$1" || return 1
	[ -d "${LED_ROOT}/$1" ]
}

configured_leds() {
	local section sysfs
	uci -q show system 2>/dev/null |
		sed -n 's/^system\.\([^=]*\)=led$/\1/p' |
		while IFS= read -r section; do
			sysfs="$(uci -q get "system.${section}.sysfs" 2>/dev/null)"
			[ -n "$sysfs" ] || continue
			available_led "$sysfs" || continue
			printf '%s\n' "$sysfs"
		done |
		awk '!seen[$0]++'
}

selected_leds() {
	local all selected led
	all="$(uci_get all_system_leds)"
	[ -n "$all" ] || all="1"
	if [ "$all" = "1" ]; then
		configured_leds
		return 0
	fi
	selected="$(uci_get led)"
	for led in $selected; do
		available_led "$led" || continue
		printf '%s\n' "$led"
	done | awk '!seen[$0]++'
}

restore_native() {
	if [ -x /etc/init.d/led ]; then
		/etc/init.d/led restart >/dev/null 2>&1
		return $?
	fi
	echo "OpenWrt LED service /etc/init.d/led not found" >&2
	return 1
}

turn_off() {
	local led path
	restore_native >/dev/null 2>&1 || true
	selected_leds | while IFS= read -r led; do
		available_led "$led" || continue
		path="${LED_ROOT}/${led}"
		[ -w "${path}/trigger" ] && printf '%s\n' none > "${path}/trigger" 2>/dev/null
		[ -w "${path}/brightness" ] && printf '%s\n' 0 > "${path}/brightness" 2>/dev/null
	done
	log "selected LEDs turned off"
}

turn_on() {
	if restore_native; then
		log "native OpenWrt LED configuration restored"
		return 0
	fi
	log "failed to restore native LED configuration"
	return 1
}

validate_time() {
	local t="$1" h m
	case "$t" in [0-9][0-9]:[0-9][0-9]) ;; *) return 1 ;; esac
	h="${t%:*}"; m="${t#*:}"
	h="${h#0}"; m="${m#0}"
	[ -n "$h" ] || h=0; [ -n "$m" ] || m=0
	[ "$h" -ge 0 ] 2>/dev/null || return 1
	[ "$h" -le 23 ] 2>/dev/null || return 1
	[ "$m" -ge 0 ] 2>/dev/null || return 1
	[ "$m" -le 59 ] 2>/dev/null || return 1
	return 0
}

time_parts() {
	local t="$1" h m
	h="${t%:*}"; m="${t#*:}"
	h="${h#0}"; m="${m#0}"
	[ -n "$h" ] || h=0; [ -n "$m" ] || m=0
	printf '%s %s\n' "$h" "$m"
}

strip_cron_block() {
	local tmp="/tmp/led-scheduler-cron.$$"
	mkdir -p "$(dirname "$CRON_FILE")"
	touch "$CRON_FILE"
	awk -v begin="$CRON_BEGIN" -v end="$CRON_END" '
		$0 == begin { skip=1; next }
		$0 == end   { skip=0; next }
		!skip       { print }
	' "$CRON_FILE" > "$tmp"
	cat "$tmp" > "$CRON_FILE"
	rm -f "$tmp"
}

reload_cron() { [ -x /etc/init.d/cron ] && /etc/init.d/cron restart >/dev/null 2>&1; }

clear_cron() {
	strip_cron_block
	reload_cron
	log "cron entries removed"
}

sync_cron() {
	local enabled off_time on_time off_h off_m on_h on_m
	enabled="$(uci_get enabled)"; [ -n "$enabled" ] || enabled="0"
	off_time="$(uci_get off_time)"; on_time="$(uci_get on_time)"
	[ -n "$off_time" ] || off_time="23:00"
	[ -n "$on_time" ] || on_time="07:00"
	strip_cron_block
	if [ "$enabled" != "1" ]; then reload_cron; log "scheduler disabled; cron entries removed"; return 0; fi
	validate_time "$off_time" || { echo "Invalid off_time: $off_time" >&2; return 1; }
	validate_time "$on_time" || { echo "Invalid on_time: $on_time" >&2; return 1; }
	set -- $(time_parts "$off_time"); off_h="$1"; off_m="$2"
	set -- $(time_parts "$on_time"); on_h="$1"; on_m="$2"
	{
		printf '%s\n' "$CRON_BEGIN"
		printf '%s %s * * * /usr/bin/led-control off\n' "$off_m" "$off_h"
		printf '%s %s * * * /usr/bin/led-control on\n' "$on_m" "$on_h"
		printf '%s\n' "$CRON_END"
	} >> "$CRON_FILE"
	reload_cron
	log "cron synchronized: off=${off_time}, on=${on_time}"
}

minutes_from_time() {
	local t="$1" h m
	set -- $(time_parts "$t"); h="$1"; m="$2"
	echo $((h * 60 + m))
}

apply_current_state() {
	local enabled off_time on_time now_h now_m now off on
	enabled="$(uci_get enabled)"
	if [ "$enabled" != "1" ]; then turn_on; return $?; fi
	off_time="$(uci_get off_time)"; on_time="$(uci_get on_time)"
	[ -n "$off_time" ] || off_time="23:00"
	[ -n "$on_time" ] || on_time="07:00"
	validate_time "$off_time" || return 1
	validate_time "$on_time" || return 1
	off="$(minutes_from_time "$off_time")"; on="$(minutes_from_time "$on_time")"
	if [ "$off" -eq "$on" ]; then turn_on; return $?; fi
	now_h="$(date +%H)"; now_m="$(date +%M)"
	now_h="${now_h#0}"; now_m="${now_m#0}"
	[ -n "$now_h" ] || now_h=0; [ -n "$now_m" ] || now_m=0
	now=$((now_h * 60 + now_m))
	if [ "$off" -lt "$on" ]; then
		if [ "$now" -ge "$off" ] && [ "$now" -lt "$on" ]; then turn_off; else turn_on; fi
	else
		if [ "$now" -ge "$off" ] || [ "$now" -lt "$on" ]; then turn_off; else turn_on; fi
	fi
}

list_configured() {
	local section name sysfs trigger
	uci -q show system 2>/dev/null |
		sed -n 's/^system\.\([^=]*\)=led$/\1/p' |
		while IFS= read -r section; do
			name="$(uci -q get "system.${section}.name" 2>/dev/null)"
			sysfs="$(uci -q get "system.${section}.sysfs" 2>/dev/null)"
			trigger="$(uci -q get "system.${section}.trigger" 2>/dev/null)"
			[ -n "$sysfs" ] || continue
			printf '%s	%s	%s\n' "${name:-$sysfs}" "$sysfs" "${trigger:-unknown}"
		done
}

status() {
	echo "enabled=$(uci_get enabled)"
	echo "off_time=$(uci_get off_time)"
	echo "on_time=$(uci_get on_time)"
	echo "all_system_leds=$(uci_get all_system_leds)"
	echo "selected sysfs LEDs:"
	selected_leds | sed 's/^/  - /'
}

usage() {
	cat <<'EOF'
Usage: led-control COMMAND

Commands:
  list        List LEDs defined by OpenWrt System -> LED Configuration
  selected    List LEDs currently selected by LED Scheduler
  off         Turn selected LEDs off
  on          Restore OpenWrt native LED configuration
  apply       Apply the correct state for the current time
  sync        Synchronize cron entries from UCI configuration
  clear-cron  Remove this application's cron entries
  status      Show scheduler configuration
EOF
}

case "$1" in
	list) list_configured ;;
	selected) selected_leds ;;
	off) turn_off ;;
	on) turn_on ;;
	apply) apply_current_state ;;
	sync) sync_cron ;;
	clear-cron) clear_cron ;;
	status) status ;;
	*) usage; exit 1 ;;
esac
