import sys

import wx
import wx.adv

from clarabot_nano.control.page.login import ControlLogin
from clarabot_nano.control.window import ControlWindow
from clarabot_nano.development.spawn.control.tester import ControlApplicationTester, phase_timeout


class _WindowGeomControlApplicationTester(ControlApplicationTester):
    """
    Test sane window positioning is kept through user actions.
    """

    def __init__(self, *args, **kwargs):
        ControlApplicationTester.__init__(self, *args, **kwargs)
        self._window_geo = None

    @phase_timeout(5.0)
    def _phase_0(self, w: ControlWindow):
        """
        When the nano is not logged in (gateway_status is wait-config) the window will be shown on startup, even if
        tray icon is available. The central widget will be the ControlLogin widget.
        """
        config = w.core_interface.get_config()
        assert (v := config["state"].get("gateway_status")) == "waiting-config", str(v)
        assert w.IsShown()
        central_widget = w.central_widget()
        assert isinstance(central_widget, ControlLogin)
        return True

    def _phase_1(self, w: ControlWindow):
        """
        Move the window to a custom position on the primary screen.
        """
        self._window_geo = self._move_window_around(w)
        return True

    def _phase_2(self, w: ControlWindow):
        """
        Minimize the window.
        """
        w.Iconize(True)
        return True

    def _phase_3(self, w: ControlWindow):
        """
        Restore it.
        """
        assert w.IsIconized()
        w.Restore()
        return True

    @phase_timeout(1.0)  # may help with window positioning on GTK+GNOME desktop
    def _phase_4(self, w: ControlWindow):
        """
        Check position.
        """
        assert not w.IsIconized()
        w_geo = w.GetScreenRect()
        assert self._window_geo == w_geo, f"window geo mismatch\ncurrent: {w_geo}\ntarget : {self._window_geo}"
        if w.tray_icon is None:
            return len(self._tester_phases)  # jump to done
        return True

    def _phase_5(self, w: ControlWindow):
        """
        Hide the window to the tray icon.
        """
        assert w.IsShown()
        w.Close()
        return True

    def _phase_6(self, w: ControlWindow):
        """
        Assert window is hidden and restore it.
        """
        assert not w.IsShown()
        w.tray_icon.ProcessEvent(wx.CommandEvent(wx.adv.wxEVT_TASKBAR_LEFT_DOWN))
        return True

    @phase_timeout(1.0)  # may help with window positioning on GTK+GNOME desktop
    def _phase_7(self, w: ControlWindow):
        """
        Check position.
        """
        assert not w.IsIconized()
        assert w.IsShown()
        w_geo = w.GetScreenRect()
        assert self._window_geo == w_geo, f"window geo mismatch\ncurrent: {w_geo}\ntarget : {self._window_geo}"
        return True

    def _phase_8(self, w: ControlWindow):
        """
        Move and minimize the window.
        """
        assert not w.IsIconized()
        assert w.IsShown()
        self._window_geo = self._move_window_around(w)
        w.Iconize(True)
        return True

    def _phase_9(self, w: ControlWindow):
        """
        Close it to the tray again.
        """
        assert w.IsIconized()
        assert w.IsShown()
        w.Close()
        return True

    def _phase_10(self, w: ControlWindow):
        """
        Restore it.
        """
        # NOTE: iconized is not lost but cannot be queried when the window closed on some platforms
        if sys.platform.startswith("win"):
            assert w.IsIconized()
        else:
            assert not w.IsIconized()
        assert not w.IsShown()
        w.tray_icon.ProcessEvent(wx.CommandEvent(wx.adv.wxEVT_TASKBAR_LEFT_DOWN))
        return True

    @phase_timeout(2.0)
    def _phase_11(self, w: ControlWindow):
        """
        On GTK+GNOME desktop these events are sometimes lost, so retry a few times.
        """
        if w.IsIconized():
            w.tray_icon.ProcessEvent(wx.CommandEvent(wx.adv.wxEVT_TASKBAR_LEFT_DOWN))
            assert False
        return True

    @phase_timeout(1.0)  # may help with window positioning on GTK+GNOME desktop
    def _phase_12(self, w: ControlWindow):
        """
        Check restored.
        """
        assert not w.IsIconized()
        assert w.IsShown()
        w_geo = w.GetScreenRect()
        assert self._window_geo == w_geo, f"window geo mismatch\ncurrent: {w_geo}\ntarget : {self._window_geo}"
        return True
