#!/bin/sh

BASE="/mnt/onboard/.adds/kobo-sideload-sync"
BIN="$BASE/bin/kobo-receiver"
RUN_DIR="$BASE/run"
PID_FILE="$RUN_DIR/receiver.pid"
STATUS_FILE="$RUN_DIR/status.txt"
SESSION_FILE="$RUN_DIR/session.json"
PAIRING_IMAGE_FILE="$RUN_DIR/pairing.png"
LEASE_FILE="$RUN_DIR/forcewifi-lease.json"
FLOW_FILE="$RUN_DIR/open-flow.id"
STOP_WAIT_TENTHS="${KSS_STOP_WAIT_TENTHS:-50}"
STRICT_STOP="${KSS_STRICT_STOP:-0}"
REFUSE_ACTIVE="${KSS_REFUSE_ACTIVE:-0}"

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

cancel_flow_after_safe_stop() {
    if [ -e "$FLOW_FILE" ] && ! rm -f "$FLOW_FILE"; then
        kss_timing_event stop_flow_cancel_fail
        echo "Receiver 已停止，但无法取消旧的二维码流程；请重试完成操作。"
        return 1
    fi
    if [ -e "$FLOW_FILE" ]; then
        kss_timing_event stop_flow_cancel_fail
        echo "Receiver 已停止，但旧的二维码流程仍然存在；请重试完成操作。"
        return 1
    fi
    kss_timing_event stop_flow_cancel_ok
    return 0
}

refuse_unfinished_staging() {
    [ "$REFUSE_ACTIVE" = "1" ] || return 0

    # Upload bodies are written as hidden .part files in the staging root,
    # before their final rename into a per-session directory. Check this even
    # when the PID file is absent or points to a process which has already died.
    for part_file in "$BASE"/staging/.*.part; do
        [ -e "$part_file" ] || continue
        kss_timing_event stop_refused_root_part
        echo "Receiver 仍有正在写入或异常遗留的文件；没有结束会话。请连接 USB 保留现场后检查。"
        return 1
    done

    # A crashed Receiver can leave complete files in an old ticket directory.
    # Empty ticket directories are harmless; any file or unreadable directory
    # is evidence which normal finish must not silently abandon.
    for staged_path in "$BASE"/staging/*; do
        [ -e "$staged_path" ] || continue
        if [ ! -d "$staged_path" ]; then
            kss_timing_event stop_refused_staging_nonempty
            echo "Receiver 暂存区存在异常遗留文件；没有结束会话。请连接 USB 保留现场后检查。"
            return 1
        fi
        staged_listing="$(ls -A "$staged_path" 2>/dev/null)"
        staged_result=$?
        if [ "$staged_result" -ne 0 ]; then
            kss_timing_event stop_refused_staging_read
            echo "Receiver 暂存目录无法读取；没有结束会话。请连接 USB 保留现场后检查。"
            return 1
        fi
        if [ -n "$staged_listing" ]; then
            kss_timing_event stop_refused_staging_nonempty
            echo "本批仍有尚未发布的暂存文件；没有结束会话。请等待网页完成，或使用高级关闭明确放弃本批。"
            return 1
        fi
    done
    return 0
}

kss_timing_event stop_begin

case "$STOP_WAIT_TENTHS" in
    ''|*[!0-9]*) STOP_WAIT_TENTHS=50 ;;
esac

# The safe finish path must inspect crash residue before either early success
# return below. The advanced close path deliberately leaves REFUSE_ACTIVE=0.
refuse_unfinished_staging || exit 1

if [ -L "$PID_FILE" ] || { [ -e "$PID_FILE" ] && { [ ! -f "$PID_FILE" ] || [ ! -r "$PID_FILE" ] || [ ! -s "$PID_FILE" ]; }; }; then
    kss_timing_event stop_refused_pid
    echo "PID 文件格式无效或无法安全读取（为空、非普通文件或不可读），已拒绝结束任何进程。请保留状态文件，通过 USB 检查。"
    exit 1
fi

if [ ! -e "$PID_FILE" ]; then
	rm -f "$STATUS_FILE" "$SESSION_FILE" "$PAIRING_IMAGE_FILE"
    kss_timing_event stop_no_pid
    cancel_flow_after_safe_stop || exit 1
    if [ -e "$LEASE_FILE" ]; then
        echo "文件接收没有运行，但本次 Wi-Fi 保活仍需恢复。请点击“完成传书并导入”。"
    else
        echo "无线传书没有运行。"
    fi
    exit 0
fi

pid="$(sed -n '1p' "$PID_FILE" 2>/dev/null)" || pid=""
case "$pid" in
    ''|*[!0-9]*)
        kss_timing_event stop_refused_pid
        echo "PID 文件格式无效，无法安全识别接收器进程；已拒绝结束任何进程。请保留状态文件，通过 USB 检查，不要重复开启无线传输。"
        exit 1
        ;;
esac

if ! kill -0 "$pid" 2>/dev/null; then
	rm -f "$PID_FILE" "$STATUS_FILE" "$SESSION_FILE" "$PAIRING_IMAGE_FILE"
    kss_timing_event stop_ok
    cancel_flow_after_safe_stop || exit 1
    if [ -e "$LEASE_FILE" ]; then
        echo "文件接收已经停止；请继续点击“完成传书并导入”恢复本次 Wi-Fi 保活。"
    else
        echo "无线传书已经停止。"
    fi
    exit 0
fi

actual="$(readlink "/proc/$pid/exe" 2>/dev/null)"
if [ "$actual" != "$BIN" ] && [ "$actual" != "$BIN (deleted)" ]; then
    kss_timing_event stop_refused_pid
    echo "无法确认 PID 对应的进程属于本项目，已拒绝结束进程。请保留状态文件，通过 USB 检查，不要重复开启无线传输。"
    exit 1
fi

if [ "$REFUSE_ACTIVE" = "1" ]; then
    active="$(sed -n 's/^ACTIVE_OPERATION=//p' "$STATUS_FILE" 2>/dev/null)"
    if [ "$active" != "false" ]; then
        kss_timing_event stop_refused_active
        echo "Receiver 正在接收、发布或状态尚未确认；没有中断文件。请等待页面结束后重试。"
        exit 1
    fi
    upload_dir="$(sed -n 's/^UPLOAD_DIR=//p' "$STATUS_FILE" 2>/dev/null)"
    case "$upload_dir" in
        "$BASE"/staging/*) ;;
        *)
            kss_timing_event stop_refused_upload_dir
            echo "Receiver 暂存目录状态无法确认；没有结束会话。请查看状态后重试。"
            exit 1
            ;;
    esac
    # Recheck immediately before signalling to narrow the race with a new
    # upload starting after the early crash-residue scan.
    refuse_unfinished_staging || exit 1
fi

if [ "$REFUSE_ACTIVE" = "1" ] && grep -q '^KEEP_RECEIVING=true$' "$STATUS_FILE"; then
    # Receiver serializes this request with upload admission. Never fall back
    # to a signal if the atomic close is refused or its response is uncertain.
    if ! "$BIN" --request-stop --session-file="$SESSION_FILE"; then
        kss_timing_event stop_admission_refused
        exit 1
    fi
    kss_timing_event stop_admission_closed
else
kill "$pid" 2>/dev/null || {
    kss_timing_event stop_signal_fail
    echo "无法请求 Receiver 停止。"
    exit 1
}
fi
kss_timing_event stop_signal_sent

attempt=0
kss_timing_event stop_wait_begin
while kill -0 "$pid" 2>/dev/null && [ "$attempt" -lt "$STOP_WAIT_TENTHS" ]; do
    attempt=$((attempt + 1))
    sleep 0.1
done
kss_timing_event stop_wait_end

if kill -0 "$pid" 2>/dev/null; then
    kss_timing_event stop_still_running
    echo "Receiver 正在安全收尾；没有强制结束。请稍后查看状态。"
    if [ "$STRICT_STOP" = "1" ]; then
        exit 1
    fi
    exit 0
fi

rm -f "$PID_FILE" "$STATUS_FILE" "$SESSION_FILE" "$PAIRING_IMAGE_FILE"
kss_timing_event stop_ok
cancel_flow_after_safe_stop || exit 1
if [ -e "$LEASE_FILE" ]; then
    echo "文件接收已安全关闭；请再点击“完成传书并导入”恢复本次 Wi-Fi 保活。"
else
    echo "无线传书已安全关闭。"
fi
