"""sshj.theme — semantic theme channels (B15, leaf).

B2 step 2d: moved from sshj_cli.py (single-file era) — no behavior change.
Holds the THEME DATA (THEMES) and detection (detect_theme). The active
channel constants (SEL, C_*, TARGET_*) and apply_theme() stay in sshj_cli:
they are module-global state that every draw call reads bare, and
apply_theme() must rebind the globals of the module that reads them.
"""
import os
import shutil
import subprocess

# Package version (branding + the grid title line; read by ui.render and the
# __init__ re-export). Lives in the theme leaf because both are versioned
# branding surfaces, and it must not create an import edge anywhere deeper.
VER = "0.9"

# B15: semantic theme channels. SEL is the aspect color (selected name ONLY —
# one highlight, not a block); teal is identity/our-address; gray is the
# muted chrome (keymap, source, footer); strike = unreachable; the target
# line carries the health signal. THEMES maps channel -> escape code.
# nord is the default (house style); light is the inverted variant (darker
# values on a light background).
THEMES = {
    "nord": {   # default (house style). Accent = kanagawa wave blue (2026-09-20,
        # was SGR 34: kanagawabones re-paints 16-color blue as #7eb3c9 CYAN,
        # so a truecolor value is the only way to get a real blue on the
        # house terminal). #749cd5 = kanagawa wave's own blue: 5.18:1 on the
        # kanagawabones bg (contrast probe), B-dominant, clearly darker and
        # bluer than the old cyan.
        "sel": "\x1b[1;4;38;2;116;156;213m",  # bold+underline kanagawa blue: selected name (hyperlink look)
        "sel_mark": "\x1b[1;38;2;116;156;213m",  # bold accent, NO underline: the selected row's tailnet mark
        "teal": "\x1b[36m",       # our-address (identity)
        "gray": "\x1b[90m",       # keymap + source + footer (DarkGray)
        "strike": "\x1b[2;9m",    # unreachable: dim + strikethrough
        "italic": "\x1b[3m",      # muted-but-there: stale lazy values
        "yellow": "\x1b[38;2;116;156;213m",  # input line (search/staged) = the accent
        "target_up": "",           # foreground (default text color)
        "target_down": "\x1b[9;90m",  # strikethrough + muted
        "target_default": None,   # -> C_GRAY (not yet probed)
    },
    "light": {  # darker variants so the same hierarchy reads on light bg
        # navy: the same accent idea on light bg, dark enough to clear the
        # 4.5:1 white/offwhite gate (measured: 10.36 / 9.50). The old rule
        # "same blue in both modes" is superseded 2026-09-20: one accent
        # per theme, both dark blue.
        "sel": "\x1b[1;4;38;2;30;58;138m",  # bold+underline navy: the accent (hyperlink look)
        "sel_mark": "\x1b[1;38;2;30;58;138m",  # bold accent, NO underline: selected row's tailnet mark
        "teal": "\x1b[32m",       # identity, off the accent's hue 34 (F2
                                   # 2026-09-19). The channel is NAMED teal but
                                   # the measured decision landed on green: the
                                   # natural teal 36 clears white (4.77:1) but
                                   # MISSES the offwhite gate (4.38:1 < 4.5:1);
                                   # the nearest 16-color that clears BOTH white
                                   # AND offwhite at >=4.5:1 while off-hue-34
                                   # is 32 (green, 5.14 / 4.71). Recorded in
                                   # worklog/F2-monitor.md + tests/test_theme.
        "gray": "\x1b[90m",       # DarkGray: the darkest portable fg
        "strike": "\x1b[2;9m",    # dim + strikethrough (portable)
        "italic": "\x1b[3m",
        "yellow": "\x1b[38;2;30;58;138m",  # navy, the accent
        "target_up": "\x1b[1m",   # bold: the 'up' state must stand out
        "target_down": "\x1b[9;90m",
        "target_default": None,
    },
    "plain": {  # NO_COLOR (no-color.org): every palette channel off
        "sel": "",              # no highlight
        "sel_mark": "",
        "teal": "",
        "gray": "",             # TARGET_DEFAULT (None) falls back here
        "strike": "",
        "italic": "",
        "yellow": "",
        "target_up": "",        # bold/strike markers off
        "target_down": "",
        "target_default": None,  # -> C_GRAY ('' under plain anyway)
    },
}


def detect_theme():
    """SSHJ_THEME wins (nord|light|plain|auto; unknown -> nord, never a
    crash) — an explicit theme beats NO_COLOR. NO_COLOR (no-color.org: set
    and NON-empty) => plain; empty string is ignored. auto is best-effort:
    COLORFGBG=15;*/15;* (light fg = light-ish bg) or gsettings color-scheme
    'default' => light, 'prefer-dark' => nord; no signal => nord. The
    fallback is always a theme, never a crash (C3)."""
    forced = os.environ.get("SSHJ_THEME", "auto").strip().lower()
    if forced in THEMES:
        return forced
    if forced != "auto":
        return "nord"
    if os.environ.get("NO_COLOR", "") != "":
        return "plain"
    cf = os.environ.get("COLORFGBG", "")
    if cf.startswith("15;"):
        return "light"
    if "gnome" in os.environ.get("XDG_CURRENT_DESKTOP", "").lower() \
            and shutil.which("gsettings"):
        try:
            r = subprocess.run(["gsettings", "get",
                                "org.gnome.desktop.interface",
                                "color-scheme"],
                               capture_output=True, text=True, timeout=1)
            return "nord" if r.stdout.strip() == "'prefer-dark'" \
                else "light"
        except Exception:
            pass
    return "nord"
