"""System tray icon — the pet window is frameless with no taskbar entry, so
this menu is the only always-available way to control or exit it: talk now,
mute, wander, click-through, nap, history, wake-word tuning, voice reset,
quit.

Every entry is a plain callback passed in by ui/app.py; this file knows
nothing about the controller or the pet window.
"""

from __future__ import annotations

from typing import Callable, Optional

from PySide6.QtCore import Qt
from PySide6.QtGui import QAction, QColor, QIcon, QPainter, QPixmap
from PySide6.QtWidgets import QMenu, QSystemTrayIcon


def _make_icon(muted: bool, napping: bool = False) -> QIcon:
    pixmap = QPixmap(32, 32)
    pixmap.fill(Qt.transparent)
    painter = QPainter(pixmap)
    painter.setRenderHint(QPainter.Antialiasing)
    if muted:
        color = QColor(200, 60, 60)
    elif napping:
        color = QColor(120, 120, 140)
    else:
        color = QColor(120, 170, 240)
    painter.setBrush(color)
    painter.setPen(Qt.NoPen)
    painter.drawEllipse(2, 2, 28, 28)
    painter.end()
    return QIcon(pixmap)


class PetTray(QSystemTrayIcon):
    def __init__(
        self,
        on_talk_now: Callable[[], None],
        on_toggle_mute: Callable[[], bool],
        on_quit: Callable[[], None],
        on_set_wander: Optional[Callable[[bool], None]] = None,
        wander_enabled: bool = True,
        on_set_click_through: Optional[Callable[[bool], None]] = None,
        click_through_enabled: bool = False,
        on_set_nap: Optional[Callable[[bool], None]] = None,
        on_show_history: Optional[Callable[[], None]] = None,
        on_show_wake_tuner: Optional[Callable[[], None]] = None,
        on_reset_voice: Optional[Callable[[], None]] = None,
        parent=None,
    ):
        super().__init__(_make_icon(muted=False), parent)
        self._on_toggle_mute = on_toggle_mute
        self._muted = False
        self._napping = False
        self.setToolTip("Bolt")

        menu = QMenu()
        self._talk_action = QAction("Talk now", menu)
        self._talk_action.triggered.connect(on_talk_now)
        menu.addAction(self._talk_action)

        self._mute_action = QAction("Mute mic", menu)
        self._mute_action.setCheckable(True)
        self._mute_action.triggered.connect(self._handle_toggle_mute)
        menu.addAction(self._mute_action)

        self._nap_action = None
        if on_set_nap is not None:
            self._nap_action = QAction("Nap (no proactive noise)", menu)
            self._nap_action.setCheckable(True)
            self._nap_action.triggered.connect(lambda checked: on_set_nap(checked))
            menu.addAction(self._nap_action)

        menu.addSeparator()

        if on_set_wander is not None:
            self._wander_action = QAction("Wander around", menu)
            self._wander_action.setCheckable(True)
            self._wander_action.setChecked(wander_enabled)
            self._wander_action.triggered.connect(lambda checked: on_set_wander(checked))
            menu.addAction(self._wander_action)

        if on_set_click_through is not None:
            self._click_through_action = QAction("Click through the pet", menu)
            self._click_through_action.setCheckable(True)
            self._click_through_action.setChecked(click_through_enabled)
            self._click_through_action.setToolTip(
                "Ignore the mouse entirely — control it from this menu instead."
            )
            self._click_through_action.triggered.connect(lambda checked: on_set_click_through(checked))
            menu.addAction(self._click_through_action)

        menu.addSeparator()

        if on_show_history is not None:
            history_action = QAction("History…", menu)
            history_action.triggered.connect(on_show_history)
            menu.addAction(history_action)

        if on_show_wake_tuner is not None:
            tuner_action = QAction("Wake word tuning…", menu)
            tuner_action.triggered.connect(on_show_wake_tuner)
            menu.addAction(tuner_action)

        # Only ever enabled while a server-picked voice (speak_as) is in use —
        # it's the way back from "talk like a pirate", which nothing else
        # undoes short of a restart.
        self._voice_action = None
        if on_reset_voice is not None:
            self._voice_action = QAction("Use default voice", menu)
            self._voice_action.setEnabled(False)
            self._voice_action.triggered.connect(on_reset_voice)
            menu.addAction(self._voice_action)

        menu.addSeparator()
        quit_action = QAction("Quit", menu)
        quit_action.triggered.connect(on_quit)
        menu.addAction(quit_action)

        self.setContextMenu(menu)
        self.show()

    def _handle_toggle_mute(self) -> None:
        self._muted = self._on_toggle_mute()
        self._mute_action.setChecked(self._muted)
        self._refresh_icon()

    def set_voice(self, voice: str) -> None:
        """Reflect the voice the controller is speaking in — a name (or id)
        when the server picked one, "" for Bolt's own."""
        if self._voice_action is None:
            return
        self._voice_action.setEnabled(bool(voice))
        self._voice_action.setText(
            f"Use default voice (now: {voice})" if voice else "Use default voice"
        )

    def set_napping(self, napping: bool) -> None:
        """Reflect a nap the *controller* decided on (quiet hours, fullscreen,
        or a petctl command) — not just ones clicked here."""
        self._napping = napping
        if self._nap_action is not None:
            self._nap_action.setChecked(napping)
        self._refresh_icon()

    def _refresh_icon(self) -> None:
        self.setIcon(_make_icon(self._muted, self._napping))
        if self._muted:
            self.setToolTip("Bolt (muted)")
        elif self._napping:
            self.setToolTip("Bolt (napping)")
        else:
            self.setToolTip("Bolt")
