"""
communication/peer.py - Peer Management
========================================
Manages known peers (other agents) with persistent JSON storage.
"""
from __future__ import annotations

import json
import time
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Optional, List, Dict


@dataclass
class Peer:
    """Represents a known remote/local agent peer."""

    agent_id: str                          # Agent 的数字 aid（如 "1", "2"）或对端标识
    public_key: str                        # Ed25519 public key (hex, 64 chars)
    display_name: str = ""                 # Human-readable name
    added_at: float = 0.0                  # Unix timestamp
    permissions: Dict[str, bool] = field(default_factory=lambda: {
        "send_messages": True,
        "receive_messages": True,
    })
    last_seen: float = 0.0                 # Last online timestamp
    online: bool = False                   # Current online status

    def to_dict(self) -> dict:
        return {
            "agent_id": self.agent_id,
            "public_key": self.public_key,
            "display_name": self.display_name,
            "added_at": self.added_at,
            "permissions": self.permissions,
            "last_seen": self.last_seen,
            "online": self.online,
        }

    @classmethod
    def from_dict(cls, data: dict) -> "Peer":
        return cls(
            agent_id=data.get("agent_id", ""),
            public_key=data.get("public_key", ""),
            display_name=data.get("display_name", ""),
            added_at=data.get("added_at", 0.0),
            permissions=data.get("permissions", {
                "send_messages": True,
                "receive_messages": True,
            }),
            last_seen=data.get("last_seen", 0.0),
            online=data.get("online", False),
        )


class PeerStore:
    """Persistent store for known peers backed by a JSON file."""

    def __init__(self, store_path: str | Path):
        self._path = Path(store_path)
        # [v1.23.58] 在初始化时确定文件路径，避免 _save() 每次修改 self._path
        if self._path.suffix:
            self._file_path = self._path
        else:
            self._file_path = self._path / "peers.json"
        self._peers: Dict[str, Peer] = {}
        self._load()

    # ------------------------------------------------------------------
    # Persistence
    # ------------------------------------------------------------------

    def _load(self):
        """Load peers from disk."""
        if not self._file_path.exists():
            return
        try:
            data = json.loads(self._file_path.read_text(encoding="utf-8"))
            for peer_data in data.get("peers", []):
                p = Peer.from_dict(peer_data)
                if p.agent_id:
                    self._peers[p.agent_id] = p
        except (json.JSONDecodeError, IOError) as e:
            # Corrupt file — start fresh
            self._peers = {}

    def _save(self):
        """Save peers to disk."""
        self._file_path.parent.mkdir(parents=True, exist_ok=True)
        data = {
            "peers": [p.to_dict() for p in self._peers.values()],
            "updated_at": time.time(),
        }
        self._file_path.write_text(
            json.dumps(data, indent=2, ensure_ascii=False),
            encoding="utf-8",
        )

    # ------------------------------------------------------------------
    # CRUD
    # ------------------------------------------------------------------

    def add_peer(self, peer: Peer) -> bool:
        """Add a peer. Returns False if already exists."""
        if peer.agent_id in self._peers:
            return False
        if peer.added_at == 0.0:
            peer.added_at = time.time()
        self._peers[peer.agent_id] = peer
        self._save()
        return True

    def remove_peer(self, agent_id: str) -> bool:
        """Remove a peer by agent_id. Returns False if not found."""
        if agent_id not in self._peers:
            return False
        del self._peers[agent_id]
        self._save()
        return True

    def get_peer(self, agent_id: str) -> Optional[Peer]:
        """Get a peer by agent_id."""
        return self._peers.get(agent_id)

    def update_peer(self, agent_id: str, **fields) -> bool:
        """Update fields on an existing peer. Returns False if not found."""
        peer = self._peers.get(agent_id)
        if not peer:
            return False
        for k, v in fields.items():
            if hasattr(peer, k):
                setattr(peer, k, v)
        self._save()
        return True

    def list_peers(self) -> List[Peer]:
        """Return all peers."""
        return list(self._peers.values())

    def peer_count(self) -> int:
        """Return the number of stored peers."""
        return len(self._peers)

    def set_online(self, agent_id: str, online: bool):
        """Mark a peer as online/offline."""
        if agent_id in self._peers:
            self._peers[agent_id].online = online
            if online:
                self._peers[agent_id].last_seen = time.time()
            self._save()

    def get_online_peers(self) -> List[Peer]:
        """Return peers currently marked as online."""
        return [p for p in self._peers.values() if p.online]
