from __future__ import annotations

import asyncio
import inspect
import json

from textual import events
from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.message import Message
from textual.screen import ModalScreen
from textual.widgets import ListItem, ListView, Static, Input, Button, Label, Checkbox, Select
from crypto_utils import fmt_date, fmt_time, set_time_display_settings, get_time_display_settings, TIMEZONE_OPTIONS, TIME_FORMAT_OPTIONS
from ui.themes import theme_list, get_gtk_theme_colors, get_qt_theme_colors
from ui.i18n import I18N, t as translate_func
from ui.widgets.call_panel import CallVolumeBar
from ui.widgets.appearance import NumberSlider, ColorPicker, ColorRulesManager, ColorRule, ColorRuleItem, AppearanceCategory
from ui.keybindings import (
    KeybindingsManager, DEFAULT_BINDINGS, ACTION_I18N_MAP, CONTEXT_I18N_MAP,
    format_key_display, parse_textual_key, get_keybindings_manager
)

from ui.widgets.patched_textarea import TextArea


class KeyBindingInput(Static):

    class Changed(Message):
        def __init__(self, sender: "KeyBindingInput", context: str, action: str, new_key: str):
            super().__init__()
            self.sender = sender
            self.context = context
            self.action = action
            self.new_key = new_key

    def __init__(self, context: str, action: str, current_key: str, description: str):
        super().__init__(classes="keybind_input")
        self.context = context
        self.action = action
        self.current_key = current_key
        self.description = description
        self._capturing = False

    def compose(self) -> ComposeResult:
        with Horizontal(classes="keybind_row"):
            yield Static(self.description, classes="keybind_desc")
            yield Button(format_key_display(self.current_key), id=f"kb__{self.context}__{self.action}", classes="keybind_btn")
            yield Button("↺", id=f"kbr__{self.context}__{self.action}", classes="keybind_reset_btn")

    def start_capture(self) -> None:
        self._capturing = True
        self.add_class("capturing")
        try:
            btn = self.query_one(f"#kb__{self.context}__{self.action}", Button)
            btn.label = self.app.t("bind_press_key")
            btn.add_class("capturing")
        except Exception:
            pass

    def stop_capture(self, new_key: str = None) -> None:
        self._capturing = False
        self.remove_class("capturing")
        try:
            btn = self.query_one(f"#kb__{self.context}__{self.action}", Button)
            btn.remove_class("capturing")
            if new_key:
                self.current_key = new_key
            btn.label = format_key_display(self.current_key)
        except Exception:
            pass

    def on_key(self, event: events.Key) -> None:
        if not self._capturing:
            return

        event.stop()
        event.prevent_default()


        if event.key in ("ctrl", "shift", "alt", "meta", "control", "ctrl_l", "ctrl_r", "shift_l", "shift_r", "alt_l", "alt_r"):
            return

        new_key = parse_textual_key(event)
        self.stop_capture(new_key)
        self.post_message(self.Changed(self, self.context, self.action, new_key))

    def update_key(self, new_key: str) -> None:
        self.current_key = new_key
        try:
            btn = self.query_one(f"#kb__{self.context}__{self.action}", Button)
            btn.label = format_key_display(new_key)
        except Exception:
            pass


class SettingsTab(Static):
    class Chosen(Message):
        def __init__(self, sender: "SettingsTab", key: str):
            super().__init__()
            self.sender = sender
            self.key = key

    def __init__(self, key: str, label: str):
        super().__init__(label, classes="settings_tab")
        self.key = key

    def set_active(self, active: bool) -> None:
        if active:
            self.add_class("active")
        else:
            self.remove_class("active")

    def on_click(self, event: events.Click) -> None:
        self.post_message(self.Chosen(self, self.key))
        event.stop()

class SettingsScreen(ModalScreen):
    BINDINGS = [("escape", "close", ""), ("up", "prev_tab", ""), ("down", "next_tab", "")]

    def compose(self) -> ComposeResult:
        with Horizontal(id="settings_root"):
            with Vertical(id="settings_nav"):
                yield Static("", id="settings_title")
                yield VerticalScroll(id="settings_tabs_scroll")
                yield Static("", id="settings_hint")

                yield Button("", id="btn_logout", variant="error", classes="logout_btn")
            with Vertical(id="settings_content"):
                yield Static("", id="settings_header")
                yield VerticalScroll(id="settings_scroll")

    async def on_mount(self) -> None:
        self._tabs = ["profile", "privacy", "lang", "theme", "appearance", "plugins", "audio", "network", "cloud", "backup", "bindings"]
        self._idx = 0
        self._active = "profile"
        self._audio_test_engine = None
        self.refresh_labels()

        tabs = self.query_one("#settings_tabs_scroll", VerticalScroll)
        res = tabs.mount(
            SettingsTab("profile", self.app.t("tab_profile")),
            SettingsTab("privacy", self.app.t("tab_privacy")),
            SettingsTab("lang", self.app.t("tab_language")),
            SettingsTab("theme", self.app.t("tab_theme")),
            SettingsTab("appearance", self.app.t("tab_appearance")),
            SettingsTab("plugins", self.app.t("tab_plugins")),
            SettingsTab("audio", self.app.t("tab_audio")),
            SettingsTab("network", self.app.t("tab_network")),
            SettingsTab("cloud", self.app.t("tab_cloud")),
            SettingsTab("backup", self.app.t("tab_backup")),
            SettingsTab("bindings", self.app.t("tab_bindings")),
        )
        if inspect.isawaitable(res):
            await res

        self._set_active("profile")
        await self._show("profile")

    def on_resize(self, event: events.Resize) -> None:
        try:
            w = int(event.size.width)
        except Exception:
            return

        try:
            nav = self.query_one("#settings_nav")
            content = self.query_one("#settings_content")
        except Exception:
            return

        if w < 70:
            try:
                nav.styles.width = 18
                nav.styles.min_width = 18
            except Exception:
                pass
            try:
                content.styles.padding = (1, 1)
            except Exception:
                pass
        else:
            try:
                nav.styles.width = 26
                nav.styles.min_width = 26
            except Exception:
                pass
            try:
                content.styles.padding = (1, 2)
            except Exception:
                pass

    def refresh_labels(self) -> None:
        try:
            self.query_one("#settings_title", Static).update(self.app.t("settings_title"))
            self.query_one("#settings_hint", Static).update(self.app.t("hint_esc"))
        except Exception:
            pass

        try:
            self.query_one("#btn_logout", Button).label = self.app.t("btn_logout")
        except Exception:
            pass

        try:
            for tab in self.query(SettingsTab):
                if tab.key == "profile":
                    tab.update(self.app.t("tab_profile"))
                elif tab.key == "privacy":
                    tab.update(self.app.t("tab_privacy"))
                elif tab.key == "lang":
                    tab.update(self.app.t("tab_language"))
                elif tab.key == "theme":
                    tab.update(self.app.t("tab_theme"))
                elif tab.key == "appearance":
                    tab.update(self.app.t("tab_appearance"))
                elif tab.key == "plugins":
                    tab.update(self.app.t("tab_plugins"))
                elif tab.key == "audio":
                    tab.update(self.app.t("tab_audio"))
                elif tab.key == "network":
                    tab.update(self.app.t("tab_network"))
                elif tab.key == "cloud":
                    tab.update(self.app.t("tab_cloud"))
                elif tab.key == "backup":
                    tab.update(self.app.t("tab_backup"))
                elif tab.key == "bindings":
                    tab.update(self.app.t("tab_bindings"))

        except Exception:
            pass

        try:
            hdr = self.query_one("#settings_header", Static)
            if getattr(self, "_active", None) == "profile":
                hdr.update(self.app.t("tab_profile"))
            elif getattr(self, "_active", None) == "privacy":
                hdr.update(self.app.t("tab_privacy"))
            elif getattr(self, "_active", None) == "lang":
                hdr.update(self.app.t("tab_language"))
            elif getattr(self, "_active", None) == "theme":
                hdr.update(self.app.t("tab_theme"))
            elif getattr(self, "_active", None) == "appearance":
                hdr.update(self.app.t("tab_appearance"))
            elif getattr(self, "_active", None) == "plugins":
                hdr.update(self.app.t("tab_plugins"))
            elif getattr(self, "_active", None) == "audio":
                hdr.update(self.app.t("tab_audio"))
            elif getattr(self, "_active", None) == "network":
                hdr.update(self.app.t("network_status_header"))
            elif getattr(self, "_active", None) == "cloud":
                hdr.update(self.app.t("cloud_header"))
            elif getattr(self, "_active", None) == "backup":
                hdr.update(self.app.t("backup_header"))
            elif getattr(self, "_active", None) == "bindings":
                hdr.update(self.app.t("bindings_header"))
        except Exception:
            pass

    async def action_close(self) -> None:
        await self._stop_audio_test()
        await self.app.pop_screen()

    async def action_prev_tab(self) -> None:
        self._idx = (self._idx - 1) % len(self._tabs)
        key = self._tabs[self._idx]
        self._set_active(key)
        await self._show(key)

    async def action_next_tab(self) -> None:
        self._idx = (self._idx + 1) % len(self._tabs)
        key = self._tabs[self._idx]
        self._set_active(key)
        await self._show(key)

    async def on_settings_tab_chosen(self, event: SettingsTab.Chosen) -> None:
        key = event.key
        if key in self._tabs:
            self._idx = self._tabs.index(key)
        self._set_active(key)
        await self._show(key)

    async def on_key_binding_input_changed(self, event: KeyBindingInput.Changed) -> None:
        kb_manager = get_keybindings_manager()
        if not kb_manager:
            return

        conflicts = kb_manager.find_conflicts(event.context, event.new_key, event.action)
        if conflicts:
            conflict_ctx, conflict_action = conflicts[0]
            action_i18n = ACTION_I18N_MAP.get(conflict_action, conflict_action)
            self.app.notify(self.app.t("bind_conflict", action=self.app.t(action_i18n)), severity="warning")
            old_key = kb_manager.get(event.context, event.action)
            event.sender.update_key(old_key)
            return

        kb_manager.set(event.context, event.action, event.new_key)
        self.app.notify(self.app.t("bind_saved"))

    async def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if not bid:
            return

        if bid == "audio_apply":
            await self._apply_audio_settings()
            return

        if bid == "appearance_save":
            await self._apply_appearance_settings()
            return

        if bid == "theme_sync_gtk":
            await self._sync_gtk_theme()
            return

        if bid == "theme_sync_qt":
            await self._sync_qt_theme()
            return

        if bid == "appearance_add_rule":
            from ui.dialogs import EditColorRuleDialog
            new_rule = ColorRule()
            dlg = EditColorRuleDialog(new_rule, -1)
            result = await self.app.push_screen_wait(dlg)
            if result and isinstance(result, tuple):
                _, updated_rule = result
                if hasattr(self, "_color_rules_manager"):
                    self._color_rules_manager.add_rule(updated_rule)
                    await self._refresh_rules_list()
            return

        if bid == "audio_test_toggle":
            await self._toggle_audio_test()
            return

        if bid.startswith("status_btn_"):

            new_status = bid.replace("status_btn_", "")

            for btn in self.query(".status_btn"):
                if btn.id == bid:
                    btn.add_class("active")
                else:
                    btn.remove_class("active")

            return

        if bid == "save_profile_btn":

            status = "online"
            for btn in self.query(".status_btn"):
                if "active" in btn.classes:
                    status = btn.id.replace("status_btn_", "")
                    break

            bio_input = self.query_one("#bio_input", TextArea)
            bio = bio_input.text

            if hasattr(self.app, "update_profile"):
                await self.app.update_profile(status=status, bio=bio)
                self.app.notify(self.app.t("profile_saved"))
            return

        if bid == "visibility_btn":

            profile = getattr(self.app, "my_profile", {})

            current_vis = profile.get("visible", 1)

            new_vis = 0 if current_vis == 1 else 1

            if hasattr(self.app, "update_profile"):
                await self.app.update_profile(visibility=new_vis)

                await self._show("privacy")
            return

        if bid.startswith("unblock_"):
            fp = bid.replace("unblock_", "")
            if hasattr(self.app, "unblock_user"):
                await self.app.unblock_user(fp)

                await self._show("privacy")
            return

        if bid == "block_btn":
            try:
                target = self.query_one("#block_target", Input).value.strip()
            except Exception:
                target = ""
            if target and hasattr(self.app, "block_user"):
                await self.app.block_user(target)
                await self._show("privacy")
            return

        if bid == "btn_show_ip":
            lbl = self.query_one("#ip_display", Static)
            lbl.update(self.app.t("net_fetching"))
            ip = await self.app.net.get_public_ip()
            lbl.update(f"[bold]{ip}[/]")
            return

        if bid == "cloud_sync_up":
            if hasattr(self.app, "cloud_sync_up"):
                await self.app.cloud_sync_up()
            await self._show("cloud")
            return

        if bid == "cloud_sync_down":
            if hasattr(self.app, "cloud_sync_down"):
                try:
                    res = self.app.cloud_sync_down()

                    import inspect as _inspect
                    import asyncio as _asyncio

                    if _inspect.isawaitable(res):
                        _asyncio.create_task(res)
                except Exception:
                    pass
            return

        if bid == "cloud_mode_toggle":
            try:
                cur = (self.app.db.get_setting("cloud_chat_mode") or "all").strip().lower()
                nxt = "selected" if cur != "selected" else "all"
                self.app.db.set_setting("cloud_chat_mode", nxt)
            except Exception:
                pass
            await self._show("cloud")
            return

        if bid == "backup_create":
            if hasattr(self.app, "create_backup"):

                is_encrypted = self.app.is_db_encrypted() if hasattr(self.app, 'is_db_encrypted') else False

                if is_encrypted:

                    p = self.app.create_backup()
                    if p:
                        self.app.notify(self.app.t("backup_created") + " 🔒")
                else:

                    await self._ask_backup_passphrase()
                    return
            await self._show("backup")
            return

        if bid == "backup_restore":
            try:
                lv = self.query_one("#backup_list", ListView)
                it = lv.highlighted_child
                sel_id = getattr(it, "id", "") if it else ""
                path = getattr(self, "_backup_map", {}).get(sel_id)
                if path and hasattr(self.app, "restore_backup_file"):
                    self.app.restore_backup_file(path)
            except Exception:
                pass
            return

        if bid == "backup_delete":
            try:
                lv = self.query_one("#backup_list", ListView)
                it = lv.highlighted_child
                sel_id = getattr(it, "id", "") if it else ""
                path = getattr(self, "_backup_map", {}).get(sel_id)
                if path and hasattr(self.app, "delete_backup_file"):
                    self.app.delete_backup_file(path)
                    self.app.notify(self.app.t("backup_deleted"))
            except Exception:
                pass
            await self._show("backup")
            return

        if bid == "bind_reset_all":
            kb_manager = get_keybindings_manager()
            if kb_manager:
                kb_manager.reset()
                self.app.notify(self.app.t("bind_reset_done"))
                await self._show("bindings")
            return

        if bid.startswith("kb__"):
            parts = bid.split("__")
            if len(parts) == 3:
                _, context, action = parts
                for kb_input in self.query(KeyBindingInput):
                    if kb_input._capturing:
                        kb_input.stop_capture()

                for kb_input in self.query(KeyBindingInput):
                    if kb_input.context == context and kb_input.action == action:
                        kb_input.start_capture()
                        break
            return


        if bid.startswith("kbr__"):
            parts = bid.split("__")
            if len(parts) == 3:
                _, context, action = parts
                kb_manager = get_keybindings_manager()
                if kb_manager:
                    kb_manager.reset(context, action)
                    for kb_input in self.query(KeyBindingInput):
                        if kb_input.context == context and kb_input.action == action:
                            new_key = kb_manager.get(context, action)
                            kb_input.update_key(new_key)
                            break
                    self.app.notify(self.app.t("bind_saved"))
            return

        if bid == "btn_logout":

            if hasattr(self.app, "logout_and_restart"):
                self.app.logout_and_restart()
            else:
                self.app.exit(result="logout")
            return

    async def on_list_view_selected(self, event: ListView.Selected) -> None:

        lvid = getattr(event.list_view, "id", "") or ""
        wid = getattr(event.item, "id", "") or ""

        if lvid == "lang_list":
            if wid.startswith("lang_"):
                code = wid.replace("lang_", "")
                self.app.set_lang(code)
            self.refresh_labels()
            await self._show("lang")
            return

        if lvid == "theme_list":
            if wid.startswith("theme_"):
                self.app.set_theme(wid.replace("theme_", "", 1).replace("_", "-"))
            return

        if lvid == "cloud_chat_list":

            try:
                chat_id = getattr(self, "_cloud_chat_map", {}).get(wid)
                if not chat_id:
                    return

                try:
                    cur_mode = (self.app.db.get_setting("cloud_chat_mode") or "all").strip().lower()
                    if cur_mode != "selected":
                        self.app.db.set_setting("cloud_chat_mode", "selected")
                except Exception:
                    pass

                try:
                    raw = self.app.db.get_setting("cloud_selected_chats_json")
                    cur = json.loads(raw) if raw else []
                except Exception:
                    cur = []
                if not isinstance(cur, list):
                    cur = []
                s = set(str(x) for x in cur if isinstance(x, str) and x)
                if chat_id in s:
                    s.remove(chat_id)
                else:
                    s.add(chat_id)
                try:
                    self.app.db.set_setting("cloud_selected_chats_json", json.dumps(list(s), ensure_ascii=False))
                except Exception:
                    pass

                try:
                    seed_hint = ""
                    try:
                        seed_hint = str(getattr(self, "_cloud_chat_seed_hint", {}).get(chat_id) or "")
                    except Exception:
                        seed_hint = ""
                    checked = "☑" if chat_id in s else "☐"
                    label = f"{checked} {chat_id}{seed_hint}"
                    try:
                        from textual.widgets import Static as _Static

                        st = event.item.query_one(_Static)
                        st.update(label)
                    except Exception:

                        try:
                            event.item.remove_children()
                        except Exception:
                            pass
                        try:
                            event.item.mount(Static(label))
                        except Exception:
                            pass
                except Exception:
                    pass
            except Exception:
                pass
            return

    async def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
        cid = event.checkbox.id or ""
        v = bool(event.value)

        if cid == "cloud_enabled":
            try:
                self.app.db.set_setting("cloud_user_enabled", "1" if v else "0")
            except Exception:
                pass
            return

        if cid == "cloud_snapshots":
            try:
                self.app.db.set_setting("cloud_user_publish_snapshots", "1" if v else "0")
            except Exception:
                pass
            try:
                if v and getattr(self.app, "net", None) is not None:
                    self.app.net.request_cloud_publish()
            except Exception:
                pass
            return

        if cid == "cloud_files":
            try:
                self.app.db.set_setting("cloud_user_publish_files", "1" if v else "0")
            except Exception:
                pass
            return

    def _set_active(self, key: str) -> None:
        self._active = key
        for tab in self.query(SettingsTab):
            tab.set_active(tab.key == key)

        hdr = self.query_one("#settings_header", Static)
        if key == "profile":
            hdr.update(self.app.t("tab_profile"))
        elif key == "privacy":
            hdr.update(self.app.t("tab_privacy"))
        elif key == "lang":
            hdr.update(self.app.t("tab_language"))
        elif key == "theme":
            hdr.update(self.app.t("tab_theme"))
        elif key == "audio":
            hdr.update(self.app.t("tab_audio"))
        elif key == "network":
            hdr.update(self.app.t("network_status_header"))
        elif key == "cloud":
            hdr.update(self.app.t("cloud_header"))
        elif key == "backup":
            hdr.update(self.app.t("backup_header"))

    async def _stop_audio_test(self) -> None:
        eng = getattr(self, "_audio_test_engine", None)
        if eng is not None:
            try:
                import asyncio as _asyncio

                _asyncio.create_task(eng.stop())
            except Exception:
                pass
        self._audio_test_engine = None

    def _audio_percent_to_gain(self, v: str, *, default: float = 1.0) -> float:
        try:
            pct = int(str(v or "").strip() or int(default * 100))
        except Exception:
            pct = int(default * 100)
        pct = max(0, min(300, pct))
        return float(pct) / 100.0

    def _audio_gain_to_percent_str(self, g: float) -> str:
        try:
            pct = int(round(float(g) * 100.0))
        except Exception:
            pct = 100
        pct = max(0, min(300, pct))
        return str(pct)

    def _audio_parse_device(self, v) -> int | None:
        s = str(v or "").strip().lower()
        if not s or s == "default":
            return None
        try:
            return int(s)
        except Exception:
            return None

    async def _apply_appearance_settings(self) -> None:

        try:
            tz_sel = self.query_one("#appearance_tz", Select)
            fmt_sel = self.query_one("#appearance_fmt", Select)
            tz_val = str(getattr(tz_sel, "value", "UTC") or "UTC")
            fmt_val = str(getattr(fmt_sel, "value", "24h") or "24h")
            self.app.db.set_setting("display_timezone", tz_val)
            self.app.db.set_setting("display_time_format", fmt_val)
            set_time_display_settings(tz=tz_val, fmt=fmt_val)
        except Exception:
            pass

        try:
            spacing_slider = self.query_one("#appearance_spacing", NumberSlider)
            spacing_val = int(spacing_slider.value)
            self.app.db.set_setting("appearance_msg_spacing", str(spacing_val))
        except Exception:
            pass

        try:
            btn_style_sel = self.query_one("#appearance_btn_style", Select)
            btn_style_val = str(getattr(btn_style_sel, "value", "flat") or "flat")
            self.app.db.set_setting("button_style", btn_style_val)
            self.app.apply_button_style(btn_style_val)
        except Exception:
            pass

        try:
            my_color_picker = self.query_one("#appearance_my_color", ColorPicker)
            if hasattr(self, "_color_rules_manager"):
                self._color_rules_manager.my_color = my_color_picker.color
        except Exception:
            pass

        try:
            default_color_picker = self.query_one("#appearance_default_color", ColorPicker)
            if hasattr(self, "_color_rules_manager"):
                self._color_rules_manager.default_color = default_color_picker.color
        except Exception:
            pass

        try:
            if hasattr(self, "_color_rules_manager"):
                self.app._color_rules_manager = self._color_rules_manager
        except Exception:
            pass

        try:
            if hasattr(self.app, "_color_rules_manager") and self.app._color_rules_manager:
                self.app._color_rules_manager._load()
        except Exception:
            pass

        try:

            self.app._rendered_peer = None
            self.app._rendered_ids = []

            if hasattr(self.app, "_msg_widgets"):
                self.app._msg_widgets = {}

            if hasattr(self.app, "_chat_oldest_cursor"):
                self.app._chat_oldest_cursor = None
            if hasattr(self.app, "_chat_has_older"):
                self.app._chat_has_older = False
            if hasattr(self.app, "_chat_window_size"):
                self.app._chat_window_size = 0

            self.app.request_refresh_active_chat(immediate=True)
        except Exception:
            pass

        try:
            self.app.notify(self.app.t("appearance_saved"))
        except Exception:
            pass

    async def _sync_gtk_theme(self) -> None:
        gtk_info = get_gtk_theme_colors()
        if gtk_info and gtk_info.get("bg") and gtk_info.get("text"):

            await self._apply_system_theme("system-gtk", gtk_info)
            self.app.notify(self.app.t("theme_sync_success"))
        else:
            self.app.notify(self.app.t("theme_sync_failed"), severity="warning")

    async def _sync_qt_theme(self) -> None:
        qt_info = get_qt_theme_colors()
        if qt_info and qt_info.get("bg") and qt_info.get("text"):

            await self._apply_system_theme("system-qt", qt_info)
            self.app.notify(self.app.t("theme_sync_success"))
        else:
            self.app.notify(self.app.t("theme_sync_failed"), severity="warning")

    async def _apply_system_theme(self, theme_name: str, colors: dict) -> None:
        from ui.themes import THEMES, css_for_theme
        from textual.css.stylesheet import Stylesheet

        theme_colors = {k: v for k, v in colors.items() if k != "is_dark" and v}

        THEMES[theme_name] = theme_colors

        css = css_for_theme(theme_name, theme_colors)

        try:

            self.app.stylesheet.add_source(css, path=f"<{theme_name}>", is_default_css=False, tie_breaker=999)
            self.app.stylesheet.reparse()
        except Exception:
            pass

        self.app.set_theme(theme_name, persist=True)

        try:
            self.app.refresh(layout=True)
        except Exception:
            pass

    async def _apply_theme(self, theme_name: str) -> None:
        self.app.set_theme(theme_name, persist=True)

        try:
            lv = self.query_one("#theme_list", ListView)
            names = theme_list()
            lv.index = names.index(theme_name) if theme_name in names else 0
        except Exception:
            pass

    async def _refresh_rules_list(self) -> None:
        try:
            container = self.query_one("#rules_list_container", Vertical)
            await container.remove_children()

            if not hasattr(self, "_color_rules_manager"):
                return

            rules = self._color_rules_manager.rules
            if not rules:
                await container.mount(Static(self.app.t("appearance_no_rules"), classes="section_desc"))
                return

            total = len(rules)
            for i, rule in enumerate(rules):
                item = ColorRuleItem(rule, i, total=total, id=f"rule_item_{i}")
                await container.mount(item)
        except Exception:
            pass

    async def on_color_rule_item_toggle_requested(self, event: ColorRuleItem.ToggleRequested) -> None:
        if hasattr(self, "_color_rules_manager"):
            self._color_rules_manager.toggle_rule(event.index)
            await self._refresh_rules_list()

    async def on_color_rule_item_delete_requested(self, event: ColorRuleItem.DeleteRequested) -> None:
        if hasattr(self, "_color_rules_manager"):
            self._color_rules_manager.delete_rule(event.index)
            await self._refresh_rules_list()

    async def on_color_rule_item_edit_requested(self, event: ColorRuleItem.EditRequested) -> None:
        if hasattr(self, "_color_rules_manager"):
            rule = self._color_rules_manager.rules[event.index] if 0 <= event.index < len(self._color_rules_manager.rules) else None
            if rule:
                from ui.dialogs import EditColorRuleDialog
                dlg = EditColorRuleDialog(rule, event.index)
                result = await self.app.push_screen_wait(dlg)
                if result and isinstance(result, tuple):
                    idx, updated_rule = result
                    self._color_rules_manager.update_rule(idx, updated_rule)
                    await self._refresh_rules_list()

    async def on_color_rule_item_move_up_requested(self, event: ColorRuleItem.MoveUpRequested) -> None:
        if hasattr(self, "_color_rules_manager"):
            self._color_rules_manager.move_rule_up(event.index)
            await self._refresh_rules_list()

    async def on_color_rule_item_move_down_requested(self, event: ColorRuleItem.MoveDownRequested) -> None:
        if hasattr(self, "_color_rules_manager"):
            self._color_rules_manager.move_rule_down(event.index)
            await self._refresh_rules_list()

    async def _apply_audio_settings(self) -> None:
        try:
            in_sel = self.query_one("#audio_in_dev", Select)
            out_sel = self.query_one("#audio_out_dev", Select)
            mic_bar = self.query_one("#audio_mic_bar", CallVolumeBar)
            spk_bar = self.query_one("#audio_spk_bar", CallVolumeBar)
        except Exception:
            return

        in_val = str(getattr(in_sel, "value", "default") or "default")
        out_val = str(getattr(out_sel, "value", "default") or "default")
        mic_gain = float(getattr(mic_bar, "gain", 1.0))
        spk_gain = float(getattr(spk_bar, "gain", 1.0))

        try:
            prev_in = str(self.app.db.get_setting("audio_input_device") or "default")
        except Exception:
            prev_in = "default"
        try:
            prev_out = str(self.app.db.get_setting("audio_output_device") or "default")
        except Exception:
            prev_out = "default"
        devices_changed = (prev_in.strip() != in_val.strip()) or (prev_out.strip() != out_val.strip())

        try:
            self.app.db.set_setting("audio_input_device", in_val)
            self.app.db.set_setting("audio_output_device", out_val)
            self.app.db.set_setting("audio_mic_gain", str(mic_gain))
            self.app.db.set_setting("audio_spk_gain", str(spk_gain))
        except Exception:
            pass

        try:
            if getattr(self.app, "_voice", None) is not None:
                try:
                    self.app._voice.set_mic_gain(mic_gain)
                except Exception:
                    pass
                try:
                    self.app._voice.set_gain(spk_gain)
                except Exception:
                    pass
        except Exception:
            pass

        if devices_changed and getattr(self, "_audio_test_engine", None) is not None:
            try:
                self.app.notify(self.app.t("audio_restart_test_hint"), severity="warning")
            except Exception:
                pass

        try:
            self.app.notify(self.app.t("audio_saved"))
        except Exception:
            pass

    async def _toggle_audio_test(self, *, force_start: bool = False) -> None:
        from voice import VoiceEngine, VoiceParams

        if getattr(self, "_audio_test_engine", None) is not None and not force_start:
            await self._stop_audio_test()
            try:
                btn = self.query_one("#audio_test_toggle", Button)
                btn.label = self.app.t("audio_test_start")
            except Exception:
                pass
            return

        if getattr(self.app, "_voice", None) is not None:
            try:
                self.app.notify(self.app.t("audio_test_stop_call_first"), severity="warning")
            except Exception:
                pass
            return

        try:
            in_sel = self.query_one("#audio_in_dev", Select)
            out_sel = self.query_one("#audio_out_dev", Select)
            mic_bar = self.query_one("#audio_mic_bar", CallVolumeBar)
            spk_bar = self.query_one("#audio_spk_bar", CallVolumeBar)
        except Exception:
            return

        mic_gain = float(getattr(mic_bar, "gain", 1.0))
        spk_gain = float(getattr(spk_bar, "gain", 1.0))
        in_dev = self._audio_parse_device(getattr(in_sel, "value", None))
        out_dev = self._audio_parse_device(getattr(out_sel, "value", None))

        params = VoiceParams(rate=48000, channels=1, frame_ms=20)
        eng = VoiceEngine(
            params=params,
            send_pcm_cb=None,
            input_device=in_dev,
            output_device=out_dev,
            mic_gain=mic_gain,
            speaker_gain=spk_gain,
            monitor_self=True,
            enable_tx=False,
        )
        try:
            await eng.start()
        except Exception:
            try:
                self.app.notify(self.app.t("audio_devices_unavailable"), severity="error")
            except Exception:
                pass
            return

        self._audio_test_engine = eng
        try:
            btn = self.query_one("#audio_test_toggle", Button)
            btn.label = self.app.t("audio_test_stop")
        except Exception:
            pass

    async def on_call_volume_bar_changed(self, event: CallVolumeBar.Changed) -> None:
        wid = getattr(event.sender, "id", "") or ""
        g = float(getattr(event, "gain", 1.0))
        g = max(0.0, min(3.0, g))

        if wid == "audio_mic_bar":
            try:
                self.app.db.set_setting("audio_mic_gain", str(g))
            except Exception:
                pass
            try:
                if getattr(self.app, "_voice", None) is not None:
                    self.app._voice.set_mic_gain(g)
            except Exception:
                pass
            try:
                if getattr(self, "_audio_test_engine", None) is not None:
                    self._audio_test_engine.set_mic_gain(g)
            except Exception:
                pass
            return

        if wid == "audio_spk_bar":
            try:
                self.app.db.set_setting("audio_spk_gain", str(g))
            except Exception:
                pass
            try:
                if getattr(self.app, "_voice", None) is not None:
                    self.app._voice.set_gain(g)
            except Exception:
                pass
            try:
                if getattr(self, "_audio_test_engine", None) is not None:
                    self._audio_test_engine.set_gain(g)
            except Exception:
                pass
            return

    async def _clear_content(self) -> None:
        cont = self.query_one("#settings_scroll", VerticalScroll)
        await cont.remove_children()

    async def _mount(self, *widgets):
        cont = self.query_one("#settings_scroll", VerticalScroll)
        r = cont.mount(*widgets)
        if inspect.isawaitable(r):
            await r

    async def _ask_backup_passphrase(self) -> None:
        from ui.dialogs import ConfirmDialog

        title = self.app.t("backup_encrypt_title")
        body = self.app.t("backup_encrypt_body")

        wants_encryption = await self.app.push_screen_wait(
            ConfirmDialog(title=title, body=body, ok_key="yes", cancel_key="no")
        )

        if wants_encryption:

            passphrase = await self._get_backup_passphrase()
            if passphrase:
                p = self.app.create_backup(passphrase=passphrase)
                if p:
                    self.app.notify(self.app.t("backup_created") + " 🔒")
            else:

                return
        else:

            p = self.app.create_backup()
            if p:
                self.app.notify(self.app.t("backup_created"))

        await self._show("backup")

    async def _get_backup_passphrase(self) -> str | None:
        from ui.dialogs import TextPromptDialog

        title = self.app.t("backup_passphrase_title")
        prompt = self.app.t("backup_passphrase_prompt")

        result = await self.app.push_screen_wait(
            TextPromptDialog(title=title, prompt=prompt, password=True)
        )

        if result and len(result) >= 4:
            return result
        elif result:
            self.app.notify(self.app.t("backup_passphrase_short"), severity="error")
            return None
        return None

    async def _show(self, which: str) -> None:
        if which != "audio":
            await self._stop_audio_test()
        await self._clear_content()

        if which == "profile":
            ident = getattr(self.app, "ident", None)
            alias = ident.alias if ident else ""
            fp = ident.fp if ident else ""
            acc = getattr(self.app, "my_account", None) or {}
            created = acc.get("created_at")
            created_s = (fmt_date(int(created)) + " " + fmt_time(int(created))) if created else "?"

            profile = getattr(self.app, "my_profile", {})
            current_status = profile.get("status", "online")
            current_bio = profile.get("bio") or ""

            status_btns = []
            for s in ["online", "idle", "dnd", "offline"]:
                btn = Button(self.app.t(f"status_{s}"), id=f"status_btn_{s}", classes=f"status_btn {s}")
                if s == current_status:
                    btn.add_class("active")
                status_btns.append(btn)

            status_container = Horizontal(*status_btns, classes="status_selector")

            bio_ta = TextArea(current_bio, id="bio_input", show_line_numbers=False)
            bio_ta.styles.height = 5

            save_btn = Button(self.app.t("save_btn"), id="save_profile_btn", variant="primary", classes="action_btn")
            save_btn.styles.margin = (1, 0, 2, 0)

            await self._mount(
                Static(self.app.t("profile_alias", v=alias), classes="profile_info"),
                Static(self.app.t("profile_id", v=fp), classes="profile_info"),
                Static(self.app.t("profile_created", v=created_s), classes="profile_info"),
                Label(self.app.t("status_label"), classes="section_label"),
                status_container,
                Label(self.app.t("bio_label"), classes="section_label"),
                bio_ta,
                save_btn,
            )
            return

        if which == "privacy":
            profile = getattr(self.app, "my_profile", {})

            visibility = profile.get("visible", 1)

            vis_btn_label = self.app.t("visibility_public") if visibility == 1 else self.app.t("visibility_private")
            vis_btn_variant = "success" if visibility == 1 else "error"

            blocked_list = ListView(id="blocked_list")
            blocked_users = getattr(self.app, "blocked_users", [])
            blocked_items: list[ListItem] = []
            for b_fp in blocked_users:
                blocked_items.append(
                    ListItem(
                        Horizontal(
                            Static(b_fp, classes="blocked_user_fp"),
                            Button(
                                self.app.t("unblock_btn"),
                                id=f"unblock_{b_fp}",
                                classes="unblock_btn",
                                variant="error",
                            ),
                            classes="blocked_user_row",
                        ),
                        id=f"blocked_item_{b_fp}",
                    )
                )

            await self._mount(
                Label(self.app.t("visibility_label"), classes="section_label"),
                Button(vis_btn_label, id="visibility_btn", variant=vis_btn_variant, classes="action_btn"),
                Label(self.app.t("block_user"), classes="section_label"),
                Input(placeholder=self.app.t("block_placeholder"), id="block_target"),
                Button(self.app.t("block_user"), id="block_btn", variant="error", classes="action_btn"),
                Label(self.app.t("blocked_users_label"), classes="section_label"),
                blocked_list
            )

            if blocked_items:
                r = blocked_list.extend(blocked_items)
                if inspect.isawaitable(r):
                    await r
            return

        if which == "lang":
            cur = getattr(self.app, "lang", "en")
            lv = ListView(id="lang_list")
            await self._mount(lv)

            items = []

            lang_codes = sorted(list(I18N.keys()))

            for code in lang_codes:
                key = f"lang_{code}"

                native_name = translate_func(cur, key)
                english_name = translate_func("en", key)

                label = f"{native_name} | {english_name}"
                items.append(ListItem(Static(label), id=f"lang_{code}"))

            r = lv.extend(items)
            if inspect.isawaitable(r):
                await r
            try:
                if cur in lang_codes:
                    lv.index = lang_codes.index(cur)
            except Exception:
                pass
            lv.focus()
            return

        if which == "theme":
            cur = getattr(self.app, "theme_name", "default")

            sync_container = Horizontal(
                Button(self.app.t("theme_sync_gtk"), id="theme_sync_gtk", classes="theme_sync_btn"),
                Button(self.app.t("theme_sync_qt"), id="theme_sync_qt", classes="theme_sync_btn"),
                classes="theme_sync_container"
            )
            await self._mount(sync_container)

            lv = ListView(id="theme_list")
            await self._mount(lv)
            items = []
            names = theme_list()
            for n in names:
                key = "theme_" + n.replace("-", "_")
                items.append(ListItem(Static(self.app.t(key)), id="theme_" + n.replace("-", "_")))

            r = lv.extend(items)
            if inspect.isawaitable(r):
                await r

            try:
                lv.index = names.index(cur) if cur in names else 0
            except Exception:
                pass
            lv.focus()
            return

        if which == "appearance":

            if hasattr(self.app, "_color_rules_manager") and self.app._color_rules_manager:
                self._color_rules_manager = self.app._color_rules_manager
            else:
                self._color_rules_manager = ColorRulesManager(self.app.db)
                self.app._color_rules_manager = self._color_rules_manager

            try:
                cur_tz = (self.app.db.get_setting("display_timezone") or "UTC").strip()
            except Exception:
                cur_tz = "UTC"
            try:
                cur_fmt = (self.app.db.get_setting("display_time_format") or "24h").strip()
            except Exception:
                cur_fmt = "24h"
            try:
                cur_spacing = int(self.app.db.get_setting("appearance_msg_spacing") or 1)
            except Exception:
                cur_spacing = 1
            try:
                if hasattr(self, "_color_rules_manager") and self._color_rules_manager:
                    cur_default_color = self._color_rules_manager.default_color
                else:
                    cur_default_color = "#cccccc"
            except Exception:
                cur_default_color = "#cccccc"

            tz_opts = [(label, val) for val, label in TIMEZONE_OPTIONS]
            fmt_opts = [(label, val) for val, label in TIME_FORMAT_OPTIONS]

            tz_select = Select(tz_opts, id="appearance_tz", value=cur_tz)
            fmt_select = Select(fmt_opts, id="appearance_fmt", value=cur_fmt)

            cat_time = AppearanceCategory(self.app.t("appearance_cat_time"), id="cat_time")

            cat_messages = AppearanceCategory(self.app.t("appearance_cat_messages"), id="cat_messages")

            cat_colors = AppearanceCategory(self.app.t("appearance_cat_colors"), id="cat_colors")

            cat_rules = AppearanceCategory(self.app.t("appearance_cat_rules"), id="cat_rules")

            await self._mount(
                cat_time,
                cat_messages,
                cat_colors,
                cat_rules,
                Button(self.app.t("save_btn"), id="appearance_save", variant="primary", classes="action_btn"),
            )

            try:
                time_content = cat_time.get_content()
                await time_content.mount(
                    Label(self.app.t("appearance_timezone"), classes="section_label"),
                    Static(self.app.t("appearance_timezone_desc"), classes="section_desc"),
                    tz_select,
                    Label(self.app.t("appearance_time_format"), classes="section_label"),
                    Static(self.app.t("appearance_time_format_desc"), classes="section_desc"),
                    fmt_select,
                )
            except Exception:
                pass

            try:
                msg_content = cat_messages.get_content()
                spacing_slider = NumberSlider(
                    value=cur_spacing, min_val=0, max_val=5, step=1,
                    unit="lines", id="appearance_spacing"
                )

                cur_btn_style = self.app.db.get_setting("button_style") or "flat"
                btn_style_options = [
                    (self.app.t("btn_style_flat"), "flat"),
                    (self.app.t("btn_style_default"), "default"),
                ]
                btn_style_select = Select(btn_style_options, value=cur_btn_style, id="appearance_btn_style")

                await msg_content.mount(
                    Label(self.app.t("appearance_button_style"), classes="section_label"),
                    Static(self.app.t("appearance_button_style_desc"), classes="section_desc"),
                    btn_style_select,
                    Label(self.app.t("appearance_msg_spacing"), classes="section_label"),
                    Static(self.app.t("appearance_msg_spacing_desc"), classes="section_desc"),
                    spacing_slider,
                )
            except Exception:
                pass

            try:
                colors_content = cat_colors.get_content()
                my_color_picker = ColorPicker(value=self._color_rules_manager.my_color, id="appearance_my_color")
                default_color_picker = ColorPicker(value=cur_default_color, id="appearance_default_color")
                await colors_content.mount(
                    Label(self.app.t("appearance_my_color"), classes="section_label"),
                    Static(self.app.t("appearance_my_color_desc"), classes="section_desc"),
                    my_color_picker,
                    Label(self.app.t("appearance_default_color"), classes="section_label"),
                    Static(self.app.t("appearance_default_color_desc"), classes="section_desc"),
                    default_color_picker,
                )
            except Exception:
                pass

            try:
                rules_content = cat_rules.get_content()
                await rules_content.mount(
                    Label(self.app.t("appearance_rules_title"), classes="section_label"),
                    Static(self.app.t("appearance_rules_desc"), classes="section_desc"),
                    Vertical(id="rules_list_container"),
                    Button(self.app.t("appearance_add_rule"), id="appearance_add_rule", variant="default", classes="action_btn"),
                )
                await self._refresh_rules_list()
            except Exception:
                pass

            return

        if which == "audio":
            from voice import list_audio_devices

            try:
                in_raw = (self.app.db.get_setting("audio_input_device") or "default").strip()
            except Exception:
                in_raw = "default"
            try:
                out_raw = (self.app.db.get_setting("audio_output_device") or "default").strip()
            except Exception:
                out_raw = "default"
            try:
                mic_gain = float(self.app.db.get_setting("audio_mic_gain") or 1.0)
            except Exception:
                mic_gain = 1.0
            try:
                spk_gain = float(self.app.db.get_setting("audio_spk_gain") or 1.0)
            except Exception:
                spk_gain = 1.0

            devs = list_audio_devices()
            in_opts = [(self.app.t("audio_default_device"), "default")]
            out_opts = [(self.app.t("audio_default_device"), "default")]
            for d in devs:
                try:
                    idx = int(d.get("index", 0))
                    name = str(d.get("name", ""))
                    max_in = int(d.get("max_input_channels", 0))
                    max_out = int(d.get("max_output_channels", 0))
                except Exception:
                    continue
                label = f"{idx}: {name}".strip()
                if max_in > 0:
                    in_opts.append((label, str(idx)))
                if max_out > 0:
                    out_opts.append((label, str(idx)))

            in_sel = Select(in_opts, id="audio_in_dev", value=in_raw)
            out_sel = Select(out_opts, id="audio_out_dev", value=out_raw)

            mic_bar = CallVolumeBar(gain=mic_gain, id="audio_mic_bar")
            spk_bar = CallVolumeBar(gain=spk_gain, id="audio_spk_bar")

            try:
                mic_bar.styles.height = 1
                mic_bar.styles.width = 46
            except Exception:
                pass
            try:
                spk_bar.styles.height = 1
                spk_bar.styles.width = 46
            except Exception:
                pass

            await self._mount(
                Label(self.app.t("audio_input_device"), classes="section_label"),
                in_sel,
                Label(self.app.t("audio_output_device"), classes="section_label"),
                out_sel,
                Label(self.app.t("audio_mic_volume"), classes="section_label"),
                mic_bar,
                Label(self.app.t("audio_speaker_volume"), classes="section_label"),
                spk_bar,
                Button(self.app.t("audio_apply"), id="audio_apply", variant="primary", classes="action_btn"),
                Button(self.app.t("audio_test_start"), id="audio_test_toggle", variant="success", classes="action_btn"),
            )
            return

        if which == "network":
            conf = self.app.global_config.get("network", {})
            mode = conf.get("mode", "direct")

            is_connected = False
            if self.app.net.ws:

                if hasattr(self.app.net.ws, "open"):
                    is_connected = self.app.net.ws.open
                elif hasattr(self.app.net.ws, "state"):

                    state = self.app.net.ws.state
                    is_connected = getattr(state, "name", "") == "OPEN"

            status_key = "net_status_connected" if is_connected else "net_status_disconnected"
            status_text = self.app.t(status_key)
            color = "green" if is_connected else "red"

            panel = Vertical(
                Label(self.app.t("net_mode_label", mode=mode.upper()), classes="net_label"),
                Label(f"{self.app.t('net_status_label', status='')}[{color}]{status_text}[/]", classes="net_label"),
                Label(self.app.t("net_public_ip"), classes="net_label"),
                Static(self.app.t("net_hidden"), id="ip_display"),
                Button(self.app.t("net_show_ip"), id="btn_show_ip", variant="primary"),
                id="network_status_panel"
            )

            await self._mount(panel)
            return

        if which == "cloud":

            try:
                enabled = (self.app.db.get_setting("cloud_user_enabled") or "0").strip().lower() in ("1", "true", "yes", "on")
            except Exception:
                enabled = False
            try:
                snaps = (self.app.db.get_setting("cloud_user_publish_snapshots") or "0").strip().lower() in ("1", "true", "yes", "on")
            except Exception:
                snaps = False
            try:
                files = (self.app.db.get_setting("cloud_user_publish_files") or "0").strip().lower() in ("1", "true", "yes", "on")
            except Exception:
                files = False

            try:
                mode = (self.app.db.get_setting("cloud_chat_mode") or "all").strip().lower()
                mode = mode if mode in ("all", "selected") else "all"
            except Exception:
                mode = "all"

            try:
                raw = self.app.db.get_setting("cloud_selected_chats_json")
                selected = json.loads(raw) if raw else []
            except Exception:
                selected = []
            if not isinstance(selected, list):
                selected = []
            selected_set = set(str(x) for x in selected if isinstance(x, str) and x)

            last_pub_ts = self.app.db.get_setting("cloud_last_publish_ts") or ""
            last_pub_sha = self.app.db.get_setting("cloud_last_publish_sha") or ""
            last_fetch_ts = self.app.db.get_setting("cloud_last_fetch_ts") or ""
            last_fetch_sha = self.app.db.get_setting("cloud_last_fetch_sha") or ""

            cloud_enabled_chk = Checkbox(self.app.t("cloud_enabled_label"), id="cloud_enabled", value=enabled)
            cloud_snaps_chk = Checkbox(self.app.t("cloud_snapshots_label"), id="cloud_snapshots", value=snaps)
            cloud_files_chk = Checkbox(self.app.t("cloud_files_label"), id="cloud_files", value=files)

            mode_label = self.app.t("cloud_mode_selected") if mode == "selected" else self.app.t("cloud_mode_all")

            chat_lv = ListView(id="cloud_chat_list")
            self._cloud_chat_map = {}
            self._cloud_chat_seed_hint = {}
            chat_items = []
            chats = []
            try:
                chats = list(self.app.db.list_chats())
            except Exception:
                chats = []
            for c in chats:
                peer = c.get("peer_alias")
                if not isinstance(peer, str) or not peer:
                    continue

                seed_hint = ""
                try:
                    fid = self.app.db.get_latest_file_id(peer)
                    if fid and getattr(self.app, "net", None) is not None:
                        seeds = await self.app.net.find_seeds(fid, timeout=3.0)
                        online_n = 0
                        try:
                            online_n = len(set(seeds))
                        except Exception:
                            online_n = len(list(seeds))
                        total_n = None
                        try:
                            total_n = int(getattr(self.app.net, "_file_seeds_total", {}).get(fid))
                        except Exception:
                            total_n = None
                        if isinstance(total_n, int) and total_n > 0 and total_n >= online_n:
                            seed_hint = f" • seeds:{online_n}/{total_n}"
                        else:
                            seed_hint = f" • seeds:{online_n}"
                except Exception:
                    seed_hint = ""

                try:
                    self._cloud_chat_seed_hint[str(peer)] = str(seed_hint)
                except Exception:
                    pass

                checked = "☑" if peer in selected_set else "☐"
                label = f"{checked} {peer}{seed_hint}"
                it = ListItem(Static(label), id=f"cloudchat_{len(self._cloud_chat_map)}")
                self._cloud_chat_map[it.id] = peer
                chat_items.append(it)

            status = (
                f"{self.app.t('cloud_status')}\n"
                f"{self.app.t('cloud_last_publish')}: {last_pub_ts} {last_pub_sha}\n"
                f"{self.app.t('cloud_last_fetch')}: {last_fetch_ts} {last_fetch_sha}"
            )

            await self._mount(
                Label(self.app.t("cloud_section_settings"), classes="section_label"),
                cloud_enabled_chk,
                cloud_snaps_chk,
                cloud_files_chk,
                Button(self.app.t("cloud_mode_toggle", mode=mode_label), id="cloud_mode_toggle", variant="primary", classes="action_btn"),
                Label(self.app.t("cloud_chats_label"), classes="section_label"),
                chat_lv,
                Label(self.app.t("cloud_section_actions"), classes="section_label"),
                Button(self.app.t("cloud_sync_up"), id="cloud_sync_up", variant="success", classes="action_btn"),
                Button(self.app.t("cloud_sync_down"), id="cloud_sync_down", variant="warning", classes="action_btn"),
                Label(self.app.t("cloud_section_status"), classes="section_label"),
                Static(status),
            )

            if chat_items:
                r = chat_lv.extend(chat_items)
                if inspect.isawaitable(r):
                    await r
            return

        if which == "backup":
            lv = ListView(id="backup_list")
            self._backup_map = {}
            items = []
            try:
                items = list(self.app.list_backups())
            except Exception:
                items = []
            bk_items = []
            for i, p in enumerate(items):
                name = p.name
                it = ListItem(Static(name), id=f"bk_{i}")
                self._backup_map[it.id] = p
                bk_items.append(it)

            await self._mount(
                Label(self.app.t("backup_section"), classes="section_label"),
                Button(self.app.t("backup_create"), id="backup_create", variant="success", classes="action_btn"),
                Button(self.app.t("backup_restore"), id="backup_restore", variant="warning", classes="action_btn"),
                Button(self.app.t("backup_delete"), id="backup_delete", variant="error", classes="action_btn"),
                Label(self.app.t("backup_list_label"), classes="section_label"),
                lv,
            )

            if bk_items:
                r = lv.extend(bk_items)
                if inspect.isawaitable(r):
                    await r
            return
        if which == "plugins":
            from ui.screens.plugins import PluginsTab
            plugins_tab = PluginsTab()
            await self._mount(plugins_tab)
            return

        if which == "bindings":
            kb_manager = get_keybindings_manager()
            if not kb_manager and hasattr(self.app, 'db'):
                from ui.keybindings import init_keybindings_manager
                kb_manager = init_keybindings_manager(self.app.db)

            widgets = []

            widgets.append(Button(self.app.t("bind_reset_all"), id="bind_reset_all", variant="warning", classes="action_btn"))

            for context in ["global", "find_mode", "select_mode", "composer", "navigation"]:
                context_i18n = CONTEXT_I18N_MAP.get(context, context)
                widgets.append(Label(self.app.t(context_i18n), classes="section_label"))

                actions = DEFAULT_BINDINGS.get(context, {})
                for action in actions.keys():
                    action_i18n = ACTION_I18N_MAP.get(action, action)
                    current_key = kb_manager.get(context, action) if kb_manager else actions[action]
                    description = self.app.t(action_i18n)

                    kb_input = KeyBindingInput(context, action, current_key, description)
                    widgets.append(kb_input)

            await self._mount(*widgets)
            return