#!/bin/sh

BASE="/mnt/onboard/.adds/kobo-sideload-sync"
BIN="$BASE/bin/kobo-receiver"
CONFIG_FILE="/mnt/onboard/.kobo/Kobo/Kobo eReader.conf"
LEASE_FILE="$BASE/run/forcewifi-lease.json"
RUN_DIR="$BASE/run"
FLOW_FILE="$RUN_DIR/open-flow.id"
TRACE_FLOW_FILE="$RUN_DIR/trace-flow.id"

if [ -r "$BASE/action-timing.sh" ]; then
    . "$BASE/action-timing.sh"
else
    kss_timing_event() { :; }
fi

run_lease() {
    "$BIN" \
        --force-wifi-lease-action="$1" \
        --force-wifi-config-file="$CONFIG_FILE" \
        --force-wifi-lease-file="$LEASE_FILE"
}

recover_receiver_update() {
    update_dir="$BASE/updates/active"
    marker="$update_dir/needs-recovery"
    [ -e "$marker" ] || [ -L "$marker" ] || return 0
    for directory in "$BASE/updates" "$update_dir" "$RUN_DIR"; do
        [ ! -L "$directory" ] && [ -d "$directory" ] || return 1
    done
    for update_file in "$marker" "$update_dir/old-0" "$update_dir/journal.json"; do
        [ ! -L "$update_file" ] && [ -f "$update_file" ] && [ -r "$update_file" ] || return 1
    done
    if [ -e "$RUN_DIR/receiver.pid" ] || [ -L "$RUN_DIR/receiver.pid" ]; then
        [ ! -L "$RUN_DIR/receiver.pid" ] && [ -f "$RUN_DIR/receiver.pid" ] && [ -s "$RUN_DIR/receiver.pid" ] || return 1
        update_pid="$(sed -n '1p' "$RUN_DIR/receiver.pid" 2>/dev/null)" || return 1
        case "$update_pid" in ''|*[!0-9]*) return 1 ;; esac
        if kill -0 "$update_pid" 2>/dev/null; then
            # A live update coordinator/candidate owns recovery. Normal lease
            # logic still refuses active transfers through private control.
            update_exe="$(readlink "/proc/$update_pid/exe" 2>/dev/null)"
            [ "$update_exe" = "$BIN" ] || [ "$update_exe" = "$BIN (deleted)" ] || return 1
            return 0
        fi
    fi
    [ "$(wc -c < "$marker" 2>/dev/null)" = "65" ] || return 1
    expected_update_hash="$(sed -n '1p' "$marker" 2>/dev/null)" || return 1
    case "$expected_update_hash" in ''|*[!a-f0-9]*) return 1 ;; esac
    [ "${#expected_update_hash}" -eq 64 ] || return 1
    command -v sha256sum >/dev/null 2>&1 || return 1
    actual_update_hash="$(sha256sum "$update_dir/old-0" 2>/dev/null)" || return 1
    actual_update_hash="${actual_update_hash%% *}"
    [ "$actual_update_hash" = "$expected_update_hash" ] || return 1
    "$update_dir/old-0" --recover-self-update
}

# NickelMenu discards stdout on a nonzero command result. This separate action
# describes only conditions observable NOW; it neither retries stop nor treats
# a cached lease/status as proof of a completed finish. No cross-action record
# is written to the onboard volume or /tmp.
describe_current_stop_failure() {
    for part_file in "$BASE"/staging/.*.part; do
        [ -e "$part_file" ] || continue
        echo "当前检查发现：暂存区仍有正在写入或异常遗留的文件。"
        return 0
    done
    for staged_path in "$BASE"/staging/*; do
        [ -e "$staged_path" ] || continue
        if [ ! -d "$staged_path" ]; then
            echo "当前检查发现：暂存区存在异常遗留文件。"
            return 0
        fi
        if ! staged_listing="$(ls -A "$staged_path" 2>/dev/null)"; then
            echo "当前检查发现：暂存目录无法读取。"
            return 0
        fi
        if [ -n "$staged_listing" ]; then
            echo "当前检查发现：暂存区仍有尚未发布的文件。"
            return 0
        fi
    done

    failure_pid_file="$RUN_DIR/receiver.pid"
    if [ -L "$failure_pid_file" ] || { [ -e "$failure_pid_file" ] && { [ ! -f "$failure_pid_file" ] || [ ! -r "$failure_pid_file" ] || [ ! -s "$failure_pid_file" ]; }; }; then
        echo "当前检查发现：PID 文件格式无效或无法安全读取（为空、非普通文件或不可读）。"
        return 0
    fi
    if [ -e "$failure_pid_file" ]; then
        failure_pid="$(sed -n '1p' "$failure_pid_file" 2>/dev/null)" || failure_pid=""
        case "$failure_pid" in
            ''|*[!0-9]*)
                echo "当前检查发现：PID 文件格式无效，无法安全识别接收器进程。"
                return 0
                ;;
        esac
        if kill -0 "$failure_pid" 2>/dev/null; then
            failure_exe="$(readlink "/proc/$failure_pid/exe" 2>/dev/null)"
            if [ "$failure_exe" != "$BIN" ] && [ "$failure_exe" != "$BIN (deleted)" ]; then
                echo "当前检查发现：无法确认 PID 对应的进程属于本项目。"
                return 0
            fi
            failure_active=""
            if [ ! -L "$RUN_DIR/status.txt" ] && [ -f "$RUN_DIR/status.txt" ] && [ -r "$RUN_DIR/status.txt" ]; then
                failure_active="$(sed -n 's/^ACTIVE_OPERATION=//p' "$RUN_DIR/status.txt" 2>/dev/null)" || failure_active=""
            fi
            if [ "$failure_active" != "false" ]; then
                echo "当前状态记录显示仍有接收或发布操作，或活动状态尚未确认。"
                return 0
            fi
        fi
    fi
    echo "安全结束尚未确认；当前只读检查未确定失败原因，不能据此认定仍在传输或已经结束。"
}

new_flow_id() {
    flow_uuid=""
    if [ -r /proc/sys/kernel/random/uuid ]; then
        IFS= read -r flow_uuid 2>/dev/null < /proc/sys/kernel/random/uuid || flow_uuid=""
    fi
    flow_uuid="${flow_uuid%%-*}"
    case "$flow_uuid" in
        ????????) printf 'f%s\n' "$flow_uuid"; return 0 ;;
    esac

    flow_uptime=""
    if IFS=' ' read -r flow_uptime flow_idle 2>/dev/null < /proc/uptime; then
        flow_uptime="${flow_uptime%%.*}"
    fi
    case "$flow_uptime" in
        ''|*[!0-9]*) flow_uptime=0 ;;
    esac
    printf 'f%s-%s\n' "$flow_uptime" "$$"
}

store_flow_file() {
    flow_path="$1"
    flow_value="$2"
    flow_tmp="$flow_path.tmp.$$"
    if ! (umask 077; printf '%s\n' "$flow_value" > "$flow_tmp"); then
        rm -f "$flow_tmp"
        return 1
    fi
    if ! mv -f "$flow_tmp" "$flow_path"; then
        rm -f "$flow_tmp"
        return 1
    fi
    return 0
}

store_flow_id() {
    flow_value="$1"
    kss_flow_id_is_valid "$flow_value" || return 1
    mkdir -p "$RUN_DIR" || return 1
    if ! store_flow_file "$FLOW_FILE" "$flow_value"; then
        return 1
    fi
    if ! store_flow_file "$TRACE_FLOW_FILE" "$flow_value"; then
        rm -f "$FLOW_FILE" "$TRACE_FLOW_FILE"
        return 1
    fi
    return 0
}

# A clean idle device is not a failed finish. Probe BEFORE stop/lease recovery:
# absence after a failed stop could hide a missing recovery record. Read-only,
# no cleanup, no process signals, no Wi-Fi changes, no library scan.
finish_not_needed() {
    [ ! -L "$BASE" ] && [ -d "$BASE" ] && [ -r "$BASE" ] && [ -x "$BASE" ] || return 1
    for idle_dir in "$RUN_DIR" "$BASE/staging"; do
        [ ! -L "$idle_dir" ] || return 1
        if [ -e "$idle_dir" ]; then
            [ -d "$idle_dir" ] && [ -r "$idle_dir" ] && [ -x "$idle_dir" ] || return 1
        fi
    done
    for idle_file in "$RUN_DIR/receiver.pid" "$RUN_DIR/session.json" "$RUN_DIR/status.txt" "$LEASE_FILE" "$FLOW_FILE"; do
        [ ! -e "$idle_file" ] && [ ! -L "$idle_file" ] || return 1
    done
    if [ -d "$BASE/staging" ]; then
        ls -A "$BASE/staging" >/dev/null 2>&1 || return 1
        for idle_item in "$BASE/staging"/* "$BASE/staging"/.[!.]* "$BASE/staging"/..?*; do
            [ -e "$idle_item" ] || { [ ! -L "$idle_item" ] || return 1; continue; }
            [ ! -L "$idle_item" ] && [ -d "$idle_item" ] || return 1
            idle_contents="$(ls -A "$idle_item" 2>/dev/null)" || return 1
            [ -z "$idle_contents" ] || return 1
        done
    fi
    return 0
}

action="${1:-show}"
if [ "$action" = "recover-update" ]; then
    recover_receiver_update
    exit $?
fi
if [ "$action" = "prepare" ]; then
    # The old helper remains usable even if the replaced BIN is missing/broken.
    # Read-only status actions never initiate recovery writes.
    if ! recover_receiver_update; then
        echo "接收器更新恢复尚未完成，请保留记录并通过 USB 检查。"
        exit 1
    fi
fi
if [ "$action" = "finish-not-needed" ]; then
    finish_not_needed
    exit $?
fi
if [ "$action" = "show-stop-failure" ]; then
    echo "结束无线传输未完成；本次没有继续执行 Wi-Fi 恢复或更新书库。"
    describe_current_stop_failure
    echo "这是当前检查结果，不一定是刚才失败的原因。请保留状态文件并反馈诊断，勿删除文件绕过检查或重复开启无线传输。"
    exit 0
fi
if [ ! -x "$BIN" ]; then
    if [ "$action" = "show" ]; then
        echo "Receiver 程序不存在，无法读取本次 Wi-Fi 恢复记录。请通过 USB 重新安装。"
        exit 0
    fi
    echo "Receiver 程序不存在。"
    exit 1
fi

case "$action" in
    prepare)
        if [ -f "$BASE/universal-candidate.txt" ]; then
            /bin/sh "$BASE/device-preflight.sh" || exit 1
        fi
        KSS_FLOW_ID="$(new_flow_id)" || KSS_FLOW_ID=""
        export KSS_FLOW_ID
        kss_timing_event open_prepare_begin
        run_lease prepare
        result=$?
        if [ "$result" -eq 0 ]; then
            kss_timing_event open_flow_store_begin
            if store_flow_id "$KSS_FLOW_ID"; then
                kss_timing_event open_flow_store_ok
                kss_timing_event open_prepare_ok
            else
                kss_timing_event open_flow_store_fail
                kss_timing_event open_prepare_fail
                echo "无法记录本次开启流程；Wi-Fi 尚未改变。请先完成传书并导入后再重试。"
                result=1
            fi
        else
            kss_timing_event open_prepare_fail
        fi
        exit "$result"
        ;;
    mark-active)
        kss_timing_event open_mark_active_begin
        run_lease mark-active
        result=$?
        if [ "$result" -eq 0 ]; then
            kss_timing_event open_mark_active_ok
        else
            kss_timing_event open_mark_active_fail
        fi
        exit "$result"
        ;;
    begin-finish)
        kss_timing_event finish_begin_restore_begin
        run_lease begin-finish
        result=$?
        if [ "$result" -eq 0 ]; then
            kss_timing_event finish_begin_restore_ok
        else
            kss_timing_event finish_begin_restore_fail
        fi
        exit "$result"
        ;;
    original-is-true)
        kss_timing_event restore_probe_true_begin
        run_lease original-is-true
        result=$?
        case "$result" in
            0) kss_timing_event restore_probe_true_yes ;;
            1) kss_timing_event restore_probe_true_no ;;
            *) kss_timing_event restore_probe_true_fail ;;
        esac
        exit "$result"
        ;;
    original-is-false)
        kss_timing_event restore_probe_false_begin
        run_lease original-is-false
        result=$?
        case "$result" in
            0) kss_timing_event restore_probe_false_yes ;;
            1) kss_timing_event restore_probe_false_no ;;
            *) kss_timing_event restore_probe_false_fail ;;
        esac
        exit "$result"
        ;;
    finish-prepare)
        kss_timing_event finish_prepare_begin
        # Pairing withdrawal can take 5s. Allow one extra second for process
        # exit; polling still returns immediately when shutdown is complete.
        # Keep this attempt's stop messages in memory, not another file
        # on the shared volume. NickelMenu runs this action quietly and uses a
        # separate read-only display action after failure; keep the true result.
        stop_output="$(KSS_STOP_WAIT_TENTHS=60 KSS_STRICT_STOP=1 KSS_REFUSE_ACTIVE=1 /bin/sh "$BASE/stop.sh" 2>&1)"
        stop_result=$?
        if [ "$stop_result" -ne 0 ]; then
            kss_timing_event finish_stop_fail
            echo "结束无线传输未完成："
            if [ -n "$stop_output" ]; then
                printf '%s\n' "$stop_output"
            else
                echo "无法确认接收器已安全停止，请保留现场并反馈诊断。"
            fi
            echo "本次未继续恢复 Wi-Fi 设置或更新书库；请勿删除状态文件绕过检查。"
            exit "$stop_result"
        fi
        kss_timing_event finish_stop_ok
        kss_timing_event finish_begin_restore_begin
        "$BIN" \
            --force-wifi-lease-action=begin-finish \
            --force-wifi-config-file="$CONFIG_FILE" \
            --force-wifi-lease-file="$LEASE_FILE"
        result=$?
        if [ "$result" -eq 0 ]; then
            kss_timing_event finish_begin_restore_ok
        else
            kss_timing_event finish_begin_restore_fail
            echo "接收器已停止，但无法准备 Wi-Fi 恢复；尚未继续改变 Wi-Fi 设置或更新书库。请保留恢复记录并反馈诊断。"
        fi
        exit "$result"
        ;;
    finalize)
        kss_timing_event finish_finalize_begin
        if ! run_lease finalize; then
            kss_timing_event finish_finalize_fail
            exit 1
        fi
        kss_timing_event finish_finalize_ok
        rm -f "$TRACE_FLOW_FILE" 2>/dev/null || :
        echo "本次 Wi-Fi 保活已经恢复，接下来由 Nickel 更新书库。"
        ;;
    import-failed)
        if [ -f "$BASE/universal-candidate.txt" ]; then
            echo "Wi-Fi 保活已经恢复，但本次更新书库请求失败。此包没有单独重扫菜单；请保留已传文件并反馈诊断，不要重复传书或重新开启无线传输。"
        else
            echo "Wi-Fi 保活已经恢复，但本次更新书库请求失败。请稍后点击“无线传书（高级）· 仅更新书库”；请求成功不代表所有书籍已经入库，请回书库核对。"
        fi
        exit 0
        ;;
    show)
        if [ -f "$BASE/universal-candidate.txt" ]; then
            /bin/sh "$BASE/device-preflight.sh" --basic || :
        fi
        # This is the last action in recovery/status failure chains. Keep useful
        # Chinese recovery text visible instead of NickelMenu's generic error.
        run_lease show 2>&1 || echo "无法读取本次 Wi-Fi 恢复记录。请通过 USB 检查。"
        exit 0
        ;;
    *)
        echo "未知的会话租约动作。"
        exit 1
        ;;
esac
