"""
Producer/Consumer Pattern for WebSocket Game Loop

This module implements the producer/consumer pattern for managing WebSocket
message flow and game loop execution with FPS control.

The producer generates game updates at a controlled frame rate (TARGET_FPS),
while the consumer processes incoming client messages and dispatches commands.
"""

import asyncio
import json
import time
import traceback
import websockets
import logging

from config import config
from rate_limiter import RateLimiter

logger = logging.getLogger(__name__)


# FPS control constants
TARGET_FPS = 5000
FRAME_DURATION = 1.0 / TARGET_FPS


async def producer(websocket):
    """
    Produce game updates by calling the main game loop.

    Args:
        websocket: The WebSocket connection with userID attribute

    Returns:
        False to indicate completion
    """
    from game_loop.loop_manager import initLifeSim
    await initLifeSim(websocket)
    return False


async def producer_handler(websocket):
    """
    Handle producer with FPS control and error catching.

    This continuously runs the producer at TARGET_FPS, tracking frame rate
    and handling disconnections/errors gracefully.

    Args:
        websocket: The WebSocket connection with userID attribute
    """
    from server.websocket_handlers import shutdown
    from player_cache import PlayerCache

    # Get the global playerRecords instance
    import app
    playerRecords = app.playerRecords

    start_time = time.time()
    frame_count = 0

    while True:
        try:
            player = playerRecords.get(websocket.userID)
            if (player and player.connection == 'disconnected' and websocket.userID != 'DUMMY_USER_ID'):
                break

            frame_start_time = time.time()
            await producer(websocket)
            frame_end_time = time.time()
            frame_duration = frame_end_time - frame_start_time

            frame_count += 1

            elapsed_time = frame_end_time - start_time

            sleep_duration = FRAME_DURATION - frame_duration
            if elapsed_time >= 1/60:
                await asyncio.sleep(0.001)
                start_time = frame_end_time
                frame_count = 0
            if elapsed_time >= 1.0:
                player = playerRecords.get(websocket.userID)
                if player:
                    player.fps = frame_count / elapsed_time
                    print("FPS: " + str(frame_count / elapsed_time) + ' ' + str(type(player)) + ' ' + str(player.c.firstname) + ' ' + str(player.c.lastname))


        except websockets.exceptions.ConnectionClosed as err:
            print("producer:Client disconnected.  Do cleanup" + str(err))
            await shutdown(websocket)
            break
        except Exception as e:
            print("Error in producer_handler: " + traceback.format_exc())
            await websocket.close()
            await shutdown(websocket)
            break


async def consumer_handler(websocket):
    """
    Handle consumer with rate limiting and error catching.

    This listens for incoming WebSocket messages and processes them
    through the consumer, with rate limiting to prevent abuse.

    Args:
        websocket: The WebSocket connection with userID attribute
    """
    from server.websocket_handlers import error

    # Initialize rate limiter
    rate_limiter = RateLimiter(
        max_requests=config.WEBSOCKET_MAX_MESSAGES_PER_MINUTE,
        window_seconds=60
    )

    async for message in websocket:
        # Check rate limit
        if not rate_limiter.is_allowed(websocket.userID):
            print(f"Rate limit exceeded for {websocket.userID}")
            await error(websocket, "Rate limit exceeded. Please slow down.")
            continue

        await(consumer(message, websocket))


async def consumer(message, websocket):
    """
    Process incoming WebSocket messages and dispatch commands.

    This function uses a table-driven command dispatcher for clean, maintainable
    command handling with O(1) lookup time.

    Args:
        message: The incoming WebSocket message (string or bytes)
        websocket: The WebSocket connection

    Returns:
        True if successful, False if no message
    """
    from server.command_dispatcher import dispatch_command
    from server.websocket_messaging import sendDict
    from functions import handleUpdates

    # Get the global playerRecords instance
    import app
    playerRecords = app.playerRecords

    player = playerRecords.get(websocket.userID)
    updateObject = {
        'date': player.date,
        'hourOfDay': player.hourOfDay,
        'minuteOfHour': player.minuteOfHour,
        'weekDayText': player.weekDayText,
        'energy': player.c.energy,
        'calcEnergy': player.c.calcEnergy,
        'money': player.c.money,
        'diamonds': player.c.diamonds,
        'prestige': player.c.prestige,
        'stress': player.c.stress,
        'happiness': player.c.happiness,
        'occupation': player.c.occupation,
        'location': player.c.location,
        'schedules': player.c.schedules,
        'intraDayMessage': player.c.intraDayMessage,
        'dailyPlan': player.c.dailyPlan,
        'gameSpeed': player.gameSpeed,
    }

    if not message:
        print("no message")
        return False

    # Decode bytes if needed
    if isinstance(message, bytes):
        message = message.decode('utf-8')

    print('received' + message)
    event = json.loads(message)

    # Dispatch command to appropriate handler
    await dispatch_command(event, player, websocket)

    # Handle updates and send to client
    updateObject = handleUpdates(updateObject, player, websocket)
    if updateObject and updateObject != {}:
        await sendDict(websocket, updateObject)

    return True
