"""Provides classes for holding and managing notifications."""

from __future__ import annotations

from dataclasses import dataclass, field
from time import time
from typing import Iterator
from uuid import uuid4

from rich.repr import Result
from typing_extensions import Literal, Self, TypeAlias

from textual.message import Message

SeverityLevel: TypeAlias = Literal["information", "warning", "error"]
"""The severity level for a notification."""


@dataclass
class Notify(Message, bubble=False):
    """Message to show a notification."""

    notification: Notification


@dataclass
class Notification:
    """Holds the details of a notification."""

    message: str
    """The message for the notification."""

    title: str = ""
    """The title for the notification."""

    severity: SeverityLevel = "information"
    """The severity level for the notification."""

    timeout: float = 5
    """The timeout (in seconds) for the notification."""

    markup: bool = False
    """Render the notification message as content markup?"""

    raised_at: float = field(default_factory=time)
    """The time when the notification was raised (in Unix time)."""

    identity: str = field(default_factory=lambda: str(uuid4()))
    """The unique identity of the notification."""

    @property
    def time_left(self) -> float:
        """The time left until this notification expires"""
        return (self.raised_at + self.timeout) - time()

    @property
    def has_expired(self) -> bool:
        """Has the notification expired?"""
        return self.time_left <= 0

    def __rich_repr__(self) -> Result:
        yield "message", self.message
        yield "title", self.title, ""
        yield "severity", self.severity
        yield "raised_it", self.raised_at
        yield "identity", self.identity
        yield "time_left", self.time_left
        yield "has_expired", self.has_expired


class Notifications:
    """Class for managing a collection of notifications."""

    def __init__(self) -> None:
        """Initialise the notification collection."""
        self._notifications: dict[str, Notification] = {}

    def _reap(self) -> Self:
        """Remove any expired notifications from the notification collection."""
        for notification in list(self._notifications.values()):
            if notification.has_expired:
                del self._notifications[notification.identity]
        return self

    def add(self, notification: Notification) -> Self:
        """Add the given notification to the collection of managed notifications.

        Args:
            notification: The notification to add.

        Returns:
            Self.
        """
        self._reap()._notifications[notification.identity] = notification
        return self

    def clear(self) -> Self:
        """Clear all the notifications."""
        self._notifications.clear()
        return self

    def __len__(self) -> int:
        """The number of notifications."""
        return len(self._reap()._notifications)

    def __iter__(self) -> Iterator[Notification]:
        return iter(self._reap()._notifications.values())

    def __contains__(self, notification: Notification) -> bool:
        return notification.identity in self._notifications

    def __delitem__(self, notification: Notification) -> None:
        try:
            del self._reap()._notifications[notification.identity]
        except KeyError:
            # An attempt to remove a notification we don't know about is a
            # no-op. What matters here is that the notification is forgotten
            # about, and it looks like a caller has tried to be
            # belt-and-braces. We're fine with this.
            pass
