"""PeerDrop Core — Peer discovery via py-ipfs-lite mDNS."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from peerdrop.core.events import EventBus, PeerDiscovered, PeerLost
from peerdrop.core.models import Peer

if TYPE_CHECKING:
    from py_ipfs_lite.peer import Peer as IPFSPeer

logger = logging.getLogger(__name__)


class DiscoveryManager:
    """Wraps py-ipfs-lite mDNS discovery.

    py-ipfs-lite (via py-libp2p) handles all mDNS broadcasting and listening.
    This manager subscribes to discovery events and maintains a peer map.
    """

    def __init__(self, ipfs_peer: IPFSPeer, event_bus: EventBus) -> None:
        self._ipfs_peer = ipfs_peer
        self._event_bus = event_bus
        self._peers: dict[str, Peer] = {}
        self._handler_registered = False

    async def start(self) -> None:
        """Subscribe to py-libp2p's mDNS peer discovery events."""
        from libp2p.discovery.events.peerDiscovery import peerDiscovery

        if self._handler_registered:
            return
        logger.info(f"[{id(self)}] DiscoveryManager registering handler, _peers id={id(self._peers)}")
        peerDiscovery.register_peer_discovered_handler(self._on_peer_found)
        self._handler_registered = True
        logger.info(f"[{id(self)}] Discovery manager started, listening for mDNS peers")

    def stop(self) -> None:
        """Unregister from py-libp2p's global peer discovery singleton.

        peerDiscovery is a module-level singleton, so without unregistering,
        restarting an engine in the same process would accumulate duplicate
        handlers and duplicate discovery callbacks.
        """
        if not self._handler_registered:
            return
        try:
            from libp2p.discovery.events.peerDiscovery import peerDiscovery
            peerDiscovery.unregister_peer_discovered_handler(self._on_peer_found)
        except Exception as e:
            logger.warning(f"Failed to unregister discovery handler: {e}")
        self._handler_registered = False

    def _on_peer_found(self, peer_info: Any) -> None:
        """Callback when mDNS discovers a new peer."""
        from libp2p.peer.peerinfo import PeerInfo

        if not isinstance(peer_info, PeerInfo):
            return

        peer_id = str(peer_info.peer_id)
        addrs = [str(addr) for addr in peer_info.addrs]

        peer = Peer(
            peer_id=peer_id,
            addrs=addrs,
            name="",
        )
        self._peers[peer_id] = peer
        logger.info(f"[{id(self)}] Peer discovered: {peer_id} at {addrs}")
        logger.info(f"[{id(self)}] _peers now has {len(self._peers)} entries: {list(self._peers.keys())}")

        self._event_bus.publish(PeerDiscovered(
            peer_id=peer_id,
            addrs=addrs,
        ))

    def get_peers(self) -> list[Peer]:
        """Return all discovered peers."""
        result = list(self._peers.values())
        logger.info(f"[{id(self)}] get_peers() called, returning {len(result)} peers from {len(self._peers)} entries: {list(self._peers.keys())}")
        return result

    def get_peer(self, peer_id: str) -> Peer | None:
        """Lookup a peer by ID."""
        return self._peers.get(peer_id)

    def remove_peer(self, peer_id: str) -> None:
        """Mark a peer as offline and emit PeerLost."""
        if peer_id in self._peers:
            peer = self._peers.pop(peer_id)
            peer.is_online = False
            logger.info(f"Peer lost: {peer_id}")
            self._event_bus.publish(PeerLost(peer_id=peer_id))
