from __future__ import annotations

from textual import events
from textual.message import Message
from textual.widgets import Button, Static, ListView, ListItem
from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical, VerticalScroll

class CallParticipantItem(Static):

    class ContextRequested(Message):
        def __init__(self, sender: "CallParticipantItem", alias: str, x: int, y: int):
            super().__init__()
            self.sender = sender
            self.alias = alias
            self.x = x
            self.y = y

    def __init__(self, alias: str, is_speaking: bool = False, is_muted: bool = False):
        super().__init__(alias, classes="call-participant")
        self.alias = alias
        self._is_speaking = is_speaking
        self._is_muted = is_muted
        self._update_display()

    def _update_display(self) -> None:
        icon = "🔇" if self._is_muted else ("🎤" if self._is_speaking else "👤")
        self.update(f"{icon} {self.alias}")

    def set_speaking(self, speaking: bool) -> None:
        self._is_speaking = speaking
        self._update_display()

    def set_muted(self, muted: bool) -> None:
        self._is_muted = muted
        self._update_display()

    def set_label(self, text: str) -> None:
        self.update(text)

    def on_mouse_down(self, event: events.MouseDown) -> None:
        if event.button == 3:
            self.post_message(self.ContextRequested(self, self.alias, event.screen_x, event.screen_y))
            event.stop()

class GroupMemberItem(Static):

    class ContextRequested(Message):
        def __init__(self, alias: str, x: int, y: int):
            super().__init__()
            self.alias = alias
            self.x = x
            self.y = y

    def __init__(self, alias: str, online: bool = False, in_call: bool = False):
        super().__init__("", classes="gmp-member")
        self.alias = alias
        self._online = online
        self._in_call = in_call
        self._update_display()

    def _update_display(self) -> None:
        if self._in_call:
            icon = "🎤"
            self.add_class("in-call")
            self.remove_class("online")
        elif self._online:
            icon = "🟢"
            self.add_class("online")
            self.remove_class("in-call")
        else:
            icon = "⚫"
            self.remove_class("online")
            self.remove_class("in-call")
        self.update(f"{icon} {self.alias}")

    def on_mouse_down(self, event: events.MouseDown) -> None:
        if event.button == 3:
            self.post_message(self.ContextRequested(self.alias, event.screen_x, event.screen_y))
            event.stop()

class GroupMembersPanel(Vertical):

    DEFAULT_CSS = """
    GroupMembersPanel {
        width: 0;
        min-width: 0;
        max-width: 0;
        height: 100%;
        dock: right;
        background: $surface;
        border-left: solid $surface-darken-1;
        display: none;
        overflow: hidden;
    }
    GroupMembersPanel.visible {
        width: 18;
        min-width: 18;
        max-width: 18;
        display: block;
    }
    GroupMembersPanel #gmp_header {
        height: 1;
        width: 100%;
        padding: 0 1;
        background: $surface-darken-1;
    }
    GroupMembersPanel #gmp_title {
        width: 1fr;
        height: 1;
        text-align: left;
        color: $text-muted;
    }
    GroupMembersPanel #gmp_close {
        width: 1;
        height: 1;
        min-width: 1;
        padding: 0;
        border: none;
        background: transparent;
        color: $text-muted;
    }
    GroupMembersPanel #gmp_close:hover {
        color: $error;
    }
    GroupMembersPanel #gmp_list {
        height: 1fr;
        width: 100%;
        padding: 0;
        overflow-y: auto;
    }
    GroupMembersPanel .gmp-member {
        height: 1;
        width: 100%;
        padding: 0 1;
        background: transparent;
    }
    GroupMembersPanel .gmp-member:hover {
        background: $surface-lighten-1;
    }
    GroupMembersPanel .gmp-member.online {
        color: $success;
    }
    GroupMembersPanel .gmp-member.in-call {
        color: $warning;
    }
    """

    class CloseRequested(Message):
        def __init__(self, sender: "GroupMembersPanel"):
            super().__init__()
            self.sender = sender

    class CallRequested(Message):
        def __init__(self, sender: "GroupMembersPanel", group_id: str):
            super().__init__()
            self.sender = sender
            self.group_id = group_id

    class MemberContextRequested(Message):
        def __init__(self, alias: str, group_id: str, x: int, y: int):
            super().__init__()
            self.alias = alias
            self.group_id = group_id
            self.x = x
            self.y = y

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._group_id: str = ""
        self._group_name: str = "Group"
        self._members: list[dict] = []
        self._active_call_participants: int = 0

    def compose(self) -> ComposeResult:
        with Horizontal(id="gmp_header"):
            yield Static("Members", id="gmp_title")
            yield Button("×", id="gmp_close")
        with VerticalScroll(id="gmp_list"):
            pass

    def show(self, group_id: str, group_name: str = "Group") -> None:
        self._group_id = group_id
        self._group_name = group_name
        self.add_class("visible")
        self._update_title()

    def hide(self) -> None:
        self.remove_class("visible")

    def toggle(self, group_id: str, group_name: str = "Group") -> None:
        if self.has_class("visible") and self._group_id == group_id:
            self.hide()
        else:
            self.show(group_id, group_name)

    def _update_title(self) -> None:
        try:
            title = self.query_one("#gmp_title", Static)
            online_count = sum(1 for m in self._members if m.get("online"))
            title.update(f"{online_count}/{len(self._members)}")
        except Exception:
            pass

    def update_members(self, members: list[dict]) -> None:
        self._members = list(members)
        self._update_title()
        self._rebuild_list()

    def set_active_call(self, participant_count: int) -> None:
        self._active_call_participants = participant_count

    def _rebuild_list(self) -> None:
        try:
            container = self.query_one("#gmp_list", VerticalScroll)
            container.remove_children()

            sorted_members = sorted(
                self._members,
                key=lambda m: (not m.get("in_call", False), not m.get("online", False), m.get("alias", "").lower())
            )
            for member in sorted_members:
                alias = member.get("alias", "")
                online = member.get("online", False)
                in_call = member.get("in_call", False)

                item = GroupMemberItem(alias, online=online, in_call=in_call)
                container.mount(item)
        except Exception:
            pass

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "gmp_close":
            self.post_message(self.CloseRequested(self))
            event.stop()

    def on_group_member_item_context_requested(self, event: GroupMemberItem.ContextRequested) -> None:
        self.post_message(self.MemberContextRequested(event.alias, self._group_id, event.x, event.y))
        event.stop()

    def on_mouse_down(self, event: events.MouseDown) -> None:
        if event.button == 3:

            event.stop()

class CallVolumeBar(Static):

    class Changed(Message):
        def __init__(self, sender: "CallVolumeBar", gain: float):
            super().__init__()
            self.sender = sender
            self.gain = float(gain)

    def __init__(self, gain: float = 1.0, **kwargs):
        super().__init__("", **kwargs)
        self._gain = max(0.0, min(3.0, float(gain)))
        self._dragging = False
        self._update_bar()

    @property
    def gain(self) -> float:
        return float(self._gain)

    def set_gain(self, gain: float) -> None:
        self._gain = max(0.0, min(3.0, float(gain)))
        self._update_bar()

    def _update_bar(self) -> None:
        try:
            w = max(10, int(self.size.width) - 5)
        except:
            w = 20

        pct = self._gain / 3.0
        fill = int(pct * w)
        bar = "█" * fill + "─" * (w - fill)
        self.update(f"{int(pct * 100):>3}% {bar}")

    def _gain_from_x(self, local_x: int) -> float:
        try:
            prefix = 5
            bar_x = local_x - prefix
            bar_w = max(1, self.size.width - prefix)
            ratio = max(0.0, min(1.0, bar_x / bar_w))
            return ratio * 3.0
        except:
            return self._gain

    def on_mouse_down(self, event: events.MouseDown) -> None:
        if event.button == 1:
            self._dragging = True
            self.capture_mouse()
            self._gain = self._gain_from_x(event.x)
            self._update_bar()
            self.post_message(self.Changed(self, self._gain))
            event.stop()

    def on_mouse_move(self, event: events.MouseMove) -> None:
        if self._dragging:
            new_gain = self._gain_from_x(event.x)
            if abs(new_gain - self._gain) > 0.01:
                self._gain = new_gain
                self._update_bar()
                self.post_message(self.Changed(self, self._gain))

    def on_mouse_up(self, event: events.MouseUp) -> None:
        if self._dragging:
            self._dragging = False
            self.release_mouse()

class CallUserPopover(Vertical):

    class Closed(Message):
        def __init__(self, sender: "CallUserPopover"):
            super().__init__()
            self.sender = sender

    class RemoteMuteToggled(Message):
        def __init__(self, sender: "CallUserPopover", alias: str):
            super().__init__()
            self.sender = sender
            self.alias = alias

    class SelfMuteToggled(Message):
        def __init__(self, sender: "CallUserPopover"):
            super().__init__()
            self.sender = sender

    class SelfDeafenToggled(Message):
        def __init__(self, sender: "CallUserPopover"):
            super().__init__()
            self.sender = sender

    class RemoteGainChanged(Message):
        def __init__(self, sender: "CallUserPopover", alias: str, gain: float):
            super().__init__()
            self.sender = sender
            self.alias = alias
            self.gain = float(gain)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._alias = ""
        self._is_self = False
        self._remote_muted = False
        self._self_muted = False
        self._self_deafened = False
        self._gain = 1.0

    def compose(self) -> ComposeResult:

        with Horizontal(id="pop_header"):
            yield Static("", id="pop_title")
            yield Button("X", id="pop_close")

        with Vertical(id="pop_body"):

            yield Button("MUTE USER", id="pop_remote_mute")
            with Horizontal(id="pop_vol_row"):
                yield Static("VOL:", id="pop_vol_label")
                yield CallVolumeBar(id="pop_vol")

            yield Button("MUTE", id="pop_self_mute")
            yield Button("DEAFEN", id="pop_self_deafen")

    def open(self, *, alias: str, x: int, y: int, is_self: bool,
             remote_muted: bool, self_muted: bool, self_deafened: bool, gain: float) -> None:
        self._alias = alias
        self._is_self = is_self
        self._remote_muted = remote_muted
        self._self_muted = self_muted
        self._self_deafened = self_deafened
        self._gain = gain

        W, H = 50, 12
        sw, sh = self.app.size.width, self.app.size.height

        parent_x, parent_y = 0, 0
        try:
            parent = self.parent
            if parent is not None and hasattr(parent, "region"):
                parent_x = parent.region.x
                parent_y = parent.region.y
        except Exception:
            pass

        px = x - parent_x
        py = y - parent_y

        px = max(0, min(px, sw - W - 2))
        py = max(0, min(py, sh - H - 2))

        self.styles.display = "block"
        self.styles.visibility = "visible"
        self.styles.width = W
        self.styles.height = H
        self.styles.offset = (px, py)
        self._update_ui()
        try:
            self.refresh(layout=True, repaint=True)
        except Exception:
            pass

    def close(self) -> None:
        self.styles.display = "none"
        self.styles.visibility = "hidden"
        self.styles.width = 0
        self.styles.height = 0
        self.styles.offset = (-9999, -9999)
        try:
            self.refresh(layout=True, repaint=True)
        except Exception:
            pass

    def _update_ui(self) -> None:
        try:
            self.query_one("#pop_title", Static).update(f" {self._alias}")
        except:
            pass

        try:
            self.query_one("#pop_remote_mute").styles.display = "none" if self._is_self else "block"
            self.query_one("#pop_vol_row").styles.display = "none" if self._is_self else "block"
            self.query_one("#pop_self_mute").styles.display = "block" if self._is_self else "none"
            self.query_one("#pop_self_deafen").styles.display = "block" if self._is_self else "none"
        except:
            pass

        try:
            if self._is_self:
                m = self.query_one("#pop_self_mute", Button)
                d = self.query_one("#pop_self_deafen", Button)
                m.label = "🔇 MUTE: ON" if self._self_muted else "🎤 MUTE: OFF"
                d.label = "🔇 DEAFEN: ON" if self._self_deafened else "🔊 DEAFEN: OFF"
                m.set_class(self._self_muted, "active")
                d.set_class(self._self_deafened, "active")
            else:
                r = self.query_one("#pop_remote_mute", Button)
                r.label = "🔇 MUTED" if self._remote_muted else "🔊 UNMUTED"
                r.set_class(self._remote_muted, "active")
                self.query_one("#pop_vol", CallVolumeBar).set_gain(self._gain)
        except:
            pass

    def on_call_volume_bar_changed(self, event: CallVolumeBar.Changed) -> None:
        if not self._is_self:
            self._gain = event.gain
            self.post_message(self.RemoteGainChanged(self, self._alias, event.gain))

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if bid == "pop_close":
            self.post_message(self.Closed(self))
        elif bid == "pop_remote_mute" and not self._is_self:
            self.post_message(self.RemoteMuteToggled(self, self._alias))
        elif bid == "pop_self_mute" and self._is_self:
            self.post_message(self.SelfMuteToggled(self))
        elif bid == "pop_self_deafen" and self._is_self:
            self.post_message(self.SelfDeafenToggled(self))
        event.stop()

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