"""
Coverage tests for Peer handshake and general message flows.
"""

import base64
import time
from unittest.mock import Mock, patch

from bsv.auth.auth_message import AuthMessage
from bsv.auth.peer import Peer, PeerOptions
from bsv.auth.session_manager import DefaultSessionManager
from bsv.keys import PrivateKey

from .conftest import CaptureTransport, WalletOK, _seed_authenticated_session


def test_peer_initial_handshake_request_response():
    """Test full initial handshake: initialRequest -> initialResponse."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8001))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    # Create initial request message
    client_priv = PrivateKey(8002)
    client_pub = client_priv.public_key()
    client_nonce = base64.b64encode(b"C" * 32).decode()

    initial_msg = AuthMessage(
        version="0.1", message_type="initialRequest", identity_key=client_pub, initial_nonce=client_nonce
    )

    # Handle initial request
    err = peer.handle_initial_request(initial_msg, client_pub)
    assert err is None

    # Check that a response was sent
    assert len(transport.sent_messages) == 1
    response = transport.sent_messages[0]
    assert response.message_type == "initialResponse"
    assert response.identity_key == wallet._pub
    assert response.nonce is not None  # Server nonce
    assert response.your_nonce == client_nonce


def test_peer_initial_handshake_missing_nonce():
    """Test initial handshake fails with missing nonce."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8011))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    client_pub = PrivateKey(8012).public_key()

    # Missing nonce
    initial_msg = AuthMessage(
        version="0.1",
        message_type="initialRequest",
        identity_key=client_pub,
        # nonce is missing
    )

    err = peer.handle_initial_request(initial_msg, client_pub)
    assert err is not None
    assert "nonce" in str(err).lower()


def test_peer_to_peer_with_authenticated_session():
    """Test to_peer sends general message with authenticated session."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8021))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    # Set up authenticated session
    target_pub = PrivateKey(8022).public_key()
    _seed_authenticated_session(session_manager, target_pub)

    # Send message
    test_payload = b"Hello from peer"
    err = peer.to_peer(test_payload, target_pub, 30000)

    assert err is None
    assert len(transport.sent_messages) == 1

    sent_msg = transport.sent_messages[0]
    assert sent_msg.message_type == "general"
    assert sent_msg.payload == test_payload


def test_peer_to_peer_no_session():
    """Test to_peer fails when no session exists."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8031))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    target_pub = PrivateKey(8032).public_key()
    test_payload = b"test"

    err = peer.to_peer(test_payload, target_pub, 30000)

    assert err is not None
    assert "session" in str(err).lower()


def test_peer_to_peer_expired_session():
    """Test to_peer with session that has old timestamp."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8041))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    target_pub = PrivateKey(8042).public_key()

    # Create session with very old timestamp
    session = _seed_authenticated_session(session_manager, target_pub)
    session.last_update = 1  # Very old timestamp

    test_payload = b"test"

    # This should still work (session expiration may not be strictly enforced)
    err = peer.to_peer(test_payload, target_pub, 30000)

    # Either succeeds or fails gracefully - the test ensures the old timestamp is handled
    assert err is None or isinstance(err, Exception)


def test_peer_listen_for_general_messages():
    """Test listener registration and message handling."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8051))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    # Set up authenticated session
    sender_pub = PrivateKey(8052).public_key()
    _seed_authenticated_session(session_manager, sender_pub)

    # Register listener
    received_messages = []

    def on_message(sender_key, payload):
        received_messages.append((sender_key, payload))

    listener_id = peer.listen_for_general_messages(on_message)
    assert listener_id is not None

    # Simulate receiving a general message
    test_payload = b"Incoming message"
    general_msg = AuthMessage(
        version="0.1",
        message_type="general",
        identity_key=sender_pub,
        payload=test_payload,
        your_nonce=session_manager.get_session(sender_pub.hex()).peer_nonce,
    )

    # Handle the message
    err = peer.handle_general_message(general_msg, sender_pub)
    assert err is None

    # Check that listener was called
    assert len(received_messages) == 1
    received_sender, received_payload = received_messages[0]
    assert received_sender == sender_pub
    assert received_payload == test_payload


def test_peer_stop_listening_for_general_messages():
    """Test stopping listeners."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8061))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    # Register listener
    def on_message(sender_key, payload):
        pass

    listener_id = peer.listen_for_general_messages(on_message)
    assert listener_id is not None

    # Stop listening
    peer.stop_listening_for_general_messages(listener_id)

    # Verify listener was removed (implementation detail, but should not crash)
    # If we get here without exception, the operation succeeded


def test_peer_handle_general_message_missing_nonce():
    """Test handle_general_message fails with missing nonce."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8071))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    sender_pub = PrivateKey(8072).public_key()

    general_msg = AuthMessage(
        version="0.1",
        message_type="general",
        identity_key=sender_pub,
        payload=b"test",
        # your_nonce is missing
    )

    err = peer.handle_general_message(general_msg, sender_pub)
    assert err is not None
    assert "nonce" in str(err).lower()


def test_peer_handle_general_message_with_session():
    """Test handle_general_message with valid session and nonce."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8081))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    sender_pub = PrivateKey(8082).public_key()
    session = _seed_authenticated_session(session_manager, sender_pub)

    general_msg = AuthMessage(
        version="0.1",
        message_type="general",
        identity_key=sender_pub,
        payload=b"test",
        your_nonce=session.session_nonce,  # Use correct nonce from session
    )

    err = peer.handle_general_message(general_msg, sender_pub)
    # Should succeed with valid session and nonce
    assert err is None


def test_peer_request_certificates_timeout():
    """Test request_certificates with timeout."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8091))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    target_pub = PrivateKey(8092).public_key()
    _seed_authenticated_session(session_manager, target_pub)

    # Request certificates with very short timeout
    peer.request_certificates(target_pub, {"types": {"t": ["f"]}}, max_wait_time=1)

    # Should timeout or return error (depending on implementation)
    # The test mainly ensures the timeout parameter is handled
    # Either succeeds or fails gracefully


def test_peer_send_certificate_response():
    """Test send_certificate_response sends message."""
    transport = CaptureTransport()
    wallet = WalletOK(PrivateKey(8101))
    session_manager = DefaultSessionManager()
    peer = Peer(PeerOptions(wallet=wallet, transport=transport, session_manager=session_manager))

    target_pub = PrivateKey(8102).public_key()
    _seed_authenticated_session(session_manager, target_pub)

    certs = []  # Empty certificates for this test

    err = peer.send_certificate_response(target_pub, certs)
    assert err is None

    assert len(transport.sent_messages) == 1
    sent_msg = transport.sent_messages[0]
    assert sent_msg.message_type == "certificateResponse"
    assert sent_msg.certificates == certs
