#!/usr/bin/env python3

import asyncio
import websockets
import logging
import argparse

# --- Setup Logging ---
# Configure logging to show server activity.
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("WebSocketTestServer")

# --- Connection Counter ---
# A simple counter to keep track of connected clients.
connected_clients = set()

async def handler(websocket, path):
    """
    Handles incoming WebSocket connections.

    This function is executed for each client that connects to the server.
    It registers the client, listens for messages, and handles disconnection.
    """
    # Register client
    connected_clients.add(websocket)
    remote_address = websocket.remote_address
    logger.info(f"Client connected from {remote_address}. Total clients: {len(connected_clients)}")

    try:
        # The 'async for' loop waits for messages from the client.
        # It will run until the client disconnects.
        async for message in websocket:
            # Check if the received message is text or binary data
            if isinstance(message, str):
                logger.info(f"Received text message from {remote_address}: {message[:80]}...") # Log first 80 chars
            elif isinstance(message, bytes):
                logger.warning(f"Received {len(message)} bytes of binary data from {remote_address}. (Potential 'malformed' attack)")
            else:
                logger.warning(f"Received unknown message type from {remote_address}.")

            # (Optional) Echo the message back to the client
            # await websocket.send(f"Server received: {message}")

    except websockets.exceptions.ConnectionClosedOK:
        logger.info(f"Client {remote_address} disconnected gracefully.")
    except websockets.exceptions.ConnectionClosedError as e:
        logger.error(f"Client {remote_address} disconnected with error: {e}")
    finally:
        # Unregister client upon disconnection
        connected_clients.remove(websocket)
        logger.info(f"Client {remote_address} connection closed. Total clients: {len(connected_clients)}")


async def main():
    """
    Starts the WebSocket server.
    """
    parser = argparse.ArgumentParser(
        description="A simple WebSocket server for testing purposes."
    )
    parser.add_argument(
        "--host",
        default="localhost",
        help="The host to bind the server to (e.g., 'localhost' or '0.0.0.0')."
    )
    parser.add_argument(
        "--port",
        type=int,
        default=8765,
        help="The port to run the server on."
    )
    args = parser.parse_args()

    # The 'async with' statement ensures the server is shut down gracefully.
    async with websockets.serve(handler, args.host, args.port):
        logger.info(f"WebSocket server started on ws://{args.host}:{args.port}")
        logger.info("Press Ctrl+C to stop the server.")
        # The server will run forever until it's interrupted.
        await asyncio.Future()


if __name__ == "__main__":
    # To run this server, you need the 'websockets' library:
    # pip install websockets
    #
    # --- How to Run ---
    # 1. Save this script (e.g., as test_server.py).
    # 2. Run from your terminal:
    #    python test_server.py
    #
    # 3. To allow connections from other computers, run on '0.0.0.0':
    #    python test_server.py --host 0.0.0.0
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.info("Server shutting down.")
