from __future__ import annotations

from textual.app import ComposeResult
from textual import events
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.message import Message
from textual.screen import ModalScreen
from textual.widgets import Button, Input, Static
from rich.syntax import Syntax

from ui.widgets.patched_textarea import TextArea

class ConfirmDialog(ModalScreen[bool]):
    CSS = """
    ConfirmDialog { align: center middle; background: transparent !important; }
    #dlg_panel { width: 70; max-width: 90%; height: auto; max-height: 80%; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_body_scroll { height: auto; max-height: 18; }
    #dlg_body { height: auto; }
    #dlg_buttons { height: 1; margin-top: 1; }
    #dlg_buttons Button { width: auto; min-width: 10; margin-left: 1; }
    """

    def __init__(self, title: str, body: str, ok_key: str = "ok", cancel_key: str = "cancel"):
        super().__init__()
        self.title_text = title
        self.body_text = body
        self.ok_key = ok_key
        self.cancel_key = cancel_key

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.title_text, id="dlg_title")
            with VerticalScroll(id="dlg_body_scroll"):
                yield Static(self.body_text, id="dlg_body")
            with Horizontal(id="dlg_buttons"):
                yield Button(self.app.t(self.cancel_key), id="dlg_cancel")
                yield Button(self.app.t(self.ok_key), id="dlg_ok", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss(event.button.id == "dlg_ok")

class InfoDialog(ModalScreen[None]):

    CSS = """
    InfoDialog { align: center middle; background: transparent !important; }
    #info_panel { width: 80; max-width: 95%; height: auto; max-height: 85%; background: $panel; border: solid $secondary; padding: 1 2; }
    #info_title { text-style: bold; margin-bottom: 1; }
    #info_content_scroll { height: auto; max-height: 24; }
    #info_content { height: auto; }
    #info_close { width: 100%; margin-top: 1; }
    """

    def __init__(self, title: str, content: str):
        super().__init__()
        self.title_text = title
        self.content_text = content

    def compose(self) -> ComposeResult:
        with Vertical(id="info_panel"):
            yield Static(self.title_text, id="info_title")
            with VerticalScroll(id="info_content_scroll"):
                yield Static(self.content_text, id="info_content")
            yield Button(self.app.t("ok"), id="info_close", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss(None)

    def on_key(self, event: events.Key) -> None:
        if event.key in ("escape", "enter"):
            self.dismiss(None)
            event.stop()

class TextPromptDialog(ModalScreen[str]):
    CSS = """
    TextPromptDialog { align: center middle; background: transparent !important; }
    #dlg_panel { width: 70; max-width: 90%; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_prompt { margin-bottom: 1; color: $text-muted; }
    #dlg_input { height: 3; margin-bottom: 1; }
    #dlg_buttons { height: 1; }
    #dlg_buttons Button { width: auto; min-width: 10; margin-left: 1; }
    """

    def __init__(
        self,
        title: str,
        placeholder: str = "",
        initial: str = "",
        ok_key: str = "ok",
        cancel_key: str = "cancel",
        prompt: str = "",
        password: bool = False,
    ):
        super().__init__()
        self.title_text = title
        self.placeholder = placeholder
        self.initial = initial
        self.ok_key = ok_key
        self.cancel_key = cancel_key
        self.prompt_text = prompt
        self.password = password

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.title_text, id="dlg_title")
            if self.prompt_text:
                yield Static(self.prompt_text, id="dlg_prompt")
            self.input = Input(
                placeholder=self.placeholder,
                value=self.initial,
                id="dlg_input",
                password=self.password
            )
            yield self.input
            with Horizontal(id="dlg_buttons"):
                yield Button(self.app.t(self.cancel_key), id="dlg_cancel")
                yield Button(self.app.t(self.ok_key), id="dlg_ok", variant="primary")

    def on_mount(self) -> None:
        self.set_focus(self.input)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "dlg_ok":
            v = self.input.value.strip()
            self.dismiss(v if v else None)
        else:
            self.dismiss(None)

    def on_input_submitted(self, event: Input.Submitted) -> None:
        v = self.input.value.strip()
        self.dismiss(v if v else None)

class NewChatMenu(ModalScreen[str]):
    CSS = """
    NewChatMenu { align: center middle; background: transparent !important; }
    #dlg_panel { width: 56; max-width: 95%; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_body { margin-bottom: 1; color: $text-muted; }
    #menu_items { width: 100%; height: auto; }
    #menu_items Button { width: 100%; height: 3; content-align: center middle; padding: 0 1; margin-bottom: 1; }
    #menu_close { width: 100%; }
    """

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.app.t("menu_new_chat_title"), id="dlg_title")
            yield Static(self.app.t("menu_new_chat_body"), id="dlg_body")
            with Vertical(id="menu_items"):
                yield Button(self.app.t("menu_start_new"), id="menu_new", variant="primary")
                yield Button(self.app.t("menu_create_group"), id="menu_group")
                yield Button(self.app.t("menu_import_json"), id="menu_import")
            yield Button(self.app.t("close"), id="menu_close")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if bid == "menu_new":
            self.dismiss("new")
        elif bid == "menu_group":
            self.dismiss("group")
        elif bid == "menu_import":
            self.dismiss("import")
        else:
            self.dismiss(None)

class MessageEditorDialog(ModalScreen[str]):
    CSS = """
    MessageEditorDialog { align: center middle; background: transparent !important; }
    #dlg_panel { width: 90%; height: 70%; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_textarea { height: 1fr; background: $surface; border: solid $secondary; }
    #dlg_buttons { height: 1; margin-top: 1; }
    #dlg_buttons Button { width: auto; min-width: 10; margin-left: 1; }
    """

    def __init__(self, title: str, initial: str):
        super().__init__()
        self.title_text = title
        self.initial = initial

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.title_text, id="dlg_title")
            self.ta = TextArea(self.initial, id="dlg_textarea")
            self.ta.show_line_numbers = False
            yield self.ta
            with Horizontal(id="dlg_buttons"):
                yield Button(self.app.t("cancel"), id="dlg_cancel")
                yield Button(self.app.t("save"), id="dlg_ok", variant="primary")

    def on_mount(self) -> None:
        self.set_focus(self.ta)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "dlg_ok":
            v = self.ta.text.strip()
            self.dismiss(v if v else None)
        else:
            self.dismiss(None)

class ContextMenu(ModalScreen[str]):
    CSS = """
    /* IMPORTANT: ModalScreen defaults to `background: $background 60%` (scrim).
       This must be overridden for a context menu.
    */
    ContextMenu {
        align: left top;
        background: transparent !important;
        overflow: hidden;
    }
    ContextMenu:ansi {
        background: transparent !important;
    }

    ContextMenu #context_menu_container {
        position: absolute;
        width: auto;
        min-width: 16;
        max-width: 40;
        height: auto;
        background: $surface;
        border: solid $secondary;
        padding: 0;
        offset: -9999 -9999;
        overflow: hidden;
    }
    ContextMenu #menu_title {
        background: $surface-darken-1;
        color: $text;
        padding: 0 1;
        text-style: bold;
        width: 100%;
        height: 1;
        overflow: hidden;
    }
    ContextMenu #menu_title.hidden {
        display: none;
    }
    ContextMenu #menu_items {
        width: 100%;
        height: auto;
        overflow: hidden;
    }
    ContextMenu #menu_items Button {
        width: 100%;
        height: 1;
        border: none;
        background: $surface;
        content-align: left middle;
        padding: 0 1;
        overflow: hidden;
    }
    ContextMenu #menu_items Button:hover {
        background: $surface-lighten-1;
    }
    ContextMenu #menu_items Button:focus {
        background: $surface-lighten-2;
    }
    ContextMenu .menu-separator {
        width: 100%;
        height: 1;
        padding: 0 1;
        color: $text-muted;
    }
    """

    BINDINGS = [("escape", "dismiss", "Close")]

    def __init__(self, title: str, actions: list[tuple[str, str]], x: int = 0, y: int = 0):
        super().__init__()
        self.title_text = title
        self.actions = actions
        self._click_x = x
        self._click_y = y

    def compose(self) -> ComposeResult:
        with Vertical(id="context_menu_container"):
            title_widget = Static(self.title_text, id="menu_title")
            if not self.title_text:
                title_widget.add_class("hidden")
            yield title_widget
            with Vertical(id="menu_items"):
                for key, label in self.actions:
                    if key == "---":
                        yield Static("[dim]───────────────[/]", classes="menu-separator")
                    else:
                        yield Button(label, id=f"menu_{key}")

    def on_mount(self) -> None:

        self.set_timer(0.05, self._position_menu)

    @staticmethod
    def _compute_offset(
        *,
        screen_w: int,
        screen_h: int,
        click_x: int,
        click_y: int,
        menu_w: int,
        menu_h: int,
    ) -> tuple[int, int]:
        mx = int(click_x)
        my = int(click_y)

        gap = 1
        open_left = mx >= (int(screen_w) // 2)

        if open_left:
            x = mx - int(menu_w) - gap
        else:
            x = mx + gap

        if my + menu_h <= screen_h:
            y = my
        else:
            y = my - menu_h

        x = max(0, min(int(x), int(screen_w) - int(menu_w)))
        y = max(0, min(int(y), int(screen_h) - int(menu_h)))
        return x, y

    def _position_menu(self) -> None:
        try:
            container = self.query_one("#context_menu_container")
        except Exception:
            return

        screen_w = self.app.size.width
        screen_h = self.app.size.height

        max_label_len = max((len(label) for _, label in self.actions), default=10)
        title_len = len(self.title_text) if self.title_text else 0
        menu_width = max(max_label_len, title_len) + 4
        menu_height = len(self.actions) + (1 if self.title_text else 0) + 2

        x = self._click_x
        y = self._click_y

        if x + menu_width > screen_w:
            x = max(0, screen_w - menu_width)

        if y + menu_height > screen_h:
            y = max(0, screen_h - menu_height)

        container.styles.offset = (x, y)

    def on_mouse_down(self, event: events.MouseDown) -> None:

        try:
            container = self.query_one("#context_menu_container")
            rx = int(container.region.x)
            ry = int(container.region.y)
            rw = int(container.region.width)
            rh = int(container.region.height)
            inside = (rx <= event.screen_x < rx + rw) and (ry <= event.screen_y < ry + rh)
        except Exception:
            inside = False

        if not inside:

            if event.button == 1:
                self.dismiss(None)
            event.stop()
            return

        event.stop()

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

    def on_key(self, event: events.Key) -> None:
        if event.key == "escape":
            self.dismiss(None)
            event.stop()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id or ""
        if bid.startswith("menu_"):
            self.dismiss(bid.replace("menu_", "", 1))
        else:
            self.dismiss(None)

class RequestDialog(ModalScreen[str]):
    CSS = """
    RequestDialog { align: center middle; background: transparent !important; }
    #dlg_panel { width: 70; max-width: 90%; height: auto; max-height: 80%; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_body { height: auto; margin-bottom: 1; }
    #dlg_buttons { height: 1; }
    #dlg_buttons Button { width: auto; min-width: 10; margin-left: 1; }
    """

    def __init__(self, peer_alias: str, peer_fp: str):
        super().__init__()
        self.peer_alias = peer_alias
        self.peer_fp = peer_fp

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.app.t("handshake_request"), id="dlg_title")
            yield Static(self.app.t("handshake_request_body", alias=self.peer_alias, fp=self.peer_fp), id="dlg_body")
            with Horizontal(id="dlg_buttons"):
                yield Button(self.app.t("decline"), id="dlg_cancel")
                yield Button(self.app.t("accept"), id="dlg_ok", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss("accept" if event.button.id == "dlg_ok" else "decline")

class CodeViewDialog(ModalScreen[None]):
    CSS = """
    CodeViewDialog { align: center middle; background: transparent !important; }
    #dlg_panel { width: 90%; height: 80%; background: $panel; border: solid $secondary; padding: 1 2; }
    #code_view_scroll { height: 1fr; }
    #code_view_buttons { height: 1; margin-top: 1; }
    #code_view_buttons Button { width: auto; min-width: 10; margin-left: 1; }
    """

    def __init__(self, code: str, language: str = ""):
        super().__init__()
        self.code = code
        self.language = language

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            with VerticalScroll(id="code_view_scroll"):
                yield Static(
                    Syntax(
                        self.code,
                        self.language if self.language else "text",
                        line_numbers=True,
                    ),
                    id="code_view_content",
                )
            with Horizontal(id="code_view_buttons", classes="dialog-buttons"):
                yield Button(self.app.t("copy"), id="dlg_copy")
                yield Button(self.app.t("close"), id="dlg_close", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "dlg_copy":
            try:
                import pyperclip
                pyperclip.copy(self.code)
                self.app.notify(self.app.t("copied"))
            except ImportError:
                self.app.notify(self.app.t("pyperclip_not_installed"))
        else:
            self.dismiss()

    def on_key(self, event: events.Key) -> None:
        if event.key == "q":
            self.dismiss()

class StatusMenu(ModalScreen[str]):
    CSS = """
    StatusMenu { align: center middle; background: transparent !important; }
    #dlg_panel { width: 44; max-width: 90%; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #status_menu_list { padding: 0 0; }
    """

    def __init__(self, current_status: str):
        super().__init__()
        self.current_status = current_status

    def compose(self) -> ComposeResult:
        with Vertical(id="dlg_panel"):
            yield Static(self.app.t("set_status"), id="dlg_title")

            with Vertical(id="status_menu_list"):
                for s in ["online", "idle", "dnd", "offline"]:
                    label = self.app.t(f"status_{s}")
                    variant = "primary" if s == self.current_status else "default"
                    if s == self.current_status:
                        label = f"✓ {label}"

                    yield Button(label, id=f"status_set_{s}", classes=f"status_menu_btn {s}", variant=variant)

                yield Button(self.app.t("edit_profile"), id="status_edit_profile", variant="default", classes="status_menu_action")
                yield Button(self.app.t("cancel"), id="status_cancel", variant="error", classes="status_menu_action")

import os
import pathlib
from dataclasses import dataclass
from textual.widgets import ListView, ListItem, Label

@dataclass(frozen=True)
class _FMEntry:
    path: pathlib.Path
    is_dir: bool

class _FMItem(ListItem):
    def __init__(self, entry: _FMEntry):
        super().__init__()
        self.entry = entry

    def _icon_for(self, p: pathlib.Path, is_dir: bool) -> str:
        if is_dir:
            return "📁"
        ext = (p.suffix or "").lower().lstrip(".")
        if ext in {"png", "jpg", "jpeg", "gif", "webp", "bmp", "tiff", "tif"}:
            return "🖼"
        if ext in {"mp4", "mkv", "mov", "avi", "webm"}:
            return "🎞"
        if ext in {"mp3", "wav", "flac", "ogg", "m4a"}:
            return "🎵"
        if ext in {"zip", "7z", "rar", "tar", "gz", "xz"}:
            return "📦"
        if ext in {"txt", "md", "log", "json", "yaml", "yml", "toml", "ini"}:
            return "📄"
        return "📄"

    def compose(self) -> ComposeResult:
        icon = self._icon_for(self.entry.path, self.entry.is_dir)
        yield Label(f"{icon} {self.entry.path.name}")

class FileManagerDialog(ModalScreen[str]):

    CSS = """
    FileManagerDialog { align: center middle; background: transparent !important; }
    #fm_root { width: 90%; height: 90%; background: $panel; border: solid $secondary; padding: 1 2; }
    #fm_header { height: 3; }
    #fm_nav Button { width: 5; min-width: 5; height: 3; margin-right: 1; }
    #fm_path { width: 1fr; height: 3; }
    #fm_list { height: 1fr; border: solid $secondary; background: $surface; }
    #fm_footer { height: 3; }
    #fm_footer Button { width: 16; min-width: 16; margin-left: 1; }
    #fm_selected { width: 1fr; content-align: left middle; color: $text-muted; }
    """

    BINDINGS = [
        ("escape", "cancel", ""),
        ("enter", "open_or_select", ""),
        ("backspace", "up", ""),
        ("alt+left", "back", ""),
        ("alt+right", "forward", ""),
        ("f5", "refresh", ""),
    ]

    def __init__(self, start_path: str | os.PathLike[str] | None = None):
        super().__init__()
        self._history: list[pathlib.Path] = []
        self._hist_idx = -1
        self._selected: pathlib.Path | None = None
        self._cwd = pathlib.Path(start_path) if start_path else pathlib.Path.cwd()

    def compose(self) -> ComposeResult:
        with Vertical(id="fm_root"):
            yield Static(self.app.t("select_file") if hasattr(self.app, "t") else "Select File", id="dlg_title")
            with Horizontal(id="fm_header"):
                with Horizontal(id="fm_nav"):
                    yield Button("←", id="fm_back")
                    yield Button("→", id="fm_forward")
                    yield Button("↑", id="fm_up")
                    yield Button("⟳", id="fm_refresh")
                self.path_input = Input(id="fm_path", value=str(self._cwd))
                yield self.path_input

            self.list = ListView(id="fm_list")
            yield self.list

            with Horizontal(id="fm_footer"):
                yield Static("", id="fm_selected")
                yield Button(self.app.t("cancel") if hasattr(self.app, "t") else "Cancel", id="fm_cancel")
                yield Button(self.app.t("send") if hasattr(self.app, "t") else "Send", id="fm_send", variant="primary")

    def on_mount(self) -> None:
        self._push_history(self._cwd)
        self._refresh_list()
        self.set_focus(self.list)

    def _push_history(self, p: pathlib.Path) -> None:
        p = p.resolve()
        if self._hist_idx >= 0 and self._history and self._history[self._hist_idx] == p:
            return

        if self._hist_idx < len(self._history) - 1:
            self._history = self._history[: self._hist_idx + 1]
        self._history.append(p)
        self._hist_idx = len(self._history) - 1

    def _set_cwd(self, p: pathlib.Path, *, push: bool = True) -> None:
        try:
            p = p.expanduser().resolve()
        except Exception:
            p = pathlib.Path(str(p)).expanduser()
        if not p.exists() or not p.is_dir():
            return
        self._cwd = p
        self._selected = None
        self.path_input.value = str(self._cwd)
        if push:
            self._push_history(self._cwd)
        self._refresh_list()

    def _refresh_list(self) -> None:
        self.list.clear()
        entries: list[_FMEntry] = []
        try:
            for child in self._cwd.iterdir():

                is_dir = False
                try:
                    is_dir = child.is_dir()
                except Exception:
                    is_dir = False
                entries.append(_FMEntry(child, is_dir))
        except Exception:
            entries = []

        entries.sort(key=lambda e: (not e.is_dir, e.path.name.lower()))
        for e in entries:
            self.list.append(_FMItem(e))

        self.query_one("#fm_selected", Static).update("")

    def _update_selected_label(self) -> None:
        lbl = self.query_one("#fm_selected", Static)
        if self._selected:
            lbl.update(str(self._selected))
        else:
            lbl.update("")

    def action_cancel(self) -> None:
        self.dismiss(None)

    def action_refresh(self) -> None:
        self._refresh_list()

    def action_up(self) -> None:
        parent = self._cwd.parent
        if parent and parent != self._cwd:
            self._set_cwd(parent)

    def action_back(self) -> None:
        if self._hist_idx > 0:
            self._hist_idx -= 1
            self._set_cwd(self._history[self._hist_idx], push=False)

    def action_forward(self) -> None:
        if self._hist_idx < len(self._history) - 1:
            self._hist_idx += 1
            self._set_cwd(self._history[self._hist_idx], push=False)

    def action_open_or_select(self) -> None:
        item = self.list.highlighted_child
        if isinstance(item, _FMItem):
            self._open_entry(item.entry)

    def _open_entry(self, entry: _FMEntry) -> None:
        if entry.is_dir:
            self._set_cwd(entry.path)
            return
        self._selected = entry.path
        self._update_selected_label()
        self.dismiss(str(self._selected))

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id or ""
        if bid == "fm_cancel":
            self.dismiss(None)
            return
        if bid == "fm_send":
            if self._selected and self._selected.exists() and self._selected.is_file():
                self.dismiss(str(self._selected))
            return
        if bid == "fm_back":
            self.action_back(); return
        if bid == "fm_forward":
            self.action_forward(); return
        if bid == "fm_up":
            self.action_up(); return
        if bid == "fm_refresh":
            self.action_refresh(); return

    def on_list_view_selected(self, event: ListView.Selected) -> None:
        if isinstance(event.item, _FMItem):
            entry = event.item.entry

            if entry.is_dir:
                self._set_cwd(entry.path)
                return

            self._selected = entry.path
            self._update_selected_label()

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id != "fm_path":
            return
        raw = (event.value or "").strip()
        if not raw:
            return
        self._set_cwd(pathlib.Path(raw))

class SaveFileDialog(ModalScreen[str]):

    CSS = """
    SaveFileDialog { align: center middle; background: transparent !important; }
    #sf_root { width: 90%; height: 90%; background: $panel; border: solid $secondary; padding: 1 2; }
    #sf_header { height: 3; }
    #sf_nav Button { width: 5; min-width: 5; height: 3; margin-right: 1; }
    #sf_path { width: 1fr; height: 3; }
    #sf_list { height: 1fr; border: solid $secondary; background: $surface; }
    #sf_footer { height: 3; }
    #sf_name { width: 1fr; height: 3; }
    #sf_footer Button { width: 16; min-width: 16; margin-left: 1; }
    """

    BINDINGS = [
        ("escape", "cancel", ""),
        ("enter", "save", ""),
        ("backspace", "up", ""),
        ("alt+left", "back", ""),
        ("alt+right", "forward", ""),
        ("f5", "refresh", ""),
    ]

    def __init__(
        self,
        *,
        suggested_name: str,
        start_dir: str | os.PathLike[str] | None = None,
    ):
        super().__init__()
        self._history: list[pathlib.Path] = []
        self._hist_idx = -1
        self._cwd = pathlib.Path(start_dir) if start_dir else pathlib.Path.cwd()
        self.suggested_name = (suggested_name or "file").strip() or "file"

    def compose(self) -> ComposeResult:
        with Vertical(id="sf_root"):
            yield Static(self.app.t("download") if hasattr(self.app, "t") else "Download", id="dlg_title")
            with Horizontal(id="sf_header"):
                with Horizontal(id="sf_nav"):
                    yield Button("←", id="sf_back")
                    yield Button("→", id="sf_forward")
                    yield Button("↑", id="sf_up")
                    yield Button("⟳", id="sf_refresh")
                self.path_input = Input(id="sf_path", value=str(self._cwd))
                yield self.path_input

            self.list = ListView(id="sf_list")
            yield self.list

            with Horizontal(id="sf_footer"):
                self.name_input = Input(id="sf_name", value=self.suggested_name, placeholder="filename")
                yield self.name_input
                yield Button(self.app.t("cancel") if hasattr(self.app, "t") else "Cancel", id="sf_cancel")
                yield Button(self.app.t("save") if hasattr(self.app, "t") else "Save", id="sf_save", variant="primary")

    def on_mount(self) -> None:
        self._push_history(self._cwd)
        self._refresh_list()
        self.set_focus(self.list)

    def _push_history(self, p: pathlib.Path) -> None:
        p = p.resolve()
        if self._hist_idx >= 0 and self._history and self._history[self._hist_idx] == p:
            return
        if self._hist_idx < len(self._history) - 1:
            self._history = self._history[: self._hist_idx + 1]
        self._history.append(p)
        self._hist_idx = len(self._history) - 1

    def _set_cwd(self, p: pathlib.Path, *, push: bool = True) -> None:
        try:
            p = p.expanduser().resolve()
        except Exception:
            p = pathlib.Path(str(p)).expanduser()
        if not p.exists() or not p.is_dir():
            return
        self._cwd = p
        self.path_input.value = str(self._cwd)
        if push:
            self._push_history(self._cwd)
        self._refresh_list()

    def _refresh_list(self) -> None:
        self.list.clear()
        entries: list[_FMEntry] = []
        try:
            for child in self._cwd.iterdir():
                try:
                    is_dir = child.is_dir()
                except Exception:
                    is_dir = False
                entries.append(_FMEntry(child, is_dir))
        except Exception:
            entries = []

        entries.sort(key=lambda e: (not e.is_dir, e.path.name.lower()))
        for e in entries:
            self.list.append(_FMItem(e))

    def action_cancel(self) -> None:
        self.dismiss(None)

    def action_refresh(self) -> None:
        self._refresh_list()

    def action_up(self) -> None:
        parent = self._cwd.parent
        if parent and parent != self._cwd:
            self._set_cwd(parent)

    def action_back(self) -> None:
        if self._hist_idx > 0:
            self._hist_idx -= 1
            self._set_cwd(self._history[self._hist_idx], push=False)

    def action_forward(self) -> None:
        if self._hist_idx < len(self._history) - 1:
            self._hist_idx += 1
            self._set_cwd(self._history[self._hist_idx], push=False)

    def action_save(self) -> None:
        name = (self.name_input.value or "").strip()
        if not name:
            return
        out = self._cwd / name
        self.dismiss(str(out))

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id or ""
        if bid == "sf_cancel":
            self.dismiss(None)
            return
        if bid == "sf_save":
            self.action_save()
            return
        if bid == "sf_back":
            self.action_back(); return
        if bid == "sf_forward":
            self.action_forward(); return
        if bid == "sf_up":
            self.action_up(); return
        if bid == "sf_refresh":
            self.action_refresh(); return

    def on_list_view_selected(self, event: ListView.Selected) -> None:
        if isinstance(event.item, _FMItem):
            entry = event.item.entry
            if entry.is_dir:
                self._set_cwd(entry.path)
            else:

                try:
                    self.name_input.value = entry.path.name
                except Exception:
                    pass

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id == "sf_path":
            raw = (event.value or "").strip()
            if raw:
                self._set_cwd(pathlib.Path(raw))
        elif event.input.id == "sf_name":
            self.action_save()

class UserProfileDialog(ModalScreen):
    def __init__(self, user_data: dict, x: int = -1, y: int = -1):
        super().__init__()
        self.user_data = user_data
        self.x = x
        self.y = y

    def compose(self) -> ComposeResult:
        alias = self.user_data.get("alias", "Unknown")
        status = self.user_data.get("status", "offline")
        status_msg = self.user_data.get("status_msg", "")
        fp = self.user_data.get("fp", "")
        bio = self.user_data.get("bio", "")

        with Vertical(id="profile_dialog_panel"):

            with Horizontal(classes="profile_header"):
                yield Static("●", classes=f"status_indicator {status}")
                yield Static(alias, classes="profile_alias_large")

            if status_msg:
                yield Static(status_msg, classes="profile_status_msg")

            yield Static(f"ID: {fp}", classes="profile_fp_small")

            yield Static(self.app.t("bio_label"), classes="section_label")
            with VerticalScroll(classes="profile_bio_box"):
                yield Static(bio if bio else "No bio.", classes="profile_bio_text")

            with Horizontal(classes="profile_footer"):
                yield Button(self.app.t("close"), id="close_profile", variant="default")

    def on_mount(self) -> None:
        if self.x >= 0 and self.y >= 0:
            panel = self.query_one("#profile_dialog_panel")

            w = 60
            h = 25
            screen_w = self.app.size.width
            screen_h = self.app.size.height

            final_x = min(self.x, screen_w - w - 2)
            final_y = min(self.y, screen_h - h - 2)

            panel.styles.offset = (final_x, final_y)
            panel.styles.align = ("left", "top")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "close_profile":
            self.dismiss()

    def on_click(self, event: events.Click) -> None:

        if event.widget == self:
            self.dismiss()

class MyStatusDialog(ModalScreen):
    def __init__(self, current_status: str, status_msg: str, bio: str, x: int = -1, y: int = -1):
        super().__init__()
        self.current_status = current_status
        self.status_msg = status_msg
        self.bio = bio
        self.x = x
        self.y = y

    def compose(self) -> ComposeResult:
        with Vertical(id="my_status_panel"):
            yield Static(self.app.t("set_status"), classes="panel_title")

            with Vertical(classes="status_grid"):

                with Horizontal(classes="status_row"):
                    for s in ["online", "idle"]:
                        label = self.app.t(f"status_{s}")
                        variant = "primary" if s == self.current_status else "default"
                        if s == self.current_status:
                            label = f"✓ {label}"
                        yield Button(label, id=f"status_set_{s}", classes=f"status_menu_btn_grid {s}", variant=variant)

                with Horizontal(classes="status_row"):
                    for s in ["dnd", "offline"]:
                        label = self.app.t(f"status_{s}")
                        variant = "primary" if s == self.current_status else "default"
                        if s == self.current_status:
                            label = f"✓ {label}"
                        yield Button(label, id=f"status_set_{s}", classes=f"status_menu_btn_grid {s}", variant=variant)

            yield Static("Status Message", classes="section_label")
            yield Input(self.status_msg, placeholder="What's on your mind?", id="status_msg_input", max_length=100)

            yield Static(self.app.t("bio_label"), classes="section_label")
            with VerticalScroll(classes="profile_bio_box"):
                yield Static(self.bio if self.bio else "No bio.", classes="profile_bio_text")

            with Horizontal(classes="panel_actions"):
                yield Button(self.app.t("edit_profile"), id="status_edit_profile", variant="default")
                yield Button(self.app.t("close"), id="status_cancel", variant="error")

    def on_mount(self) -> None:
        if self.x >= 0:

            self.styles.align = ("left", "bottom")

            panel = self.query_one("#my_status_panel")

            panel.styles.margin = (0, 0, 3, self.x)

            panel.styles.offset = (0, 0)

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if bid == "status_cancel":
            self.dismiss(None)
        elif bid == "status_edit_profile":
            self.dismiss("edit_profile")
        elif bid.startswith("status_set_"):
            status = bid.replace("status_set_", "")
            msg = self.query_one("#status_msg_input", Input).value
            self.dismiss({"status": status, "status_msg": msg})

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id == "status_msg_input":
            msg = event.value
            self.dismiss({"status": self.current_status, "status_msg": msg})

class _MemberChipRemoved(Message):
    def __init__(self, alias: str):
        super().__init__()
        self.alias = alias

class _MemberChip(Static):

    def __init__(self, alias: str):
        super().__init__()
        self.alias = alias

    def compose(self) -> ComposeResult:
        yield Static(f"× {self.alias}", classes="member_chip_text")

    def on_click(self, event: events.Click) -> None:
        self.post_message(_MemberChipRemoved(self.alias))

class CreateGroupDialog(ModalScreen):

    CSS = """
    CreateGroupDialog {
        align: center middle;
        background: transparent !important;
    }
    #create_group_panel {
        width: 60;
        max-width: 90%;
        height: auto;
        max-height: 80%;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #create_group_panel .dlg_title {
        text-style: bold;
        margin-bottom: 1;
    }
    #create_group_panel .section_label {
        margin-top: 1;
        color: $text-muted;
    }
    #create_group_panel .name_input {
        margin-bottom: 1;
    }
    #create_group_panel .member_input_row {
        height: 3;
        margin-bottom: 1;
    }
    #create_group_panel .member_input_row Input {
        width: 1fr;
    }
    #create_group_panel .member_input_row Button {
        width: 8;
        margin-left: 1;
    }
    #create_group_panel .members_container {
        height: auto;
        max-height: 8;
        margin-bottom: 1;
    }
    #create_group_panel .members_list {
        layout: horizontal;
        height: auto;
        width: 100%;
    }
    #create_group_panel .member_chip {
        background: $accent;
        color: $text;
        padding: 0 1;
        margin: 0 1 0 0;
        height: 1;
    }
    #create_group_panel .member_chip:hover {
        background: $error;
    }
    #create_group_panel .member_chip_text {
        height: 1;
    }
    #create_group_panel .no_members {
        color: $text-muted;
        text-style: italic;
    }
    #create_group_panel .dlg_buttons {
        height: 3;
        margin-top: 1;
    }
    #create_group_panel .dlg_buttons Button {
        width: auto;
        min-width: 12;
        margin-left: 1;
    }
    """

    def __init__(self):
        super().__init__()
        self._members: list[str] = []

    def compose(self) -> ComposeResult:
        with Vertical(id="create_group_panel"):
            yield Static(self.app.t("dlg_create_group_title"), classes="dlg_title")

            yield Static(self.app.t("dlg_group_name"), classes="section_label")
            yield Input(placeholder=self.app.t("dlg_group_name_ph"), id="group_name_input", classes="name_input")

            yield Static(self.app.t("dlg_add_members"), classes="section_label")
            with Horizontal(classes="member_input_row"):
                yield Input(placeholder=self.app.t("dlg_group_member_add_ph"), id="member_alias_input")
                yield Button("+", id="btn_add_member", variant="primary")

            with VerticalScroll(classes="members_container"):
                yield Horizontal(id="members_list", classes="members_list")

            with Horizontal(classes="dlg_buttons"):
                yield Button(self.app.t("cancel"), id="btn_cancel")
                yield Button(self.app.t("dlg_create_group_btn"), id="btn_create", variant="primary")

    def on_mount(self) -> None:
        self.set_focus(self.query_one("#group_name_input", Input))
        self._update_members_display()

    def _update_members_display(self) -> None:
        container = self.query_one("#members_list", Horizontal)
        container.remove_children()

        if not self._members:
            container.mount(Static(self.app.t("dlg_no_members_yet"), classes="no_members"))
        else:
            for alias in self._members:
                chip = _MemberChip(alias)
                chip.add_class("member_chip")
                container.mount(chip)

    def _add_member(self) -> None:
        inp = self.query_one("#member_alias_input", Input)
        alias = inp.value.strip()
        if alias and alias not in self._members:
            self._members.append(alias)
            self._update_members_display()
        inp.value = ""
        self.set_focus(inp)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if bid == "btn_cancel":
            self.dismiss(None)
        elif bid == "btn_add_member":
            self._add_member()
        elif bid == "btn_create":
            name = self.query_one("#group_name_input", Input).value.strip()
            if not name:
                self.app.notify(self.app.t("dlg_group_name_required"))
                return
            self.dismiss({"name": name, "members": self._members})

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id == "member_alias_input":
            self._add_member()
        elif event.input.id == "group_name_input":

            self.set_focus(self.query_one("#member_alias_input", Input))

    def on__member_chip_removed(self, event: _MemberChipRemoved) -> None:
        if event.alias in self._members:
            self._members.remove(event.alias)
            self._update_members_display()

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

class PythonCodeEditorDialog(ModalScreen):

    CSS = """
    PythonCodeEditorDialog { align: center middle; background: rgba(0,0,0,0.85) !important; }
    #py_editor_panel {
        width: 95%;
        height: 95%;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #py_editor_title { text-style: bold; margin-bottom: 1; }
    #py_editor_main {
        height: 1fr;
    }
    #py_code_area {
        width: 2fr;
        height: 100%;
    }
    #py_code_area TextArea {
        height: 100%;
        border: solid $secondary;
    }
    #py_help_panel {
        width: 1fr;
        height: 100%;
        padding: 0 0 0 1;
    }
    #py_help_scroll {
        height: 100%;
        background: $surface;
        border: solid $secondary;
        padding: 1;
    }
    #py_editor_buttons {
        height: 3;
        min-height: 3;
        margin-top: 1;
        align: right middle;
        dock: bottom;
    }
    #py_editor_buttons Button {
        width: auto;
        min-width: 12;
        margin-left: 1;
    }
    .py_section_title {
        text-style: bold;
        color: $accent;
        margin-top: 1;
    }
    .py_var_line {
        margin-left: 1;
    }
    """

    def __init__(self, initial_code: str = ""):
        super().__init__()
        self._code = initial_code

    def compose(self) -> ComposeResult:
        from textual.widgets import TextArea
        from textual.containers import Horizontal, VerticalScroll

        with Vertical(id="py_editor_panel"):
            yield Label(self.app.t("py_editor_title"), id="py_editor_title")

            with Horizontal(id="py_editor_main"):

                with Vertical(id="py_code_area"):
                    yield TextArea(self._code, id="py_code_textarea", language="python", show_line_numbers=True)

                with Vertical(id="py_help_panel"):
                    with VerticalScroll(id="py_help_scroll"):
                        yield Static(f"[bold underline]{self.app.t('py_vars_title')}[/]\n", classes="py_section_title")

                        yield Static(f"[bold cyan]─── {self.app.t('py_user_info')} ───[/]", classes="py_section_title")
                        yield Static("[cyan]alias[/] → Username (str)", classes="py_var_line")
                        yield Static("[cyan]user_id[/] → Unique user ID (str)", classes="py_var_line")
                        yield Static("[cyan]fg_color[/] → User color (str)", classes="py_var_line")
                        yield Static("[cyan]status[/] → online/offline/idle/dnd", classes="py_var_line")
                        yield Static("[cyan]bio[/] → User bio (str)", classes="py_var_line")
                        yield Static("[cyan]created_at[/] → Account creation date", classes="py_var_line")
                        yield Static("[cyan]joined_at[/] → Chat join date", classes="py_var_line")
                        yield Static("[cyan]role[/] → User role (str)", classes="py_var_line")
                        yield Static("[cyan]is_self[/] → Is my message (bool)", classes="py_var_line")
                        yield Static("[cyan]perms[/] → Permission set (kick,ban,admin,mod)", classes="py_var_line")

                        yield Static(f"[bold cyan]─── {self.app.t('py_message_info')} ───[/]", classes="py_section_title")
                        yield Static("[cyan]message[/] → Message content (str)", classes="py_var_line")
                        yield Static("[cyan]msg_len[/] → Message length (int)", classes="py_var_line")
                        yield Static("[cyan]word_count[/] → Word count (int)", classes="py_var_line")
                        yield Static("[cyan]is_reply[/] → Is reply (bool)", classes="py_var_line")
                        yield Static("[cyan]is_edited[/] → Is edited (bool)", classes="py_var_line")
                        yield Static("[cyan]has_file[/] → Has file (bool)", classes="py_var_line")
                        yield Static("[cyan]filename[/] → Filename (str)", classes="py_var_line")
                        yield Static("[cyan]mime_type[/] → File type (str)", classes="py_var_line")
                        yield Static("[cyan]meta[/] → All metadata (dict)", classes="py_var_line")

                        yield Static(f"[bold cyan]─── {self.app.t('py_text_analysis')} ───[/]", classes="py_section_title")
                        yield Static("[cyan]has_url[/] → Has link (bool)", classes="py_var_line")
                        yield Static("[cyan]has_emoji[/] → Has emoji (bool)", classes="py_var_line")
                        yield Static("[cyan]has_mention[/] → Has @ mention (bool)", classes="py_var_line")
                        yield Static("[cyan]is_caps[/] → ALL CAPS (bool)", classes="py_var_line")
                        yield Static("[cyan]is_question[/] → Is question (?) (bool)", classes="py_var_line")

                        yield Static(f"[bold cyan]─── {self.app.t('py_datetime')} ───[/]", classes="py_section_title")
                        yield Static("[cyan]hour[/] → Hour (0-23)", classes="py_var_line")
                        yield Static("[cyan]minute[/] → Minute (0-59)", classes="py_var_line")
                        yield Static("[cyan]second[/] → Second (0-59)", classes="py_var_line")
                        yield Static("[cyan]day[/] → Day (1-31)", classes="py_var_line")
                        yield Static("[cyan]month[/] → Month (1-12)", classes="py_var_line")
                        yield Static("[cyan]year[/] → Year (2026)", classes="py_var_line")
                        yield Static("[cyan]weekday[/] → Monday, Tuesday...", classes="py_var_line")
                        yield Static("[cyan]date[/] → 2026-01-08 format", classes="py_var_line")
                        yield Static("[cyan]time[/] → 14:30:45 format", classes="py_var_line")

                        yield Static(f"[bold cyan]─── {self.app.t('py_functions')} ───[/]", classes="py_section_title")
                        yield Static("[cyan]re[/] → Regex module", classes="py_var_line")
                        yield Static("[cyan]len, int, str, bool[/]", classes="py_var_line")
                        yield Static("[cyan]abs, min, max, sum[/]", classes="py_var_line")
                        yield Static("[cyan]any, all[/]", classes="py_var_line")

                        yield Static(f"\n[bold yellow]─── {self.app.t('py_examples')} ───[/]", classes="py_section_title")
                        yield Static("[green]hour >= 9 and hour <= 17[/]", classes="py_var_line")
                        yield Static("[green]'admin' in perms[/]", classes="py_var_line")
                        yield Static("[green]msg_len > 100[/]", classes="py_var_line")
                        yield Static("[green]status == 'online'[/]", classes="py_var_line")
                        yield Static("[green]'hello' in message.lower()[/]", classes="py_var_line")
                        yield Static("[green]weekday in ['Saturday','Sunday'][/]", classes="py_var_line")
                        yield Static("[green]re.search(r'\\d+', message)[/]", classes="py_var_line")
                        yield Static(f"\n[dim]{self.app.t('py_result_hint')}[/]", classes="py_var_line")

            with Horizontal(id="py_editor_buttons"):
                yield Button(self.app.t("py_btn_cancel"), id="py_cancel")
                yield Button(self.app.t("py_btn_save"), id="py_save", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        from textual.widgets import TextArea

        if event.button.id == "py_cancel":
            self.dismiss(None)
        elif event.button.id == "py_save":
            code = self.query_one("#py_code_textarea", TextArea).text
            self.dismiss(code)

    def on_click(self, event) -> None:
        if event.widget == self:
            self.dismiss(None)

class EditColorRuleDialog(ModalScreen):

    CSS = """
    EditColorRuleDialog { align: center middle; background: transparent !important; }
    #dlg_panel {
        width: 80;
        max-width: 95%;
        height: auto;
        max-height: 95%;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #dlg_title { text-style: bold; margin-bottom: 1; }
    #dlg_scroll {
        height: auto;
        max-height: 40;
        min-height: 10;
        scrollbar-gutter: stable;
    }
    .rule_section { margin-bottom: 1; height: auto; }
    .rule_label { margin-bottom: 0; }
    .hidden_input {
        display: none;
    }
    #open_python_editor {
        width: 100%;
        margin-top: 1;
    }
    .python_preview {
        margin-top: 1;
        padding: 1;
        background: $surface;
        border: solid $secondary;
        height: auto;
        max-height: 5;
        overflow-y: auto;
    }
    #dlg_buttons {
        height: 3;
        min-height: 3;
        margin-top: 1;
        align: right middle;
        dock: bottom;
    }
    #dlg_buttons Button {
        width: auto;
        min-width: 12;
        margin-left: 1;
    }
    """

    def __init__(self, rule, index: int):
        super().__init__()
        from ui.widgets.appearance import ColorRule
        self._rule = rule if rule else ColorRule()
        self._index = index
        self._is_new = (index < 0)

    def compose(self) -> ComposeResult:
        from ui.widgets.appearance import ColorPicker, ColorRule

        type_opts = []
        for rule_id, rule_label in ColorRule.RULE_TYPES:
            translated = self.app.t(f"rule_type_{rule_id}")

            if translated == f"rule_type_{rule_id}":
                translated = rule_label
            type_opts.append((translated, rule_id))

        perm_opts = []
        for perm_id, perm_label in ColorRule.PERMISSIONS:
            translated = self.app.t(f"rule_perm_{perm_id}")
            if translated == f"rule_perm_{perm_id}":
                translated = perm_label
            perm_opts.append((translated, perm_id))

        from textual.widgets import Select, Checkbox, Label

        with Vertical(id="dlg_panel"):
            yield Static(self.app.t("dlg_edit_rule_title"), id="dlg_title")

            with VerticalScroll(id="dlg_scroll"):

                with Vertical(classes="rule_section"):
                    yield Label(self.app.t("dlg_rule_type"), classes="rule_label")
                    yield Select(type_opts, id="rule_type", value=self._rule.rule_type)

                with Vertical(classes="rule_section", id="pattern_section"):
                    yield Label(self.app.t("dlg_rule_pattern"), classes="rule_label", id="pattern_label")
                    yield Input(value=self._rule.pattern, id="rule_pattern", placeholder="")

                with Vertical(classes="rule_section", id="python_section"):
                    yield Label(self.app.t("py_code_label"), classes="rule_label")

                    yield Input(value=self._rule.pattern if self._rule.rule_type == "custom_python" else "", id="python_code_hidden", classes="hidden_input")

                    yield Button(self.app.t("py_open_editor"), id="open_python_editor", variant="primary")
                    yield Static("", id="python_preview", classes="python_preview")

                with Vertical(classes="rule_section", id="perm_section"):
                    yield Label(self.app.t("dlg_rule_pattern"), classes="rule_label")
                    yield Select(perm_opts, id="rule_perm", value=self._rule.pattern or "kick")

                with Vertical(classes="rule_section", id="color1_section"):
                    yield Label(self.app.t("dlg_rule_color"), classes="rule_label")

                    is_prev1 = (self._rule.color == ColorRule.PREVIOUS_COLOR)
                    yield Checkbox(self.app.t("dlg_use_previous_color"), value=is_prev1, id="use_prev_color1")
                    yield ColorPicker(value=self._rule.color if not is_prev1 else "#ffffff", id="rule_color")

                with Vertical(classes="rule_section"):
                    yield Checkbox(self.app.t("dlg_rule_split"), value=self._rule.split, id="rule_split")

                with Vertical(classes="rule_section", id="color2_section"):
                    yield Label(self.app.t("dlg_rule_color2"), classes="rule_label")
                    is_prev2 = (self._rule.color2 == ColorRule.PREVIOUS_COLOR)
                    yield Checkbox(self.app.t("dlg_use_previous_color"), value=is_prev2, id="use_prev_color2")
                    yield ColorPicker(value=self._rule.color2 if (self._rule.color2 and not is_prev2) else "#888888", id="rule_color2")

                with Vertical(classes="rule_section"):
                    yield Checkbox(self.app.t("dlg_rule_enabled"), value=self._rule.enabled, id="rule_enabled")

            with Horizontal(id="dlg_buttons"):
                yield Button(self.app.t("cancel"), id="dlg_cancel")

                save_label = self.app.t("add") if self._is_new else self.app.t("save")
                yield Button(save_label, id="dlg_save", variant="primary")

    def on_mount(self) -> None:
        self._update_visibility()

        if self._rule.rule_type == "custom_python" and self._rule.pattern:
            code = self._rule.pattern
            preview = self.query_one("#python_preview", Static)
            lines = code.strip().split('\n')
            preview_text = '\n'.join(lines[:3])
            if len(lines) > 3:
                preview_text += f"\n[dim]... +{len(lines)-3} satır daha[/]"
            preview.update(f"[green]{preview_text}[/]")

    def _update_visibility(self) -> None:
        try:
            from textual.widgets import Select, Checkbox

            rule_type = self.query_one("#rule_type", Select).value
            is_split = self.query_one("#rule_split", Checkbox).value

            pattern_section = self.query_one("#pattern_section", Vertical)
            perm_section = self.query_one("#perm_section", Vertical)

            no_pattern_rules = {
                "is_self", "is_reply", "is_edited",
                "has_file", "has_image",
                "chat_dm", "chat_group",
            }

            try:
                python_section = self.query_one("#python_section", Vertical)
            except Exception:
                python_section = None

            if rule_type in no_pattern_rules:
                pattern_section.styles.display = "none"
                perm_section.styles.display = "none"
                if python_section:
                    python_section.styles.display = "none"
            elif rule_type == "permission":
                pattern_section.styles.display = "none"
                perm_section.styles.display = "block"
                if python_section:
                    python_section.styles.display = "none"
            elif rule_type == "custom_python":

                pattern_section.styles.display = "none"
                perm_section.styles.display = "none"
                if python_section:
                    python_section.styles.display = "block"
            else:
                pattern_section.styles.display = "block"
                perm_section.styles.display = "none"
                if python_section:
                    python_section.styles.display = "none"

                try:
                    pattern_input = self.query_one("#rule_pattern", Input)
                    pattern_label = self.query_one("#pattern_label", Label)

                    placeholders = {
                        "user": ("dlg_rule_pattern_user", "Username"),
                        "regex_name": ("dlg_rule_pattern_regex_name", "Regex pattern"),
                        "regex_msg": ("dlg_rule_pattern_regex_msg", "Regex pattern"),
                        "user_contains": ("dlg_rule_pattern_user", "Text to find"),
                        "msg_contains": ("dlg_rule_pattern", "Text to find"),
                        "time_range": ("dlg_pattern_time_range", "HH:MM-HH:MM (e.g. 09:00-17:00)"),
                        "date_regex": ("dlg_pattern_date_regex", "Regex for datetime"),
                    }

                    if rule_type in placeholders:
                        key, fallback = placeholders[rule_type]
                        text = self.app.t(key)
                        if text == key:
                            text = fallback
                        pattern_input.placeholder = text
                        pattern_label.update(text)
                    else:
                        pattern_input.placeholder = ""
                        pattern_label.update(self.app.t("dlg_rule_pattern"))
                except Exception:
                    pass

            color2_section = self.query_one("#color2_section", Vertical)
            if is_split:
                color2_section.styles.display = "block"
            else:
                color2_section.styles.display = "none"

            try:
                from ui.widgets.appearance import ColorPicker as CP
                use_prev1 = self.query_one("#use_prev_color1", Checkbox).value
                color_picker1 = self.query_one("#rule_color", CP)
                color_picker1.styles.display = "none" if use_prev1 else "block"

                if is_split:
                    use_prev2 = self.query_one("#use_prev_color2", Checkbox).value
                    color_picker2 = self.query_one("#rule_color2", CP)
                    color_picker2.styles.display = "none" if use_prev2 else "block"
            except Exception:
                pass
        except Exception:
            pass

    def on_select_changed(self, event) -> None:
        if event.select.id == "rule_type":
            self._update_visibility()

    def on_checkbox_changed(self, event) -> None:
        if event.checkbox.id in ("rule_split", "use_prev_color1", "use_prev_color2"):
            self._update_visibility()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        from ui.widgets.appearance import ColorRule, ColorPicker
        from textual.widgets import Select, Checkbox

        if event.button.id == "dlg_cancel":
            self.dismiss(None)
        elif event.button.id == "open_python_editor":

            current_code = self.query_one("#python_code_hidden", Input).value
            self.app.push_screen(PythonCodeEditorDialog(current_code), self._on_python_editor_closed)
        elif event.button.id == "dlg_save":
            try:
                rule_type = self.query_one("#rule_type", Select).value

                no_pattern_rules = {"is_self", "is_reply", "is_edited", "has_file", "has_image", "chat_dm", "chat_group"}
                if rule_type in no_pattern_rules:
                    pattern = ""
                elif rule_type == "permission":
                    pattern = self.query_one("#rule_perm", Select).value
                elif rule_type == "custom_python":

                    pattern = self.query_one("#python_code_hidden", Input).value.strip()
                else:
                    pattern = self.query_one("#rule_pattern", Input).value.strip()

                use_prev1 = self.query_one("#use_prev_color1", Checkbox).value
                if use_prev1:
                    color = ColorRule.PREVIOUS_COLOR
                else:
                    color = self.query_one("#rule_color", ColorPicker).color

                is_split = self.query_one("#rule_split", Checkbox).value
                color2 = None
                if is_split:
                    use_prev2 = self.query_one("#use_prev_color2", Checkbox).value
                    if use_prev2:
                        color2 = ColorRule.PREVIOUS_COLOR
                    else:
                        color2 = self.query_one("#rule_color2", ColorPicker).color

                enabled = self.query_one("#rule_enabled", Checkbox).value

                updated_rule = ColorRule(
                    rule_type=rule_type,
                    pattern=pattern,
                    color=color,
                    color2=color2,
                    split=is_split,
                    enabled=enabled,
                    priority=self._rule.priority,
                )

                self.dismiss((self._index, updated_rule))
            except Exception as e:
                self.app.notify(self.app.t("generic_error", e=str(e)))
                self.dismiss(None)

    def _on_python_editor_closed(self, code: str | None) -> None:
        if code is not None:

            self.query_one("#python_code_hidden", Input).value = code

            preview = self.query_one("#python_preview", Static)
            if code.strip():

                lines = code.strip().split('\n')
                preview_text = '\n'.join(lines[:3])
                if len(lines) > 3:
                    preview_text += f"\n[dim]{self.app.t('code_preview_more_lines', n=len(lines)-3)}[/]"
                preview.update(f"[green]{preview_text}[/]")
            else:
                preview.update(f"[dim]{self.app.t('code_preview_no_code')}[/]")

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

class PluginSettingsDialog(ModalScreen):

    CSS = """
    PluginSettingsDialog { align: center middle; background: rgba(0,0,0,0.7) !important; }
    #plugin_dlg_panel {
        width: 70;
        max-width: 90%;
        height: auto;
        max-height: 85%;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #plugin_dlg_title { text-style: bold; margin-bottom: 1; color: $accent; }
    #plugin_dlg_scroll {
        height: auto;
        max-height: 25;
        min-height: 5;
        scrollbar-gutter: stable;
    }
    .plugin-setting-row {
        height: auto;
        min-height: 4;
        margin-bottom: 1;
        padding: 1;
        background: $surface;
        border: solid $surface-lighten-1;
    }
    .plugin-setting-label {
        height: 1;
        text-style: bold;
        color: $text;
    }
    .plugin-setting-type-hint {
        height: 1;
        color: $text-disabled;
        text-style: italic;
    }
    .plugin-setting-desc {
        height: auto;
        color: $text-muted;
        margin-bottom: 1;
    }
    .plugin-setting-input {
        height: auto;
        min-height: 3;
        align: left middle;
    }
    .plugin-setting-input Checkbox {
        height: 3;
        padding: 0 1;
        background: $surface;
        border: solid $secondary;
    }
    .plugin-setting-input Checkbox:focus {
        border: solid $accent;
    }
    .plugin-setting-input Checkbox.-on {
        background: $success-darken-2;
        border: solid $success;
    }
    .plugin-setting-input Input {
        width: 100%;
    }
    .plugin-setting-input Select {
        width: 100%;
    }
    #plugin_dlg_buttons {
        height: 3;
        margin-top: 1;
        align: right middle;
    }
    #plugin_dlg_buttons Button {
        width: auto;
        min-width: 12;
        margin-left: 1;
    }
    """

    def _get_type_hint(self, setting) -> str:
        from plugins import PluginSettingType

        if setting.type == PluginSettingType.BOOLEAN:
            return "Toggle on/off"
        elif setting.type == PluginSettingType.STRING:
            hint = "Text"
            if setting.placeholder:
                hint += f" • e.g. {setting.placeholder}"
            return hint
        elif setting.type == PluginSettingType.INTEGER:
            hint = "Number"
            parts = []
            if setting.min_value is not None:
                parts.append(f"min: {setting.min_value}")
            if setting.max_value is not None:
                parts.append(f"max: {setting.max_value}")
            if parts:
                hint += f" • {', '.join(parts)}"
            elif setting.placeholder:
                hint += f" • e.g. {setting.placeholder}"
            return hint
        elif setting.type == PluginSettingType.SELECT:
            return f"Select • {len(setting.options)} options"
        elif setting.type == PluginSettingType.TEXT:
            return "Multi-line text"
        elif setting.type == PluginSettingType.COLOR:
            return "Color • #RRGGBB format"
        elif setting.type == PluginSettingType.PATH:
            return "File/folder path"
        elif setting.type == PluginSettingType.KEYBIND:
            return "Key combination • e.g. ctrl+shift+p"
        elif setting.type == PluginSettingType.JSON:
            return "JSON data"
        elif setting.type == PluginSettingType.LIST:
            return "List • comma separated"
        else:
            return "Value"

    def __init__(self, plugin_id: str, meta, settings: list, current_values: dict):
        super().__init__()
        self._plugin_id = plugin_id
        self._meta = meta
        self._settings = settings
        self._current_values = current_values.copy()

    def compose(self) -> ComposeResult:
        from textual.widgets import Select, Checkbox, Label
        from plugins import PluginSettingType

        with Vertical(id="plugin_dlg_panel"):
            yield Static(f"[bold]⚙ {self._meta.name} Settings[/]", id="plugin_dlg_title")

            with VerticalScroll(id="plugin_dlg_scroll"):
                for setting in self._settings:
                    with Vertical(classes="plugin-setting-row"):
                        yield Label(setting.label, classes="plugin-setting-label")

                        type_hint = self._get_type_hint(setting)
                        yield Static(f"[dim italic]{type_hint}[/]", classes="plugin-setting-type-hint")
                        if setting.description:
                            yield Static(f"[dim]{setting.description}[/]", classes="plugin-setting-desc")

                        value = self._current_values.get(setting.key, setting.default)

                        with Horizontal(classes="plugin-setting-input"):
                            if setting.type == PluginSettingType.BOOLEAN:
                                yield Checkbox("", value=bool(value), id=f"setting_{setting.key}")

                            elif setting.type == PluginSettingType.STRING:
                                yield Input(
                                    value=str(value) if value else "",
                                    placeholder=setting.placeholder,
                                    id=f"setting_{setting.key}"
                                )

                            elif setting.type == PluginSettingType.INTEGER:
                                yield Input(
                                    value=str(value) if value is not None else "",
                                    placeholder=setting.placeholder or "0",
                                    id=f"setting_{setting.key}"
                                )

                            elif setting.type == PluginSettingType.SELECT:

                                options = [(label, val) for val, label in setting.options]
                                yield Select(options, value=value, id=f"setting_{setting.key}")

                            elif setting.type == PluginSettingType.TEXT:
                                yield TextArea(
                                    value if value else "",
                                    id=f"setting_{setting.key}"
                                )

                            elif setting.type == PluginSettingType.COLOR:
                                yield Input(
                                    value=str(value) if value else "#ffffff",
                                    placeholder="#ffffff",
                                    id=f"setting_{setting.key}"
                                )

                            else:
                                yield Input(
                                    value=str(value) if value else "",
                                    id=f"setting_{setting.key}"
                                )

            with Horizontal(id="plugin_dlg_buttons"):
                yield Button("Cancel", id="plugin_cancel")
                yield Button("Save", id="plugin_save", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        from textual.widgets import Select, Checkbox
        from plugins import PluginSettingType

        if event.button.id == "plugin_cancel":
            self.dismiss(None)
        elif event.button.id == "plugin_save":

            new_values = {}

            for setting in self._settings:
                widget_id = f"setting_{setting.key}"
                try:
                    if setting.type == PluginSettingType.BOOLEAN:
                        widget = self.query_one(f"#{widget_id}", Checkbox)
                        new_values[setting.key] = widget.value

                    elif setting.type == PluginSettingType.INTEGER:
                        widget = self.query_one(f"#{widget_id}", Input)
                        try:
                            new_values[setting.key] = int(widget.value) if widget.value else setting.default
                        except ValueError:
                            new_values[setting.key] = setting.default

                    elif setting.type == PluginSettingType.SELECT:
                        widget = self.query_one(f"#{widget_id}", Select)
                        new_values[setting.key] = widget.value

                    elif setting.type == PluginSettingType.TEXT:
                        widget = self.query_one(f"#{widget_id}", TextArea)
                        new_values[setting.key] = widget.text

                    else:
                        widget = self.query_one(f"#{widget_id}", Input)
                        new_values[setting.key] = widget.value

                except Exception as e:
                    new_values[setting.key] = self._current_values.get(setting.key, setting.default)

            self.dismiss((self._plugin_id, new_values))

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

class PluginDialog(ModalScreen):

    CSS = """
    PluginDialog { align: center middle; background: rgba(0,0,0,0.7) !important; }
    #plugin_dialog_panel {
        width: 60;
        max-width: 85%;
        height: auto;
        max-height: 80%;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #plugin_dialog_title {
        text-style: bold;
        margin-bottom: 1;
        color: $accent;
    }
    #plugin_dialog_content {
        height: auto;
        max-height: 20;
        margin-bottom: 1;
    }
    #plugin_dialog_buttons {
        height: 3;
        align: right middle;
    }
    #plugin_dialog_buttons Button {
        width: auto;
        min-width: 10;
        margin-left: 1;
    }
    """

    def __init__(
        self,
        title: str,
        content,
        buttons: list,
        plugin_id: str = ""
    ):
        super().__init__()
        self._title = title
        self._content = content
        self._buttons = buttons
        self._plugin_id = plugin_id

    def compose(self) -> ComposeResult:
        with Vertical(id="plugin_dialog_panel"):
            yield Static(f"[bold]{self._title}[/]", id="plugin_dialog_title")

            if isinstance(self._content, str):
                yield Static(self._content, id="plugin_dialog_content")
            else:
                with Vertical(id="plugin_dialog_content"):
                    yield self._content

            with Horizontal(id="plugin_dialog_buttons"):
                for i, (label, value) in enumerate(self._buttons):
                    variant = "primary" if i == len(self._buttons) - 1 else "default"
                    yield Button(label, id=f"plugin_btn_{value}", variant=variant)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        btn_id = event.button.id or ""
        if btn_id.startswith("plugin_btn_"):
            value = btn_id.replace("plugin_btn_", "")
            self.dismiss(value)

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)

class PluginInputDialog(ModalScreen):

    CSS = """
    PluginInputDialog { align: center middle; background: rgba(0,0,0,0.7) !important; }
    #plugin_input_panel {
        width: 60;
        max-width: 85%;
        height: auto;
        background: $panel;
        border: solid $secondary;
        padding: 1 2;
    }
    #plugin_input_title {
        text-style: bold;
        margin-bottom: 1;
        color: $accent;
    }
    #plugin_input_prompt {
        margin-bottom: 1;
    }
    #plugin_input_field {
        margin-bottom: 1;
    }
    #plugin_input_buttons {
        height: 3;
        align: right middle;
    }
    #plugin_input_buttons Button {
        width: auto;
        min-width: 10;
        margin-left: 1;
    }
    """

    def __init__(
        self,
        title: str,
        prompt: str,
        default: str = "",
        plugin_id: str = ""
    ):
        super().__init__()
        self._title = title
        self._prompt = prompt
        self._default = default
        self._plugin_id = plugin_id

    def compose(self) -> ComposeResult:
        with Vertical(id="plugin_input_panel"):
            yield Static(f"[bold]{self._title}[/]", id="plugin_input_title")
            yield Static(self._prompt, id="plugin_input_prompt")
            yield Input(value=self._default, id="plugin_input_field")

            with Horizontal(id="plugin_input_buttons"):
                yield Button("Cancel", id="plugin_input_cancel")
                yield Button("OK", id="plugin_input_ok", variant="primary")

    def on_mount(self) -> None:
        self.query_one("#plugin_input_field", Input).focus()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "plugin_input_cancel":
            self.dismiss(None)
        elif event.button.id == "plugin_input_ok":
            value = self.query_one("#plugin_input_field", Input).value
            self.dismiss(value)

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id == "plugin_input_field":
            self.dismiss(event.value)

    def on_click(self, event: events.Click) -> None:
        if event.widget == self:
            self.dismiss(None)
