#!/usr/bin/env bash

# https://github.com/SHORiN-KiWATA/shorin-contrib
#
# niri-force-kill-window
# 通过鼠标点击强制结束 (SIGKILL) 任意窗口。
# 完美支持 Wayland 原生与 XWayland 代理窗口的独立精准击杀。
# 具备多语言自适应 (i18n) 与智能依赖提示功能。
#
# 实现原理：
# 1. 通过 niri msg pick-window 点选窗口
# 2. 利用窗口的 PID，通过进程名称判断它是 xwayland 还是 wayland
# 3. 获取真实的底层 PID（Wayland 直接取用，XWayland 利用 xprop 获取焦点窗口的真实 PID）
# 4. 向上追溯该真实 PID，找到所属应用的根进程 (App Root)，并利用防火墙逻辑避开系统进程
# 5. 从根进程向下遍历收集所有子孙进程，利用 kill -9 执行“九头蛇绞杀”，防止多进程软件死灰复燃

# ==========================================
# 0. 多语言 (Locale) 自适应支持
# ==========================================
if [[ "${LANG}" == zh_* ]]; then
    STR_ERR_DEP_TITLE="依赖缺失"
    STR_ERR_DEP_MSG="缺少必需命令: %s\n请尝试安装包: %s"
    STR_ERR_INFO_TITLE="获取信息失败"
    STR_ERR_INFO_MSG="无法获取窗口或进程信息。"
    STR_ERR_KILL_TITLE="结束失败"
    STR_ERR_KILL_MSG="无法提取目标真实 PID。"
    STR_SUCC_TITLE="强制杀死 (%s)"
    STR_SUCC_MSG="应用: %s\n连根拔起: 成功摧毁 %s 个相关进程。"
    STR_UNKNOWN_APP="未知应用"
else
    STR_ERR_DEP_TITLE="Missing Dependency"
    STR_ERR_DEP_MSG="Command not found: %s\nPlease install package: %s"
    STR_ERR_INFO_TITLE="Info Error"
    STR_ERR_INFO_MSG="Could not determine window or process information."
    STR_ERR_KILL_TITLE="Kill Failed"
    STR_ERR_KILL_MSG="Could not extract target real PID."
    STR_SUCC_TITLE="Headshot! (%s)"
    STR_SUCC_MSG="App: %s\nHydra Kill: %s processes terminated."
    STR_UNKNOWN_APP="Unknown App"
fi

# ==========================================
# 1. 环境与依赖检查
# ==========================================
check_dependency() {
    local cmd="$1"
    local pkg="$2"
    if ! command -v "$cmd" &> /dev/null; then
        local msg
        printf -v msg "$STR_ERR_DEP_MSG" "$cmd" "$pkg"

        # 如果 notify-send 存在，则发送桌面通知；否则输出到终端标准错误
        if command -v notify-send &> /dev/null; then
            notify-send "$STR_ERR_DEP_TITLE" "$msg" -u critical -i dialog-error
        else
            echo -e "[${STR_ERR_DEP_TITLE}]\n${msg}" >&2
        fi
        exit 1
    fi
}

# 检查三大核心依赖并提示对应软件包
check_dependency "niri" "niri"
check_dependency "notify-send" "libnotify"
check_dependency "xprop" "xorg-xprop"

# ==========================================
# 2. 辅助函数：发送通知与音效 (非阻塞)
# ==========================================
notify_and_play() {
    local title="$1"
    local msg="$2"
    notify-send "$title" "$msg" -a "Window Killer" -i application-exit

    # pw-play 不是强依赖，如果有则播放音效，放入后台运行防止阻塞
    if command -v pw-play &> /dev/null; then
        pw-play /usr/share/sounds/freedesktop/stereo/dialog-error.oga >/dev/null 2>&1 &
    fi
}

# ==========================================
# 3. 抓取目标窗口信息
# ==========================================
# 执行命令并捕获退出码。如果用户按 Esc 取消，返回非零退出码，静默退出
if ! output=$(niri msg pick-window 2>/dev/null); then
    exit 0
fi

# 如果没有任何输出，直接退出
if [[ -z "$output" ]]; then
    exit 0
fi

# 提取 Niri 视角下的 PID 和 App ID
pid=$(grep -oP 'PID:\s*\K\d+' <<< "$output")
app_id=$(grep -oP 'App ID:\s*"\K[^"]+' <<< "$output")
app_name="${app_id:-$STR_UNKNOWN_APP}"

# 如果正则没有提取到 PID（未命中合法窗口），静默退出
if [[ -z "$pid" ]]; then
    exit 0
fi

# 如果确实抓到了 PID，但此时进程已不存在，发出异常通知
if [[ ! -f "/proc/$pid/comm" ]]; then
    notify-send "$STR_ERR_INFO_TITLE" "$STR_ERR_INFO_MSG" -a "Window Killer" -i dialog-error
    exit 1
fi

# ==========================================
# 4. 判定协议类型并获取真实 PID
# ==========================================
process_name=$(cat "/proc/$pid/comm")
process_name_lower="${process_name,,}"

if [[ "$process_name_lower" == *"xwayland"* ]]; then
    proto_str="XWayland"
    # 给内核与 X11 服务端预留 50 毫秒的时间传递和同步焦点
    sleep 0.05
    # 顺着焦点，询问 X11 当前活动的窗口 ID
    active_wid=$(xprop -root -notype _NET_ACTIVE_WINDOW 2>/dev/null | grep -o '0x[0-9a-fA-F]\+')
    # 顺藤摸瓜：提取这个 X11 窗口绑定的真实底层 Linux PID
    real_pid=$(xprop -id "$active_wid" -notype _NET_WM_PID 2>/dev/null | grep -oP '\d+')
else
    proto_str="Wayland"
    real_pid="$pid"
fi

if [[ -z "$real_pid" ]]; then
    notify-send "$STR_ERR_KILL_TITLE" "$STR_ERR_KILL_MSG" -a "Window Killer" -i dialog-error
    exit 1
fi

# ==========================================
# 5. 九头蛇绞杀逻辑 (Hydra Kill)
# ==========================================
# 向上追溯，寻找进程家族的老祖宗 (App Root)
app_root=$real_pid
current=$real_pid

while true; do
    ppid=$(ps -o ppid= -p "$current" 2>/dev/null | tr -d ' ')

    # 如果找不到父进程，或者父进程是系统最高层 PID 1，停止追溯
    if [[ -z "$ppid" || "$ppid" == "1" ]]; then
        break
    fi

    pname=$(ps -o comm= -p "$ppid" 2>/dev/null)

    # 【核心防火墙】：遇到桌面环境、终端、系统核心服务，立刻停止溯源！
    if [[ "$pname" =~ ^(systemd|niri|bash|zsh|fish|tmux|screen|xwayland.*|sshd|login|init|sway|hyprland)$ ]]; then
        break
    fi

    app_root=$ppid
    current=$ppid
done

# 向下递归，收集家族所有子孙 PID
get_descendants() {
    local p=$1
    echo "$p"
    # pgrep -P 获取直接子进程
    for c in $(pgrep -P "$p" 2>/dev/null); do
        get_descendants "$c"
    done
}

family_pids=$(get_descendants "$app_root")
pid_count=$(echo "$family_pids" | wc -w)

# 执行联合绞杀：把所有收集到的 PID 一次性全部强制终止
kill -9 $family_pids 2>/dev/null

# ==========================================
# 6. 发送战果通知与音效
# ==========================================
printf -v final_title "$STR_SUCC_TITLE" "$proto_str"
printf -v final_msg "$STR_SUCC_MSG" "$app_name" "$pid_count"

notify_and_play "$final_title" "$final_msg"
