from datetime import UTC, datetime

import pytest

from relay.server.models import Group, Message, Peer


class TestPeer:
    def test_auto_generates_peer_id(self):
        peer = Peer(hostname="host", working_directory="/tmp/project", group_id="g1")
        assert len(peer.peer_id) == 12
        assert peer.peer_id.isalnum()

    def test_unique_peer_ids(self):
        peers = [
            Peer(hostname="host", working_directory="/tmp", group_id="g1") for _ in range(50)
        ]
        ids = {p.peer_id for p in peers}
        assert len(ids) == 50

    def test_defaults(self):
        peer = Peer(hostname="host", working_directory="/work", group_id="g1")
        assert peer.git_branch is None
        assert peer.last_read_index == 0
        assert peer.online is False

    def test_explicit_fields(self):
        peer = Peer(
            hostname="myhost",
            working_directory="/home/user/project",
            git_branch="feature-x",
            group_id="team1",
            online=True,
        )
        assert peer.hostname == "myhost"
        assert peer.working_directory == "/home/user/project"
        assert peer.git_branch == "feature-x"
        assert peer.group_id == "team1"
        assert peer.online is True

    def test_display_name_uses_last_directory_segment(self):
        peer = Peer(hostname="laptop", working_directory="/home/user/myproject", group_id="g1")
        assert peer.display_name == "laptop:myproject"

    def test_display_name_strips_trailing_slash(self):
        peer = Peer(hostname="laptop", working_directory="/home/user/myproject/", group_id="g1")
        assert peer.display_name == "laptop:myproject"

    def test_display_name_root_directory(self):
        peer = Peer(hostname="laptop", working_directory="/", group_id="g1")
        assert peer.display_name == "laptop:"

    def test_model_is_mutable(self):
        peer = Peer(hostname="host", working_directory="/tmp", group_id="g1")
        peer.online = True
        assert peer.online is True
        peer.last_read_index = 5
        assert peer.last_read_index == 5


class TestMessage:
    def test_auto_generates_message_id(self):
        msg = Message(
            from_peer="p1", from_display="host:proj", content="hello", group_id="g1"
        )
        assert len(msg.message_id) == 32
        assert msg.message_id.isalnum()

    def test_auto_generates_timestamp(self):
        before = datetime.now(UTC)
        msg = Message(
            from_peer="p1", from_display="host:proj", content="hello", group_id="g1"
        )
        after = datetime.now(UTC)
        assert before <= msg.timestamp <= after

    def test_model_is_frozen(self):
        msg = Message(
            from_peer="p1", from_display="host:proj", content="hello", group_id="g1"
        )
        with pytest.raises(Exception):
            msg.content = "changed"

    def test_unique_message_ids(self):
        msgs = [
            Message(from_peer="p1", from_display="d", content="hi", group_id="g1")
            for _ in range(50)
        ]
        ids = {m.message_id for m in msgs}
        assert len(ids) == 50


class TestGroup:
    def test_empty_group(self):
        group = Group(group_id="g1")
        assert group.group_id == "g1"
        assert group.peers == {}
        assert group.messages == []

    def test_add_peer_to_group(self):
        group = Group(group_id="g1")
        peer = Peer(hostname="host", working_directory="/tmp", group_id="g1")
        group.peers[peer.peer_id] = peer
        assert peer.peer_id in group.peers

    def test_add_message_to_group(self):
        group = Group(group_id="g1")
        msg = Message(
            from_peer="p1", from_display="host:proj", content="hello", group_id="g1"
        )
        group.messages.append(msg)
        assert len(group.messages) == 1
        assert group.messages[0].content == "hello"
