# -*- coding: utf-8 -*-
"""
Synchronous desktop notification API
"""
from __future__ import annotations

import asyncio
from typing import Any, Callable, Coroutine, Sequence, TypeVar

from .common import (
    DEFAULT_ICON,
    Attachment,
    Button,
    Capability,
    Icon,
    Notification,
    ReplyField,
    Sound,
    Urgency,
)
from .main import DesktopNotifier

__all__ = ["DesktopNotifierSync"]


T = TypeVar("T")


class DesktopNotifierSync:
    """
    A synchronous counterpart to :class:`desktop_notifier.main.DesktopNotifier`

    .. warning::
        Callbacks on interaction with the notification will not work on macOS or Linux
        without a running event loop.
    """

    def __init__(
        self,
        app_name: str = "Python",
        app_icon: Icon | None = DEFAULT_ICON,
        notification_limit: int | None = None,
    ) -> None:
        self._async_api = DesktopNotifier(app_name, app_icon, notification_limit)
        self._loop = asyncio.new_event_loop()

    def _run_coro_sync(self, coro: Coroutine[None, None, T]) -> T:
        # Make sure to always use the same loop because async queues, future, etc. are
        # always bound to a loop.
        if self._loop.is_running():
            future = asyncio.run_coroutine_threadsafe(coro, self._loop)
            res = future.result()
        else:
            res = self._loop.run_until_complete(coro)

        return res

    @property
    def app_name(self) -> str:
        """The application name"""
        return self._async_api.app_name

    @app_name.setter
    def app_name(self, value: str) -> None:
        self._async_api.app_name = value

    @property
    def app_icon(self) -> Icon | None:
        """The application icon"""
        return self._async_api.app_icon

    @app_icon.setter
    def app_icon(self, value: Icon | None) -> None:
        self._async_api.app_icon = value

    def request_authorisation(self) -> bool:
        """See :meth:`desktop_notifier.main.DesktopNotifier.request_authorisation`"""
        coro = self._async_api.request_authorisation()
        return self._run_coro_sync(coro)

    def has_authorisation(self) -> bool:
        """See :meth:`desktop_notifier.main.DesktopNotifier.has_authorisation`"""
        coro = self._async_api.has_authorisation()
        return self._run_coro_sync(coro)

    def send_notification(self, notification: Notification) -> str:
        """See :meth:`desktop_notifier.main.DesktopNotifier.send_notification`"""
        coro = self._async_api.send_notification(notification)
        return self._run_coro_sync(coro)

    def send(
        self,
        title: str,
        message: str,
        urgency: Urgency = Urgency.Normal,
        icon: Icon | None = None,
        buttons: Sequence[Button] = (),
        reply_field: ReplyField | None = None,
        on_dispatched: Callable[[], Any] | None = None,
        on_clicked: Callable[[], Any] | None = None,
        on_dismissed: Callable[[], Any] | None = None,
        attachment: Attachment | None = None,
        sound: Sound | None = None,
        thread: str | None = None,
        timeout: int = -1,  # in seconds
    ) -> str:
        """See :meth:`desktop_notifier.main.DesktopNotifier.send`"""
        notification = Notification(
            title,
            message,
            urgency=urgency,
            icon=icon,
            buttons=tuple(buttons),
            reply_field=reply_field,
            on_dispatched=on_dispatched,
            on_clicked=on_clicked,
            on_dismissed=on_dismissed,
            attachment=attachment,
            sound=sound,
            thread=thread,
            timeout=timeout,
        )
        coro = self._async_api.send_notification(notification)
        return self._run_coro_sync(coro)

    def get_current_notifications(self) -> list[str]:
        """See :meth:`desktop_notifier.main.DesktopNotifier.get_current_notifications`"""
        coro = self._async_api.get_current_notifications()
        return self._run_coro_sync(coro)

    def clear(self, identifier: str) -> None:
        """See :meth:`desktop_notifier.main.DesktopNotifier.clear`"""
        coro = self._async_api.clear(identifier)
        return self._run_coro_sync(coro)

    def clear_all(self) -> None:
        """See :meth:`desktop_notifier.main.DesktopNotifier.clear_all`"""
        coro = self._async_api.clear_all()
        return self._run_coro_sync(coro)

    def get_capabilities(self) -> frozenset[Capability]:
        """See :meth:`desktop_notifier.main.DesktopNotifier.get_capabilities`"""
        coro = self._async_api.get_capabilities()
        return self._run_coro_sync(coro)
