#!/usr/bin/env bash
set -euo pipefail

cache="${XDG_CACHE_HOME:-$HOME/.cache}/pwnbox/theme"
wal_out="${XDG_CACHE_HOME:-$HOME/.cache}/wal"
mono="$HOME/.config/wal/monochrome"
wall_dir="$HOME/.local/share/pwnbox/wallpapers"
themes_dir="${XDG_CONFIG_HOME:-$HOME/.config}/pwnbox/themes"
state="$cache/wallpaper"
custom_state="$cache/custom-theme"
style_state="$cache/barstyle"
default_wall="$HOME/.config/hypr/wall.png"
picker_cfg="$HOME/.config/rofi/picker.rasi"
barstyle_cfg="$HOME/.config/rofi/barstyle.rasi"
menu_cfg="$HOME/.config/rofi/menu.rasi"
custom_cfg="$HOME/.config/rofi/custom.rasi"
previews_dir="$HOME/.config/quickshell/previews"
thumb_dir="$cache/thumbs"
radius_state="$cache/radius-window"
uir_state="$cache/radius-ui"
blur_file="$cache/blur"
opacity_file="$cache/opacity"
anim_file="$cache/animations"
font_ui_state="$cache/font-ui"
font_app_state="$cache/font-app"
font_ui_size_state="$cache/font-size-ui"
font_app_size_state="$cache/font-size-app"

font_choices=(
    "Manrope"
    "Fairfax HD"
    "Fairfax Hax HD"
    "Fairfax Pona HD"
    "Fairfax SM HD"
    "Ubuntu"
    "Ubuntu Mono"
    "Berkeley Mono"
    "Spleen 6x12"
    "Spleen 8x16"
    "Spleen 12x24"
    "Spleen 16x32"
    "Terminus (TTF)"
    "Iosevka"
    "Iosevka Nerd Font"
    "Iosevka Nerd Font Mono"
    "Iosevka Nerd Font Propo"
    "JetBrains Mono"
    "JetBrainsMono Nerd Font"
    "JetBrainsMono Nerd Font Mono"
    "JetBrainsMono Nerd Font Propo"
    "MesloLGS Nerd Font"
    "MesloLGS Nerd Font Mono"
    "MesloLGS Nerd Font Propo"
    "FiraCode Nerd Font"
    "FiraCode Nerd Font Mono"
    "FiraCode Nerd Font Propo"
    "Inconsolata"
    "Noto Sans"
    "Noto Sans Mono"
    "Noto Serif"
    "DejaVu Sans"
    "DejaVu Sans Mono"
    "DejaVu Serif"
    "Liberation Sans"
    "Liberation Mono"
    "Liberation Serif"
)

notify() {
    command -v notify-send >/dev/null 2>&1 && notify-send -a pwnbox-theme "$1" "${2:-}" >/dev/null 2>&1 || true
}

cp_atomic() {
    local src="$1" dst="$2" tmp
    [ -e "$src" ] || return 0
    tmp="$(dirname -- "$dst")/.$(basename -- "$dst").pwnbox-tmp"
    if cp -f "$src" "$tmp" 2>/dev/null; then
        mv -f "$tmp" "$dst" 2>/dev/null || rm -f "$tmp"
    else
        rm -f "$tmp"
    fi
}

read_state() {
    local file="$1" fallback="$2" value
    value="$(cat "$file" 2>/dev/null || true)"
    [ -n "$value" ] || value="$fallback"
    printf '%s' "$value"
}

win_radius() { read_state "$radius_state" 9; }
ui_radius() { read_state "$uir_state" 9; }
blur_state() { read_state "$blur_file" on; }
blur_label() { [ "$(blur_state)" = "off" ] && printf 'Off' || printf 'On'; }
blur_hypr() { [ "$(blur_state)" = "off" ] && printf 'false' || printf 'true'; }
opacity() { read_state "$opacity_file" 100; }
opacity_label() { printf '%s%%' "$(opacity)"; }
opacity_value() {
    local o
    o="$(opacity)"
    printf '%d.%02d' "$((o / 100))" "$((o % 100))"
}
anim_state() { read_state "$anim_file" on; }
anim_label() { [ "$(anim_state)" = "off" ] && printf 'Off' || printf 'On'; }
anim_hypr() { [ "$(anim_state)" = "off" ] && printf 'false' || printf 'true'; }
ui_font() { read_state "$font_ui_state" "Manrope"; }
app_font() { read_state "$font_app_state" "Berkeley Mono"; }
ui_font_size() { read_state "$font_ui_size_state" 11; }
app_font_size() { read_state "$font_app_size_state" 9; }

theme_color() {
    local key="$1" value
    value="$(sed -nE "s/^var ${key}[[:space:]]*=[[:space:]]*\"#?([0-9a-fA-F]{6})\".*/\1/p" \
        "$HOME/.config/quickshell/Theme.js" 2>/dev/null | head -1)"
    [ -n "$value" ] && printf '%s' "$value" || printf '8a8a8a'
}

apply_ui_radius() {
    local r js rofi
    r="$(ui_radius)"
    js="$HOME/.config/quickshell/Theme.js"
    if [ -f "$js" ]; then
        sed -i -E "s/^var radius([[:space:]]*)=.*/var radius      = ${r};/" "$js" 2>/dev/null || true
    fi
    rofi="$cache/rofi.rasi"
    if [ -f "$rofi" ]; then
        sed -i -E '/^[[:space:]]*radius:[[:space:]]*/d' "$rofi" 2>/dev/null || true
        sed -i -E "0,/^[[:space:]]*\*[[:space:]]*\{/s//&\n    radius: ${r}px;/" "$rofi" 2>/dev/null || true
    fi

    apply_transparency
}

apply_transparency() {
    local js rofi o a aa bg bgalt key val
    js="$HOME/.config/quickshell/Theme.js"
    [ -f "$js" ] || return 0
    o="$(opacity)"
    case "$o" in '' | *[!0-9]*) o=100 ;; esac
    [ "$o" -gt 100 ] && o=100
    a=$((o * 255 / 100))
    aa="$(printf '%02x' "$a")"
    bg="$(theme_color bg)"
    bgalt="$(theme_color bgAlt)"

    local bgA bgAltA
    bgA="$(printf '#%s%s' "$aa" "$bg")"
    bgAltA="$(printf '#%s%s' "$aa" "$bgalt")"
    for pair in "bgA:$bgA" "bgAltA:$bgAltA"; do
        key="${pair%%:*}"
        val="${pair#*:}"
        if grep -q "^var ${key}" "$js" 2>/dev/null; then
            sed -i -E "s/^var ${key}([[:space:]]*)=.*/var ${key} = \"${val}\";/" "$js" 2>/dev/null || true
        else
            printf 'var %s = "%s";\n' "$key" "$val" >>"$js"
        fi
    done

    rofi="$cache/rofi.rasi"
    if [ -f "$rofi" ]; then
        sed -i -E "s/^([[:space:]]*background:[[:space:]]*)#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?;/\1#${bg}${aa};/" "$rofi" 2>/dev/null || true
        sed -i -E "s/^([[:space:]]*background-alt:[[:space:]]*)#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?;/\1#${bgalt}${aa};/" "$rofi" 2>/dev/null || true
    fi
}

apply_fonts() {
    local ui app uisize appsize js rofi kitty fuzzel gtk eq zed hl sp
    local eqsize zedsize_ui zedsize_buf
    ui="$(ui_font)"
    app="$(app_font)"
    uisize="$(ui_font_size)"
    appsize="$(app_font_size)"
    eqsize=$((uisize + 3))
    zedsize_ui="$(awk -v s="$appsize" 'BEGIN { printf "%.1f", s + 4 }')"
    zedsize_buf="$(awk -v s="$appsize" 'BEGIN { printf "%.1f", s + 1.5 }')"

    js="$HOME/.config/quickshell/Theme.js"
    if [ -f "$js" ]; then
        sed -i -E "s/^(var fontFamily[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${ui}\"/" "$js" 2>/dev/null || true
        sed -i -E "s/^(var fontMono[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${app}\"/" "$js" 2>/dev/null || true
        sed -i -E "s/^(var fontSize[[:space:]]*=[[:space:]]*).*/\1${uisize};/" "$js" 2>/dev/null || true
    fi

    kitty="$cache/kitty.conf"
    if [ -f "$kitty" ]; then
        sed -i '/# pwnbox-fonts-start/,/# pwnbox-fonts-end/d' "$kitty" 2>/dev/null || true
        {
            printf '# pwnbox-fonts-start\n'
            printf 'font_family %s\n' "$app"
            printf 'font_size %s.0\n' "$appsize"
            printf '# pwnbox-fonts-end\n'
        } >>"$kitty" 2>/dev/null || true
    fi

    rofi="$cache/rofi.rasi"
    if [ -f "$rofi" ]; then
        sed -i '/\/\* pwnbox-fonts-start \*\//,/\/\* pwnbox-fonts-end \*\//d' "$rofi" 2>/dev/null || true
        {
            printf '/* pwnbox-fonts-start */\n'
            printf '* { font: "%s %s"; }\n' "$ui" "$uisize"
            printf '/* pwnbox-fonts-end */\n'
        } >>"$rofi" 2>/dev/null || true
    fi

    fuzzel="$cache/fuzzel.ini"
    if [ -f "$fuzzel" ]; then
        sed -i '/# pwnbox-fonts-start/,/# pwnbox-fonts-end/d' "$fuzzel" 2>/dev/null || true
        {
            printf '# pwnbox-fonts-start\n'
            printf '[main]\nfont=%s:size=%s\n' "$ui" "$uisize"
            printf '# pwnbox-fonts-end\n'
        } >>"$fuzzel" 2>/dev/null || true
    fi

    if command -v gsettings >/dev/null 2>&1; then
        gsettings set org.gnome.desktop.interface font-name "${ui} ${uisize}" >/dev/null 2>&1 || true
        gsettings set org.gnome.desktop.interface document-font-name "${ui} ${uisize}" >/dev/null 2>&1 || true
    fi
    for gtk in "$HOME/.config/gtk-3.0/settings.ini" "$HOME/.config/gtk-4.0/settings.ini"; do
        [ -f "$gtk" ] || continue
        sed -i -E "s/^gtk-font-name=.*/gtk-font-name=${ui} ${uisize}/" "$gtk" 2>/dev/null || true
    done

    eq="$HOME/.config/equibop/settings/quickCss.css"
    if [ -f "$eq" ]; then
        sed -i '/\/\* pwnbox-fonts-start \*\//,/\/\* pwnbox-fonts-end \*\//d' "$eq" 2>/dev/null || true
        {
            printf '/* pwnbox-fonts-start */\n'
            printf ':root { --font: "%s" !important; --font-code: "%s" !important; }\n' "$ui" "$app"
            printf 'body, button, input, select, textarea { font-family: var(--font), sans-serif !important; }\n'
            printf 'code, kbd, pre, samp { font-family: var(--font-code), monospace !important; }\n'
            printf 'html, body { font-size: %spx !important; }\n' "$eqsize"
            printf '/* pwnbox-fonts-end */\n'
        } >>"$eq" 2>/dev/null || true
    fi

    zed="$HOME/.config/zed/settings.json"
    if [ -f "$zed" ]; then
        sed -i -E \
            -e "s/(\"ui_font_family\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"${ui}\"/" \
            -e "s/(\"buffer_font_family\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"${app}\"/" \
            -e "s/(\"ui_font_size\"[[:space:]]*:[[:space:]]*)[0-9.]+/\1${zedsize_ui}/" \
            -e "s/(\"buffer_font_size\"[[:space:]]*:[[:space:]]*)[0-9.]+/\1${zedsize_buf}/" \
            "$zed" 2>/dev/null || true
    fi

    hl="$HOME/.config/hypr/hyprlock.conf"
    if [ -f "$hl" ]; then
        sed -i -E "s/^([[:space:]]*font_family[[:space:]]*=[[:space:]]*).*/\1${ui}/" "$hl" 2>/dev/null || true
    fi

    printf 'set font "%s" %s\n' "$app" "$appsize" >"$cache/zathura.conf" 2>/dev/null || true

    sp="$HOME/.scripts/pwnbox-spotify"
    if [ -x "$sp" ]; then
        "$sp" fonts >/dev/null 2>&1 || true
    fi
}

restart_quickshell() {
    local tries=0
    command -v qs >/dev/null 2>&1 || return 0

    pkill -f 'bin/quickshell -n$' 2>/dev/null || true
    while pgrep -f 'bin/quickshell -n$' >/dev/null 2>&1 && [ "$tries" -lt 40 ]; do
        sleep 0.1
        tries=$((tries + 1))
    done

    tries=0
    while [ "$tries" -lt 4 ]; do
        setsid qs -n >/dev/null 2>&1 &
        sleep 0.5
        pgrep -f 'bin/quickshell -n$' >/dev/null 2>&1 && return 0
        tries=$((tries + 1))
        sleep 0.4
    done
}

reload_fonts() {
    pkill -USR1 kitty 2>/dev/null || true
    restart_quickshell
}

seed_monochrome() {
    mkdir -p "$cache"
    local f
    for f in "$mono"/*; do
        [ -e "$f" ] || continue
        cp_atomic "$f" "$cache/$(basename -- "$f")"
    done
    sync_quickshell "$mono/Theme.js"
}

sync_quickshell() {
    local src="$1"
    [ -f "$src" ] || return 0
    mkdir -p "$HOME/.config/quickshell" 2>/dev/null || true
    cp -f "$src" "$HOME/.config/quickshell/Theme.js" 2>/dev/null || true
}

sync_zed() {
    local src="$1"
    [ -f "$src" ] || return 0
    mkdir -p "$HOME/.config/zed/themes" 2>/dev/null || true
    cp -f "$src" "$HOME/.config/zed/themes/pwnbox.json" 2>/dev/null || true
}

TG_BG=""; TG_FG=""; TG_CHROM=()
declare -A TG_CMAP=()

mix_hex() {
    local a="${1#\#}" b="${2#\#}" p="${3:-50}"
    printf -v MIX_RESULT '%02x%02x%02x' \
        $(( 16#${a:0:2} + (16#${b:0:2} - 16#${a:0:2}) * p / 100 )) \
        $(( 16#${a:2:2} + (16#${b:2:2} - 16#${a:2:2}) * p / 100 )) \
        $(( 16#${a:4:2} + (16#${b:4:2} - 16#${a:4:2}) * p / 100 ))
}

lum255() { LUM_RESULT=$(( (299 * $1 + 587 * $2 + 114 * $3) / 1000 )); }

nearest_color() {
    local r="$1" g="$2" b="$3" i cr cg cb d
    local best="${TG_CHROM[0]:-}" bestd=99999999
    for i in "${TG_CHROM[@]}"; do
        cr=$((16#${i:0:2})); cg=$((16#${i:2:2})); cb=$((16#${i:4:2}))
        d=$(( (r - cr) * (r - cr) + (g - cg) * (g - cg) + (b - cb) * (b - cb) ))
        if [ "$d" -lt "$bestd" ]; then bestd=$d; best="$i"; fi
    done
    NC_RESULT="$best"
}

recolor_one() {
    local hex="${1#\#}" alpha=""
    if [ ${#hex} -eq 8 ]; then alpha="${hex:6:2}"; hex="${hex:0:6}"; fi
    local new="${TG_CMAP[$hex]:-}"
    if [ -z "$new" ]; then
        local r=$((16#${hex:0:2})) g=$((16#${hex:2:2})) b=$((16#${hex:4:2}))
        local mx=$r mn=$r
        if [ "$g" -gt "$mx" ]; then mx=$g; fi
        if [ "$g" -lt "$mn" ]; then mn=$g; fi
        if [ "$b" -gt "$mx" ]; then mx=$b; fi
        if [ "$b" -lt "$mn" ]; then mn=$b; fi
        lum255 "$r" "$g" "$b"; local ls="$LUM_RESULT"
        if [ $((mx - mn)) -le 16 ]; then
            mix_hex "$TG_BG" "$TG_FG" "$((ls * 100 / 255))"; new="$MIX_RESULT"
        else
            nearest_color "$r" "$g" "$b"; local cand="$NC_RESULT"
            if [ -z "$cand" ]; then
                new="$TG_FG"
            else
                local cr=$((16#${cand:0:2})) cg=$((16#${cand:2:2})) cb=$((16#${cand:4:2}))
                lum255 "$cr" "$cg" "$cb"; local lc="$LUM_RESULT"
                if [ "$ls" -gt "$lc" ]; then
                    local den=$((255 - lc)); if [ "$den" -eq 0 ]; then den=1; fi
                    mix_hex "$cand" "$TG_FG" "$(( (ls - lc) * 100 / den ))"; new="$MIX_RESULT"
                else
                    if [ "$lc" -eq 0 ]; then lc=1; fi
                    mix_hex "$cand" "$TG_BG" "$(( (lc - ls) * 100 / lc ))"; new="$MIX_RESULT"
                fi
            fi
        fi
        TG_CMAP[$hex]="$new"
    fi
    RO_RESULT="#${new}${alpha}"
}

load_recolor_colors() {
    TG_BG="$(theme_color bg)"; [ -n "$TG_BG" ] || TG_BG="080808"
    TG_FG="$(theme_color fg)"; [ -n "$TG_FG" ] || TG_FG="cccccc"

    TG_CHROM=()
    local nv="$cache/nvim-palette.lua" i v
    if [ -f "$nv" ]; then
        for i in 1 2 3 4 5 6 9 10 11 12 13 14; do
            v="$(sed -nE "s/^[[:space:]]*color${i}[[:space:]]*=[[:space:]]*\"#?([0-9a-fA-F]{6})\".*/\1/p" "$nv" | head -1)"
            if [ -n "$v" ]; then TG_CHROM+=("$v"); fi
        done
    fi
    if [ ${#TG_CHROM[@]} -eq 0 ]; then
        for v in "$(theme_color red)" "$(theme_color green)" "$(theme_color yellow)" \
            "$(theme_color mauve)" "$(theme_color cyan)" "$(theme_color accent)"; do
            if [ -n "$v" ]; then TG_CHROM+=("$v"); fi
        done
    fi
}

sync_telegram() {
    local base="$HOME/.config/telegram/awesome.tdesktop-theme"
    [ -f "$base" ] || return 0
    mkdir -p "$HOME/.config/telegram" "$cache" 2>/dev/null || true

    load_recolor_colors

    TG_CMAP=()
    local tmp="$cache/telegram.tdesktop-theme.tmp" line
    : >"$tmp"
    while IFS= read -r line; do
        if [[ "$line" =~ ^([[:space:]]*[A-Za-z0-9_]+:[[:space:]]*)(#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?)(.*)$ ]]; then
            recolor_one "${BASH_REMATCH[2]}"
            printf '%s%s%s\n' "${BASH_REMATCH[1]}" "$RO_RESULT" "${BASH_REMATCH[4]}" >>"$tmp"
        else
            printf '%s\n' "$line" >>"$tmp"
        fi
    done <"$base"
    local out="$HOME/.config/telegram/pwnbox.tdesktop-theme"
    if ! cmp -s "$tmp" "$out" 2>/dev/null; then
        cat "$tmp" >"$out"
    fi
    if [ -d "$HOME/Downloads" ]; then
        cat "$tmp" >"$HOME/Downloads/pwnbox.tdesktop-theme" 2>/dev/null || true
    fi
    rm -f "$tmp"
}

recolor_rgb_triplet() {
    local r="${1:-0}" g="${2:-0}" b="${3:-0}" hex out
    [ "$r" -gt 255 ] && r=255
    [ "$g" -gt 255 ] && g=255
    [ "$b" -gt 255 ] && b=255
    printf -v hex '%02x%02x%02x' "$r" "$g" "$b"
    recolor_one "#$hex"
    out="${RO_RESULT#\#}"
    RGB_RESULT="$((16#${out:0:2})), $((16#${out:2:2})), $((16#${out:4:2}))"
}

recolor_rgb_in_line() {
    local line="$1"
    if [[ "$line" =~ ^([[:space:]]*--[A-Za-z0-9_-]+:[[:space:]]*)([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})(.*)$ ]]; then
        recolor_rgb_triplet "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}"
        line="${BASH_REMATCH[1]}${RGB_RESULT}${BASH_REMATCH[5]}"
    fi
    local rest="$line" out="" pre
    while [ -n "$rest" ]; do
        if [[ "$rest" =~ ^(.*rgba?\([[:space:]]*)([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})(.*)$ ]]; then
            pre="${BASH_REMATCH[1]}"
            recolor_rgb_triplet "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}"
            out+="$pre${RGB_RESULT}"
            rest="${BASH_REMATCH[5]}"
        else
            out+="$rest"
            break
        fi
    done
    RGB_LINE="$out"
}

recolor_css_line() {
    recolor_rgb_in_line "$1"
    local rest="$RGB_LINE" out="" pre tok norm
    while [ -n "$rest" ]; do
        if [[ "$rest" =~ ^([^#]*)#([0-9a-fA-F]+)(.*)$ ]]; then
            pre="${BASH_REMATCH[1]}"
            tok="${BASH_REMATCH[2]}"
            rest="${BASH_REMATCH[3]}"
            out+="$pre#"
            case "${#tok}" in
                3) norm="${tok:0:1}${tok:0:1}${tok:1:1}${tok:1:1}${tok:2:1}${tok:2:1}" ;;
                6|8) norm="$tok" ;;
                *) out+="$tok"; continue ;;
            esac
            recolor_one "#$norm"
            out+="${RO_RESULT#\#}"
        else
            out+="$rest"
            break
        fi
    done
    RC_RESULT="$out"
}

sync_equibop() {
    local base="$HOME/.config/equibop/theme-base.css"
    local out="$HOME/.config/equibop/settings/quickCss.css"
    [ -f "$base" ] || return 0
    [ -d "$HOME/.config/equibop" ] || return 0
    mkdir -p "$(dirname "$out")" "$cache" 2>/dev/null || true

    load_recolor_colors
    TG_CMAP=()

    local tmp="$cache/equibop.css.tmp" line
    : >"$tmp"
    while IFS= read -r line; do
        recolor_css_line "$line"
        printf '%s\n' "$RC_RESULT" >>"$tmp"
    done <"$base"
    if ! cmp -s "$tmp" "$out" 2>/dev/null; then
        cat "$tmp" >"$out"
    fi
    rm -f "$tmp"
}

telegram_running() {
    pgrep -f '^AyuGram$' >/dev/null 2>&1 || pgrep -x '.AyuGram-wrappe' >/dev/null 2>&1
}

sync_telegram_fonts() {
    local settings="$HOME/.local/share/AyuGramDesktop/tdata/ayu_settings.json"
    [ -f "$settings" ] || return 0
    command -v jq >/dev/null 2>&1 || return 0

    local app tmp
    app="$(app_font)"
    tmp="$cache/.ayu_settings.json.pwnbox-tmp"
    if jq --arg f "$app" '
        def setfont(k): if has(k) then .[k] = $f else . end;
        def setbool(k; v): if has(k) then .[k] = v else . end;
        setfont("monoFont") | setfont("fontFamily") | setfont("uiFont") | setfont("interfaceFont")
        | setbool("disableCustomBackgrounds"; false) | setbool("adaptiveCoverColor"; false)
    ' "$settings" >"$tmp" 2>/dev/null && [ -s "$tmp" ]; then
        cmp -s "$tmp" "$settings" 2>/dev/null || cat "$tmp" >"$settings"
    fi
    rm -f "$tmp"
}

apply_telegram_theme() {
    local theme="$HOME/.config/telegram/pwnbox.tdesktop-theme"
    local tdata="$HOME/.local/share/AyuGramDesktop/tdata"
    local cfg="$cache/tg-config.toml"
    local stamp="$cache/telegram-theme.sha"
    local sum

    [ -f "$theme" ] || return 0
    [ -d "$tdata" ] || return 0
    command -v tg-config >/dev/null 2>&1 || return 0

    sum="$(sha256sum "$theme" 2>/dev/null | awk '{print $1}')"

    if [ -n "$sum" ] && [ -f "$stamp" ] && [ "$(cat "$stamp" 2>/dev/null)" = "$sum" ]; then
        return 0
    fi

    sync_telegram_fonts

    printf 'tdata = "%s"\n\n[theme]\npath = "%s"\n' "$tdata" "$theme" >"$cfg" 2>/dev/null || return 0

    tg-config --config "$cfg" --offline >/dev/null 2>&1 || true

    [ -n "$sum" ] && printf '%s' "$sum" >"$stamp"
}

spotify_theme() {
    local helper="$HOME/.scripts/pwnbox-spotify"
    command -v spicetify >/dev/null 2>&1 || return 0
    [ -x "$helper" ] || return 0
    "$helper" apply >/dev/null 2>&1 || true
}

equibop_reload() {
    command -v hyprctl >/dev/null 2>&1 || return 0
    command -v wtype >/dev/null 2>&1 || return 0
    pgrep -f 'Equibop/resources/app.asar' >/dev/null 2>&1 || return 0

    local active prev
    active="$(hyprctl activewindow -j 2>/dev/null | jq -r '.class // empty' 2>/dev/null)"
    [ "$active" = "equibop" ] && return 0
    prev="$(hyprctl activewindow -j 2>/dev/null | jq -r '.address // empty' 2>/dev/null)"

    hyprctl dispatch focuswindow "class:^(equibop)$" >/dev/null 2>&1 || return 0
    sleep 0.35
    wtype -M ctrl -k r -m ctrl >/dev/null 2>&1 || true
    if [ -n "$prev" ] && [ "$prev" != "null" ]; then
        hyprctl dispatch focuswindow "address:${prev}" >/dev/null 2>&1 || true
    fi
}

lighten_hex() {
    local hex="${1#\#}" amt="${2:-6}"
    local r g b
    r=$((16#${hex:0:2}))
    g=$((16#${hex:2:2}))
    b=$((16#${hex:4:2}))
    r=$(( r + (255 - r) * amt / 100 ))
    g=$(( g + (255 - g) * amt / 100 ))
    b=$(( b + (255 - b) * amt / 100 ))
    printf '#%02x%02x%02x' "$r" "$g" "$b"
}

apply_borders() {
    local bg brdr hypr rofi kitty zed
    bg="$(sed -nE 's/^var bg[[:space:]]*=[[:space:]]*"#?([0-9a-fA-F]{6})".*/\1/p' \
        "$HOME/.config/quickshell/Theme.js" 2>/dev/null | head -1)"
    [ -n "$bg" ] || bg="080808"
    brdr="$(lighten_hex "#$bg" "${border_lighten:-12}")"
    brdr="${brdr#\#}"

    hypr="$cache/hyprland.conf"
    if [ -f "$hypr" ]; then
        local hypr_tmp="$cache/.hyprland.conf.pwnbox-tmp"
        if sed '/# pwnbox-borders-start/,/# pwnbox-borders-end/d' "$hypr" 2>/dev/null \
            | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}' >"$hypr_tmp"; then
            {
                printf '# pwnbox-borders-start\n'
                printf 'general {\n'
                printf '    col.active_border = rgba(%sff)\n' "$brdr"
                printf '    col.inactive_border = rgba(%sff)\n' "$brdr"
                printf '}\n'
                printf 'windowrule = border_color rgba(%sff) rgba(%sff), match:class ^(spotify)$\n' "$bg" "$bg"
                printf 'decoration {\n'
                printf '    rounding = %s\n' "$(win_radius)"
                printf '    active_opacity = %s\n' "$(opacity_value)"
                printf '    inactive_opacity = %s\n' "$(opacity_value)"
                printf '    shadow {\n'
                printf '        color = rgba(00000055)\n'
                printf '    }\n'
                printf '    blur {\n'
                printf '        enabled = %s\n' "$(blur_hypr)"
                printf '    }\n'
                printf '}\n'
                printf 'animations {\n'
                printf '    enabled = %s\n' "$(anim_hypr)"
                printf '}\n# pwnbox-borders-end\n'
            } >>"$hypr_tmp"
            if [ "$(tr -cd '{' <"$hypr_tmp" | wc -c)" = "$(tr -cd '}' <"$hypr_tmp" | wc -c)" ]; then
                mv -f "$hypr_tmp" "$hypr" 2>/dev/null || rm -f "$hypr_tmp"
            else
                rm -f "$hypr_tmp"
            fi
        else
            rm -f "$hypr_tmp"
        fi
    fi

    rofi="$cache/rofi.rasi"
    [ -f "$rofi" ] && sed -i -E "s/^([[:space:]]*brdr:[[:space:]]*).*/\1#${brdr};/" "$rofi" 2>/dev/null || true

    kitty="$cache/kitty.conf"
    if [ -f "$kitty" ]; then
        sed -i -E \
            -e "s/^(active_border_color[[:space:]]+).*/\1#${brdr}/" \
            -e "s/^(inactive_border_color[[:space:]]+).*/\1#${brdr}/" \
            -e "s/^(inactive_tab_background[[:space:]]+).*/\1#${bg}/" \
            "$kitty" 2>/dev/null || true
    fi

    zed_dir="$HOME/.config/zed/themes"
    if [ -d "$zed_dir" ]; then
        for zed in "$zed_dir"/*.json; do
            [ -f "$zed" ] || continue
            sed -i -E \
                -e "s/^([[:space:]]*\"[^\"]*border[^\"]*\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"#${brdr}\"/" \
                "$zed" 2>/dev/null || true
            sed -i -E \
                -e "s/^([[:space:]]*\"border\.transparent\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"#${brdr}80\"/" \
                "$zed" 2>/dev/null || true
        done
        zed_pwnbox="$zed_dir/pwnbox.json"
        if [ -f "$zed_pwnbox" ]; then
            sed -i -E \
                -e "s/^([[:space:]]*\"title_bar\.inactive_background\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"#${bg}\"/" \
                -e "s/^([[:space:]]*\"tab\.inactive_background\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"/\1\"#${bg}\"/" \
                "$zed_pwnbox" 2>/dev/null || true

            if command -v jq >/dev/null 2>&1; then
                local ig iact hov zed_tmp
                ig="$(theme_color accent)"
                iact="$(theme_color fgDim)"
                hov="$(theme_color fg)"
                mkdir -p "$cache" 2>/dev/null || true
                zed_tmp="$cache/.pwnbox-zed.json.pwnbox-tmp"
                if jq --arg ig "#${ig}" --arg ia "#${iact}" --arg hov "#${hov}" \
                    '.themes[0].style["editor.indent_guide"] = $ig
                     | .themes[0].style["editor.indent_guide_active"] = $ia
                     | .themes[0].style["panel.indent_guide"] = $ig
                     | .themes[0].style["panel.indent_guide_active"] = $ia
                     | .themes[0].style["panel.indent_guide_hover"] = $hov' \
                    "$zed_pwnbox" >"$zed_tmp" 2>/dev/null && [ -s "$zed_tmp" ]; then
                    mv -f "$zed_tmp" "$zed_pwnbox"
                else
                    rm -f "$zed_tmp"
                fi
            fi
        fi
    fi

    js="$HOME/.config/quickshell/Theme.js"
    if [ -f "$js" ]; then
        sed -i '/^var brdr[[:space:]]*=/d' "$js" 2>/dev/null || true
        printf 'var brdr = "#%s";\n' "$brdr" >>"$js"
    fi

    apply_ui_radius
    sync_telegram
    apply_telegram_theme
    sync_equibop
    apply_fonts
}

set_wallpaper() {
    local img="$1"
    [ -f "$img" ] || return 0
    pkill -f 'swaybg -i' 2>/dev/null || true
    command -v swaybg >/dev/null 2>&1 || return 0
    setsid swaybg -i "$img" -m fill >/dev/null 2>&1 &
}

reload_apps() {
    pkill -USR1 kitty 2>/dev/null || true
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
    if command -v gsettings >/dev/null 2>&1; then
        local theme
        gsettings set org.gnome.desktop.interface color-scheme 'default' >/dev/null 2>&1 || true
        sleep 0.2
        gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' >/dev/null 2>&1 || true
        theme="$(gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null | tr -d "'")"
        gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita' >/dev/null 2>&1 || true
        sleep 0.2
        gsettings set org.gnome.desktop.interface gtk-theme "${theme:-Colloid-Dark-Grey}" >/dev/null 2>&1 || true
    fi
    restart_quickshell

    ( spotify_theme ) &
    ( sleep 0.6; equibop_reload ) &
}

build_mono_icons() {
    command -v magick >/dev/null 2>&1 || return 0
    local dest="$HOME/.local/share/icons/pwnbox-mono"
    local fg
    fg="$(sed -nE 's/^var fg[[:space:]]*=[[:space:]]*"#?([0-9a-fA-F]{6})".*/\1/p' \
        "$HOME/.config/quickshell/Theme.js" 2>/dev/null | head -1)"
    [ -n "$fg" ] || fg="cccccc"
    [ -f "$dest/.tint" ] && [ "$(cat "$dest/.tint" 2>/dev/null)" = "$fg" ] && return 0

    local r g b low high
    r=$((16#${fg:0:2})); g=$((16#${fg:2:2})); b=$((16#${fg:4:2}))
    low="#$(printf '%02x%02x%02x' $((r * 25 / 100)) $((g * 25 / 100)) $((b * 25 / 100)))"
    high="#$fg"

    local apps="$dest/48x48/apps"
    rm -rf "$dest"
    mkdir -p "$apps"
    {
        printf '[Icon Theme]\nName=pwnbox-mono\n'
        printf 'Comment=Monochrome icons tinted from the active theme\n'
        printf 'Directories=48x48/apps\n\n[48x48/apps]\nSize=48\nContext=Applications\nType=Fixed\n'
    } >"$dest/index.theme"
    printf '%s' "$fg" >"$dest/.tint"

    local -A map=()
    local dir p bn nm
    for dir in "$HOME/.local/share/icons" "$HOME/.icons" \
        /run/current-system/sw/share/icons/Papirus \
        /run/current-system/sw/share/icons/hicolor \
        /run/current-system/sw/share/icons/Adwaita; do
        [ -d "$dir" ] || continue
        while IFS= read -r p; do
            bn="${p##*/}"; nm="${bn%.*}"
            [ -n "${map[$nm]:-}" ] && continue
            map[$nm]="$p"
        done < <(find "$dir" \( -type f -o -type l \) 2>/dev/null | grep -E '\.(png|svg)$')
    done

    local -A want=()
    local d icon
    for d in /run/current-system/sw/share/applications \
        "/etc/profiles/per-user/$USER/share/applications" \
        "$HOME/.local/share/applications" \
        "$HOME/.nix-profile/share/applications"; do
        [ -d "$d" ] || continue
        while IFS= read -r icon; do
            [ -n "$icon" ] && want["$icon"]=1
        done < <(grep -h '^Icon=' "$d"/*.desktop 2>/dev/null | cut -d= -f2-)
    done

    local name src
    for name in "${!want[@]}"; do
        case "$name" in
            /*) [ -f "$name" ] && src="$name" || src="" ;;
            *) src="${map[$name]:-}" ;;
        esac
        [ -n "$src" ] || continue
        magick "$src" -background none -resize 64x64 -gravity center -extent 64x64 \
            -colorspace Gray -auto-level +level-colors "$low,$high" \
            "$apps/$name.png" 2>/dev/null || true
    done
}

install_palette() {
    local pair src dst
    for pair in \
        "pwnbox-kitty.conf:kitty.conf" \
        "pwnbox-rofi.rasi:rofi.rasi" \
        "pwnbox-fuzzel.ini:fuzzel.ini" \
        "pwnbox-hyprland.conf:hyprland.conf" \
        "pwnbox-gtk.css:gtk-colors.css" \
        "pwnbox-colors.json:colors.json" \
        "pwnbox-nvim.lua:nvim-palette.lua"; do
        src="$wal_out/${pair%%:*}"
        dst="$cache/${pair##*:}"
        cp_atomic "$src" "$dst"
    done
    if [ -f "$wal_out/pwnbox-theme.js" ]; then
        sync_quickshell "$wal_out/pwnbox-theme.js"
    fi
    sync_zed "$wal_out/pwnbox-zed.json"
}


custom_theme_dirs() {
    local d
    [ -d "$themes_dir" ] || return 0
    for d in "$themes_dir"/*/; do
        [ -d "$d" ] || continue
        [ -f "${d}theme.conf" ] || continue
        printf '%s\n' "${d%/}"
    done
}

theme_dir_of() {
    local d
    for d in "$themes_dir"/*/; do
        [ -d "$d" ] || continue
        [ "$(basename "${d%/}")" = "$1" ] && { printf '%s' "${d%/}"; return 0; }
    done
    return 1
}

conf_get() {
    local file="$1" key="$2" line k v
    [ -f "$file" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line#"${line%%[![:space:]]*}"}"
        case "$line" in '' | '#'* | ';'*) continue ;; esac
        case "$line" in *=*) ;; *) continue ;; esac
        k="${line%%=*}"
        v="${line#*=}"
        k="${k%"${k##*[![:space:]]}"}"
        v="${v#"${v%%[![:space:]]*}"}"
        v="${v%"${v##*[![:space:]]}"}"
        if [ "$k" = "$key" ]; then
            printf '%s' "$v"
            return 0
        fi
    done <"$file"
}

norm_hex() {
    local c="${1#\#}"
    case "$c" in
        [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
            printf '#%s' "$c" ;;
        *) return 1 ;;
    esac
}

mono_color() {
    sed -nE "s/^[[:space:]]*${1}[[:space:]]*=[[:space:]]*\"(#?[0-9a-fA-F]{6})\".*/\1/p" \
        "$mono/nvim-palette.lua" 2>/dev/null | head -1
}

scheme_color() {
    local conf="$1" key="$2" fb="$3" v
    v="$(conf_get "$conf" "$key")"
    if [ -n "$v" ]; then
        norm_hex "$v"
    else
        v="$(mono_color "$fb")"
        [ -n "$v" ] && norm_hex "$v"
    fi
}

build_custom_scheme() {
    local conf="$1" out="$2" i key c bg fg cursor
    local -A col=()
    for i in $(seq 0 15); do
        key="color$i"
        c="$(scheme_color "$conf" "$key" "$key")" || return 1
        col[$key]="$c"
    done
    bg="$(scheme_color "$conf" background bg)" || return 1
    fg="$(scheme_color "$conf" foreground fg)" || return 1
    cursor="$(scheme_color "$conf" cursor fg)" || return 1

    {
        printf '{\n  "special": {\n'
        printf '    "background": "%s",\n    "foreground": "%s",\n    "cursor": "%s"\n' "$bg" "$fg" "$cursor"
        printf '  },\n  "colors": {\n'
        for i in $(seq 0 15); do
            key="color$i"
            if [ "$i" -lt 15 ]; then
                printf '    "%s": "%s",\n' "$key" "${col[$key]}"
            else
                printf '    "%s": "%s"\n' "$key" "${col[$key]}"
            fi
        done
        printf '  }\n}\n'
    } >"$out.tmp" || { rm -f "$out.tmp"; return 1; }
    mv -f "$out.tmp" "$out" 2>/dev/null || { rm -f "$out.tmp"; return 1; }
}

custom_theme_wallpaper() {
    local dir="$1" wp f
    wp="$(conf_get "$dir/theme.conf" wallpaper)"
    if [ -n "$wp" ]; then
        case "$wp" in
            /*) [ -f "$wp" ] && { printf '%s' "$wp"; return 0; } ;;
            *) [ -f "$dir/$wp" ] && { printf '%s' "$dir/$wp"; return 0; } ;;
        esac
    fi
    for f in "$dir"/*.png "$dir"/*.jpg "$dir"/*.jpeg "$dir"/*.webp; do
        [ -f "$f" ] && { printf '%s' "$f"; return 0; }
    done
    printf '%s' "$default_wall"
}

do_custom() {
    local dir="${1%/}" quiet="${2:-}" conf name scheme wallpaper
    conf="$dir/theme.conf"
    [ -f "$conf" ] || { [ -n "$quiet" ] || notify "Theme" "No theme.conf in $(basename "$dir")"; return 1; }
    command -v wal >/dev/null 2>&1 || { [ -n "$quiet" ] || notify "Theme" "pywal not installed"; return 1; }
    name="$(conf_get "$conf" name)"
    [ -n "$name" ] || name="$(basename "$dir")"

    mkdir -p "$cache"
    scheme="$cache/custom-scheme.json"
    if ! build_custom_scheme "$conf" "$scheme"; then
        [ -n "$quiet" ] || notify "Theme" "Invalid colours in $(basename "$dir")/theme.conf"
        return 1
    fi
    if ! wal --theme "$scheme" -n -s -e -q >/dev/null 2>&1; then
        [ -n "$quiet" ] || notify "Theme" "pywal failed"
        return 1
    fi

    install_palette
    apply_borders
    ( build_mono_icons >/dev/null 2>&1 & )

    wallpaper="$(custom_theme_wallpaper "$dir")"
    printf '%s\n' "$wallpaper" >"$state"
    printf '%s\n' "$(basename "$dir")" >"$custom_state"
    set_wallpaper "$wallpaper"
    reload_apps
    [ -n "$quiet" ] || notify "Theme" "$name"
}

custom_label() {
    local cur dir name
    cur="$(read_state "$custom_state" "")"
    [ -n "$cur" ] || return 0
    while IFS= read -r dir; do
        [ "$(basename "$dir")" = "$cur" ] || continue
        name="$(conf_get "$dir/theme.conf" name)"
        [ -n "$name" ] || name="$cur"
        printf '  (%s)' "$name"
        return 0
    done < <(custom_theme_dirs)
}

custom() {
    local listf mapf dir conf name wp thumb choice
    listf="$(mktemp)"; mapf="$(mktemp)"
    while IFS= read -r dir; do
        [ -n "$dir" ] || continue
        conf="$dir/theme.conf"
        name="$(conf_get "$conf" name)"
        [ -n "$name" ] || name="$(basename "$dir")"
        wp="$(custom_theme_wallpaper "$dir")"
        printf '%s\t%s\n' "$name" "$dir" >>"$mapf"
        thumb="$(make_thumb "$wp" "custom-$(basename "$dir")")"
        if [ -n "$thumb" ]; then
            printf '%s\0icon\x1f%s\n' "$name" "$thumb" >>"$listf"
        else
            printf '%s\n' "$name" >>"$listf"
        fi
    done < <(custom_theme_dirs)

    if [ ! -s "$listf" ]; then
        rm -f "$listf" "$mapf"
        notify "Theme" "No custom themes in $themes_dir"
        return 0
    fi

    choice="$(
        rofi -dmenu -i -show-icons -no-custom \
            -kb-move-char-back Control+b -kb-move-char-forward Control+f \
            -kb-row-left Left -kb-row-right Right \
            -p "Custom theme" -theme "$custom_cfg" <"$listf" || true
    )"
    dir="$(awk -F'\t' -v n="$choice" '$1 == n { print $2; exit }' "$mapf")"
    rm -f "$listf" "$mapf"
    [ -n "$choice" ] || return 0
    [ -n "$dir" ] || return 0
    do_custom "$dir"
}

monochrome() {
    local dir
    if dir="$(theme_dir_of Monochrome)"; then
        do_custom "$dir"
        return 0
    fi
    seed_monochrome
    sync_zed "$mono/pwnbox.json"
    apply_borders
    ( build_mono_icons >/dev/null 2>&1 & )
    set_wallpaper "$default_wall"
    printf '%s\n' "$default_wall" >"$state"
    rm -f "$custom_state"
    reload_apps
    notify "Theme" "Monochrome"
}

apply() {
    local img="$1"
    [ -f "$img" ] || { notify "Theme" "Not found: $img"; return 1; }
    command -v wal >/dev/null 2>&1 || { notify "Theme" "pywal not installed"; return 1; }
    mkdir -p "$cache"
    if ! wal -i "$img" -n -s -e -q >/dev/null 2>&1; then
        notify "Theme" "pywal failed"; return 1
    fi
    install_palette
    apply_borders
    ( build_mono_icons >/dev/null 2>&1 & )
    printf '%s\n' "$img" >"$state"
    rm -f "$custom_state"
    set_wallpaper "$img"
    reload_apps
    notify "Theme" "$(basename "$img")"
}

restore() {
    local img="$default_wall" cur dir
    cur="$(read_state "$custom_state" "")"
    if [ -n "$cur" ]; then
        while IFS= read -r dir; do
            [ "$(basename "$dir")" = "$cur" ] || continue
            if [ ! -f "$cache/colors.json" ]; then
                do_custom "$dir" quiet
                return 0
            fi
            img="$(custom_theme_wallpaper "$dir")"
            printf '%s\n' "$img" >"$state"
            set_wallpaper "$img"
            borders
            ( build_mono_icons >/dev/null 2>&1 & )
            return 0
        done < <(custom_theme_dirs)
        rm -f "$custom_state"
    fi
    if [ -f "$state" ] && [ -f "$(cat "$state" 2>/dev/null)" ]; then
        img="$(cat "$state")"
    fi
    [ -f "$cache/colors.json" ] || seed_monochrome
    printf '%s\n' "$img" >"$state"
    set_wallpaper "$img"
    borders
    ( build_mono_icons >/dev/null 2>&1 & )
}

borders() {
    apply_borders
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
}

make_thumb() {
    local src="$1" key="${2:-}" out
    [ -f "$src" ] || return 0
    [ -n "$key" ] || key="$(basename "$src")"
    out="$thumb_dir/${key}.png"
    if [ ! -f "$out" ] || [ "$src" -nt "$out" ]; then
        if command -v magick >/dev/null 2>&1; then
            magick "$src" -auto-orient -resize '256x256^' -gravity center -extent 256x256 "$out" >/dev/null 2>&1 || true
        elif command -v convert >/dev/null 2>&1; then
            convert "$src" -auto-orient -resize '256x256^' -gravity center -extent 256x256 "$out" >/dev/null 2>&1 || true
        fi
    fi
    [ -f "$out" ] && printf '%s' "$out"
    return 0
}

bar_styles=(
    "islands|Islands (boxed groups)"
    "floating|Floating islands"
    "flat|Classic flat"
    "pill|Centered pill"
    "capsule|Rounded capsule"
    "bottom|Bottom dock"
    "split|Split pills"
    "blocky|Blocky groups"
    "ghost|Ghost outlines"
    "minipills|Mini pills"
)

set_barstyle() {
    local id="$1" label="$2"
    mkdir -p "$cache"
    printf '%s' "$id" >"$style_state"
    if command -v qs >/dev/null 2>&1; then
        qs ipc call barstyle set "$id" >/dev/null 2>&1 || true
    fi
    notify "Bar style" "$label"
}

barstyle() {
    local entry id choice preview
    choice="$(
        for entry in "${bar_styles[@]}"; do
            id="${entry%%|*}"
            preview="$previews_dir/$id.png"
            if [ -f "$preview" ]; then
                printf '%s\0icon\037%s\n' "$id" "$preview"
            else
                printf '%s\n' "$id"
            fi
        done | rofi -dmenu -i -show-icons -no-custom \
            -kb-move-char-back Control+b -kb-move-char-forward Control+f \
            -kb-row-left Left -kb-row-right Right \
            -p "Bar style" -theme "$barstyle_cfg" || true
    )"
    [ -n "$choice" ] || return 0
    for entry in "${bar_styles[@]}"; do
        id="${entry%%|*}"
        if [ "$choice" = "$id" ]; then
            set_barstyle "$id" "${entry##*|}"
            return 0
        fi
    done
}

pick() {
    mkdir -p "$wall_dir" "$thumb_dir"
    shopt -s nullglob
    local f choice t d name listf mapf
    listf="$(mktemp)"
    mapf="$(mktemp)"
    local -A seen=()
    d="$wall_dir"
    if [ -d "$d" ]; then
        for f in "$d"/*.png "$d"/*.jpg "$d"/*.jpeg "$d"/*.webp; do
            name="$(basename "$f")"
            [ -n "${seen[$name]:-}" ] && continue
            seen[$name]=1
            printf '%s\t%s\n' "$name" "$f" >>"$mapf"
            t="$(make_thumb "$f")"
            if [ -n "$t" ]; then
                printf '%s\0icon\037%s\n' "$name" "$t" >>"$listf"
            else
                printf '%s\n' "$name" >>"$listf"
            fi
        done
    fi
    if [ ! -s "$listf" ]; then
        rm -f "$listf" "$mapf"
        notify "Wallpaper" "No wallpapers found"
        return 0
    fi
    choice="$(
        rofi -dmenu -i -show-icons -no-custom \
            -kb-move-char-back Control+b -kb-move-char-forward Control+f \
            -kb-row-left Left -kb-row-right Right \
            -p "Wallpaper" -theme "$picker_cfg" <"$listf" || true
    )"
    f="$(awk -F'\t' -v n="$choice" '$1 == n { print $2; exit }' "$mapf")"
    rm -f "$listf" "$mapf"
    [ -n "$choice" ] || return 0
    [ -n "$f" ] || return 0
    apply "$f"
}


radius_values=(0 2 4 6 8 9 10 12 14 16 20 24)
size_values=(7 8 9 10 11 12 13 14 15 16 18 20)

menu_section() {
    printf '%s\0nonselectable\x1ftrue\x1fpermanent\x1ftrue\n' \
        "<span foreground='#$(theme_color fgDim)' weight='bold' size='small'>$1</span>"
}

choose_value() {
    local prompt="$1" current="$2"; shift 2
    printf '%s\n' "$@" | rofi -dmenu -i -no-custom -select "$current" \
        -p "$prompt" -theme "$menu_cfg" || true
}

choose_window_radius() {
    local choice
    choice="$(choose_value "Window radius" "$(win_radius)" "${radius_values[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$radius_state"
    apply_borders
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
    notify "Window radius" "${choice}px"
}

choose_ui_radius() {
    local choice
    choice="$(choose_value "UI radius" "$(ui_radius)" "${radius_values[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$uir_state"
    apply_ui_radius
    restart_quickshell
    notify "UI radius" "${choice}px"
}

choose_blur() {
    local choice msg
    choice="$(choose_value "Blur" "$(blur_state)" on off)"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$blur_file"
    msg="Disabled"
    if [ "$choice" = "on" ]; then
        msg="Enabled"
        if [ "$(opacity)" = "100" ]; then
            printf '%s' 92 >"$opacity_file"
            msg="Enabled · transparency 92%"
        fi
    fi
    apply_borders
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
    notify "Blur" "$msg"
}

choose_opacity() {
    local choice
    choice="$(choose_value "Transparency" "$(opacity)" 100 96 92 88 85 80 70 60 50)"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$opacity_file"
    apply_borders
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
    if [ "$choice" = "100" ] && [ "$(blur_state)" = "on" ]; then
        notify "Transparency" "100% — blur has nothing to see through"
    else
        notify "Transparency" "${choice}%"
    fi
}

choose_animations() {
    local choice
    choice="$(choose_value "Animations" "$(anim_state)" on off)"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$anim_file"
    apply_borders
    command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 || true
    notify "Animations" "$([ "$choice" = "on" ] && printf 'Enabled' || printf 'Disabled')"
}

choose_ui_font() {
    local choice
    choice="$(choose_value "System font" "$(ui_font)" "${font_choices[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$font_ui_state"
    apply_fonts
    reload_fonts
    notify "System font" "$choice"
}

choose_app_font() {
    local choice
    choice="$(choose_value "Application font" "$(app_font)" "${font_choices[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$font_app_state"
    apply_fonts
    reload_fonts
    notify "Application font" "$choice"
}

choose_ui_font_size() {
    local choice
    choice="$(choose_value "System font size" "$(ui_font_size)" "${size_values[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$font_ui_size_state"
    apply_fonts
    reload_fonts
    notify "System font size" "${choice}px"
}

choose_app_font_size() {
    local choice
    choice="$(choose_value "Application font size" "$(app_font_size)" "${size_values[@]}")"
    [ -n "$choice" ] || return 0
    mkdir -p "$cache"
    printf '%s' "$choice" >"$font_app_size_state"
    apply_fonts
    reload_fonts
    notify "Application font size" "${choice}px"
}

menu() {
    local choice
    choice="$(
        {
            menu_section "THEME"
            printf 'Pywal\n'
            printf 'Custom%s\n' "$(custom_label)"
            menu_section "BAR"
            printf 'Style\n'
            menu_section "WINDOWS"
            printf 'Corner radius: %spx\n' "$(win_radius)"
            menu_section "INTERFACE"
            printf 'UI corner radius: %spx\n' "$(ui_radius)"
            printf 'System font: %s\n' "$(ui_font)"
            printf 'System font size: %s\n' "$(ui_font_size)"
            printf 'Application font: %s\n' "$(app_font)"
            printf 'Application font size: %s\n' "$(app_font_size)"
            menu_section "EFFECTS"
            printf 'Blur: %s\n' "$(blur_label)"
            printf 'Transparency: %s\n' "$(opacity_label)"
            printf 'Animations: %s\n' "$(anim_label)"
        } | rofi -dmenu -i -no-custom -markup-rows -p "Theme" -theme "$menu_cfg" || true
    )"
    [ -n "$choice" ] || return 0
    case "$choice" in
        "Pywal")                  pick ;;
        "Custom"*)                custom ;;
        "Style")                  barstyle ;;
        "Corner radius:"*)        choose_window_radius ;;
        "UI corner radius:"*)     choose_ui_radius ;;
        "System font:"*)          choose_ui_font ;;
        "System font size:"*)     choose_ui_font_size ;;
        "Application font:"*)     choose_app_font ;;
        "Application font size:"*) choose_app_font_size ;;
        "Blur:"*)                 choose_blur ;;
        "Transparency:"*)         choose_opacity ;;
        "Animations:"*)           choose_animations ;;
    esac
}

case "${1:-menu}" in
    menu)       menu ;;
    pick | wallpaper) pick ;;
    custom | themes) custom ;;
    monochrome) monochrome ;;
    apply)      apply "${2:?usage: pwnbox-theme apply <image>}" ;;
    barstyle)   barstyle ;;
    radius)     choose_window_radius ;;
    uiradius)   choose_ui_radius ;;
    uifont | systemfont) choose_ui_font ;;
    appfont | monofont)  choose_app_font ;;
    uifontsize | systemsize) choose_ui_font_size ;;
    appfontsize | appsize)   choose_app_font_size ;;
    blur)       choose_blur ;;
    transparency | opacity) choose_opacity ;;
    animations | animation) choose_animations ;;
    borders)    borders ;;
    icons)      build_mono_icons ;;
    restore)    restore ;;
    -h | --help | help)
        sed -n '2,21p' "$0"
        ;;
    *)
        echo "unknown command: $1" >&2
        exit 2
        ;;
esac
