"""
"nodelink.player
~~~~~~~~~~~~~~~~
Represents a connected player and their synchronized state.
"""

import time
from typing import Any, Callable, Dict, Optional


class Player:
    """
    A connected player in a nodelink session.

    Attributes:
        id:           unique ID assigned by the server
        state:        dict of synced values — modify freely
        connected_at: unix timestamp of when they connected
        last_seen:    unix timestamp of last received message
    """

    def __init__(self, player_id: str):
        self.id: str = player_id
        self.state: Dict[str, Any] = {}
        self._prev_state: Dict[str, Any] = {}
        self.connected_at: float = time.time()
        self.last_seen: float = time.time()
        self._send_callback: Optional[Callable] = None

    def set_state(self, key: str, value: Any) -> None:
        """Set a single state value."""
        self.state[key] = value

    def get_state(self, key: str, default: Any = None) -> Any:
        """Get a single state value."""
        return self.state.get(key, default)

    def update_state(self, data: Dict[str, Any]) -> None:
        """Merge a dict into the player's state."""
        self.state.update(data)

    def clear_state(self) -> None:
        """Wipe the player's state entirely."""
        self.state.clear()

    def state_changed(self) -> bool:
        """Returns True if state has changed since last sync."""
        return self.state != self._prev_state

    def mark_synced(self) -> None:
        """Mark current state as the last synced snapshot."""
        self._prev_state = dict(self.state)

    def delta(self) -> Dict[str, Any]:
        """
        Return only the fields that changed since last sync.
        Deleted keys are returned as None so receivers know to remove them.
        """
        changed = {
            k: v for k, v in self.state.items()
            if self._prev_state.get(k) != v
        }
        deleted = {
            k: None for k in self._prev_state
            if k not in self.state
        }
        return {**changed, **deleted}

    async def send(self, event: str, data: Any = None) -> None:
        """Send an event directly to this player."""
        if self._send_callback:
            await self._send_callback(event, data)

    def touch(self) -> None:
        """Update last_seen timestamp."""
        self.last_seen = time.time()

    def __repr__(self) -> str:
        return f"<Player id={self.id!r}>"
