#!/usr/bin/env python
"""
Command Dispatcher - Table-driven command handling for WebSocket events.

This module extracts all command handling logic from the consumer() function
into a clean, maintainable, O(1) lookup-based dispatcher pattern.
"""

import json
import asyncio
import logging
from typing import Protocol, Callable, Any, Optional, Dict
from server.websocket_messaging import sendDict, sendUserInfo, sendEventMessage, sendToUser, ComplexHandler
from config import config

logger = logging.getLogger(__name__)


# ============================================================================
# Command Handler Protocol
# ============================================================================

class CommandHandler(Protocol):
    """Protocol defining the interface for command handlers."""

    async def __call__(self, event: Dict[str, Any], player: Any, websocket: Any) -> None:
        """Handle a command event.

        Args:
            event: The parsed JSON event from the client
            player: The player object from playerRecords
            websocket: The WebSocket connection
        """
        ...


# ============================================================================
# Individual Command Handlers
# ============================================================================

async def handle_stop(event: Dict, player: Any, websocket: Any) -> None:
    """Handle game stop command."""
    await sendUserInfo(player, websocket)
    print('stopped!')
    player.controller = 'inactive'


async def handle_start(event: Dict, player: Any, websocket: Any) -> None:
    """Handle game start command."""
    print('started!')
    player.controller = "active"


async def handle_restart(event: Dict, player: Any, websocket: Any) -> None:
    """Handle game restart command."""
    from functions import playerClass, getOccupations
    import app  # playerRecords is defined in app.py

    print('restart!')
    player = playerClass()
    player.occupations = getOccupations()
    player.id = websocket.userID
    app.playerRecords.set(websocket.userID, player)
    await sendUserInfo(player, websocket)
    player.updateClient = True


async def handle_character_setup(event: Dict, player: Any, websocket: Any) -> None:
    """Handle character setup."""
    from functions import characterSetup

    characterSetup(player, event['message'])
    await sendUserInfo(player, websocket)
    await _check_achievements_trigger(player.id, 'character_created', websocket)


async def handle_device_token(event: Dict, player: Any, websocket: Any) -> None:
    """Handle device token registration."""
    player.deviceToken = event['message']
    print('deviceToken: ' + player.deviceToken)
    await sendUserInfo(player, websocket)


async def handle_get_extracurriculars(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get extracurriculars request."""
    await sendDict(websocket, {
        'type': 'extraCurriculars',
        'extraCurriculars': player.extraCurriculars
    })


async def handle_apply_for_extracurricular(event: Dict, player: Any, websocket: Any) -> None:
    """Handle apply for extracurricular."""
    from functions import applyForExtracurricular

    player.askedQuestions.add("extracurricular")
    applyForExtracurricular(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_quit_extracurricular(event: Dict, player: Any, websocket: Any) -> None:
    """Handle quit extracurricular."""
    from functions import quitExtraCurricular

    quitExtraCurricular(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_retrieve_person(event: Dict, player: Any, websocket: Any) -> None:
    """Handle retrieve person request."""
    from functions import get_person, getPersonDescription
    from conversationEvents import parseConversations

    person = get_person(player, event['message'])

    # Assigning various parameters to our avatar
    person.availableConversations = await parseConversations(player, person)
    person.description = getPersonDescription(person)
    await sendToUser(websocket, json.dumps(person.__dict__, default=ComplexHandler))


async def handle_conversation(event: Dict, player: Any, websocket: Any) -> None:
    """Handle conversation event."""
    from conversationEvents import conversationInit
    from functions import get_person, getPeakEnergy

    event = event['message']
    player.previousGameSpeed = player.gameSpeed
    player.gameSpeed = config.SPEED_PAUSED

    if (player.c.calcEnergy >= player.messageEnergyCost):
        player.c.energy -= player.messageEnergyCost
        getPeakEnergy(player.c)
        await sendDict(websocket, {'type': 'u', 'calcEnergy': player.c.calcEnergy})
        print(event['conversationEvent'], event['cType'])

        conv = None
        for conversation in player.conversations:
            if conversation.character == event['characterID']:
                conv = conversation
                break

        character = get_person(player, event['characterID'])

        if conv is None or conv.character != character.id:  # no conversation, create one
            print("no conversation found for this character, creating new conversation")
            if (not event.get('response', False)):
                event['response'] = False
                print("No response")
            else:
                print("response: " + event['response'])
            conv = await conversationInit(player=player, character=event['characterID'],
                                         cType=event['cType'], response=event['response'])
            print(f"Sending new conversation to user {player.id}")
            await sendToUser(websocket, json.dumps(conv.__dict__, default=ComplexHandler, ensure_ascii=False))
            await sendToUser(websocket, json.dumps(character.__dict__, default=ComplexHandler, ensure_ascii=False))
        else:
            if event['conversationEvent'] == "response" or event['conversationEvent'] == "freeResponse":
                print('response')
                conv.addMessage(event['response'], player.c.id, date=player.date, time=player.time)
                conv.question += 1
                conv = await conversationInit(player=player, character=event['characterID'],
                                             cType=event['cType'], response=event['response'])
                print(conv.conversation[-1].message)
                print(f"Sending conversation update to user {player.id}")
                await sendToUser(websocket, json.dumps(conv.__dict__, default=ComplexHandler, ensure_ascii=False))
                await sendToUser(websocket, json.dumps(character.__dict__, default=ComplexHandler, ensure_ascii=False))
    else:
        player.messageQueue.append("You don't have enough energy to talk right now.")


async def handle_mark_conversation_as_read(event: Dict, player: Any, websocket: Any) -> None:
    """Handle mark conversation as read."""
    from functions import markConversationAsRead

    markConversationAsRead(player, event['message'])


async def handle_speed(event: Dict, player: Any, websocket: Any) -> None:
    """Handle speed change command."""
    from functions import get_speed_button_values, validate_game_speed, log_speed_change

    # Get appropriate button values based on debug mode
    buttonSpeedValues = get_speed_button_values()
    old_speed = player.gameSpeed
    player.previousGameSpeed = old_speed

    if event['message'] == "-":
        # Decrease the speed (move left in array = higher value = slower)
        if old_speed in buttonSpeedValues:
            current_index = buttonSpeedValues.index(old_speed)
            next_index = current_index - 1
            if next_index >= 0:
                player.gameSpeed = buttonSpeedValues[next_index]
                log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="button(-)")
            else:
                logger.debug(f"Speed already at minimum button value: {old_speed}")
        else:
            # Speed not in button values, find nearest and go slower
            logger.warning(f"Speed {old_speed} not in button values, finding nearest")
            for i, btn_speed in enumerate(buttonSpeedValues):
                if old_speed > btn_speed:
                    player.gameSpeed = btn_speed if i == 0 else buttonSpeedValues[i-1]
                    log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="button(-)")
                    break

    elif event['message'] == "+":
        # Increase the speed (move right in array = lower value = faster)
        if old_speed in buttonSpeedValues:
            current_index = buttonSpeedValues.index(old_speed)
            next_index = current_index + 1
            if next_index < len(buttonSpeedValues):
                player.gameSpeed = buttonSpeedValues[next_index]
                log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="button(+)")
            else:
                logger.debug(f"Speed already at maximum button value: {old_speed}")
        else:
            # Speed not in button values, find nearest and go faster
            logger.warning(f"Speed {old_speed} not in button values, finding nearest")
            for i, btn_speed in enumerate(buttonSpeedValues):
                if old_speed > btn_speed:
                    player.gameSpeed = btn_speed
                    log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="button(+)")
                    break

    elif event['message'] == "reset":
        # Reset to previous speed, unless it was a pause state
        if (player.previousGameSpeed == config.SPEED_PAUSED or
            player.previousGameSpeed == config.SPEED_QUESTION_PAUSE):
            player.gameSpeed = config.SPEED_DEFAULT
        else:
            player.gameSpeed = player.previousGameSpeed
        log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="reset")

    else:
        # Direct speed setting - VALIDATE INPUT for security
        requested_speed = event['message']
        player.gameSpeed = validate_game_speed(requested_speed)
        log_speed_change(websocket.userID, old_speed, player.gameSpeed, source="direct")

    # Send updated game speed to client
    await sendDict(websocket, {'type': 'u', 'gameSpeed': player.gameSpeed})


async def handle_focus_update(event: Dict, player: Any, websocket: Any) -> None:
    """Handle focus update."""
    from functions import update_focus

    update_focus(player, event['message']['activityId'], event['message']['newFocus'])
    await sendUserInfo(player, websocket)


async def handle_quit_habit(event: Dict, player: Any, websocket: Any) -> None:
    """Handle quit habit."""
    from functions import quitHabit

    quitHabit(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_stop_quit_habit(event: Dict, player: Any, websocket: Any) -> None:
    """Handle stop quit habit."""
    from functions import stopQuitHabit

    stopQuitHabit(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_purchase_item(event: Dict, player: Any, websocket: Any) -> None:
    """Handle purchase item."""
    from functions import purchaseItem

    purchaseItem(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_purchase_in_app_item(event: Dict, player: Any, websocket: Any) -> None:
    """Handle purchase in-app item."""
    from functions import purchaseInAppItem

    purchaseInAppItem(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_purchase_energy_refill(event: Dict, player: Any, websocket: Any) -> None:
    """Handle purchase energy refill."""
    from monetization.energy_refills import handle_purchase_energy_refill

    def send_to_client_wrapper(player_id, message):
        asyncio.create_task(sendDict(websocket, message))

    handle_purchase_energy_refill(
        player_id=player.id,
        message_data=event.get('message', {}),
        send_to_client=send_to_client_wrapper
    )
    await sendUserInfo(player, websocket)


async def handle_purchase_time_skip(event: Dict, player: Any, websocket: Any) -> None:
    """Handle purchase time skip."""
    from monetization.time_skips import handle_purchase_time_skip

    def send_to_client_wrapper(player_id, message):
        asyncio.create_task(sendDict(websocket, message))

    handle_purchase_time_skip(
        player_id=player.id,
        message_data=event.get('message', {}),
        send_to_client=send_to_client_wrapper
    )
    await sendUserInfo(player, websocket)


async def handle_get_energy_refill_tiers(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get energy refill tiers."""
    from monetization.energy_refills import get_refill_tiers

    tiers = get_refill_tiers()
    await sendDict(websocket, {'type': 'energyRefillTiers', 'tiers': tiers})


async def handle_get_time_skip_tiers(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get time skip tiers."""
    from monetization.time_skips import get_skip_tiers

    tiers = get_skip_tiers()
    await sendDict(websocket, {'type': 'timeSkipTiers', 'tiers': tiers})


async def handle_get_achievements(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get achievements."""
    from retention.achievements import get_player_achievements

    achievements = get_player_achievements(player.id)
    await sendDict(websocket, {'type': 'achievementsList', 'achievements': achievements})


async def handle_acknowledge_achievement(event: Dict, player: Any, websocket: Any) -> None:
    """Handle acknowledge achievement."""
    achievement_id = event.get('message', {}).get('achievementId')
    if achievement_id:
        print(f"Player {player.id} acknowledged achievement {achievement_id}")
        # TODO: Implement acknowledge_achievement in retention/achievements.py


async def handle_get_daily_rewards(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get daily rewards."""
    from retention.daily_rewards import handle_daily_login_check

    def send_to_client_wrapper(player_id, message):
        asyncio.create_task(sendDict(websocket, message))

    handle_daily_login_check(player.id, send_to_client_wrapper)


async def handle_claim_daily_reward(event: Dict, player: Any, websocket: Any) -> None:
    """Handle claim daily reward."""
    from retention.daily_rewards import claim_daily_reward

    day = event.get('message', {}).get('day', 1)
    result = claim_daily_reward(player.id)
    await sendDict(websocket, {'type': 'dailyRewardClaimed', 'result': result})
    await sendUserInfo(player, websocket)


async def handle_get_daily_quests(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get daily quests."""
    from retention.daily_quests import handle_daily_quest_check

    def send_to_client_wrapper(player_id, message):
        asyncio.create_task(sendDict(websocket, message))

    handle_daily_quest_check(player.id, send_to_client_wrapper)


async def handle_claim_quest_reward(event: Dict, player: Any, websocket: Any) -> None:
    """Handle claim quest reward."""
    from retention.daily_quests import claim_quest_reward, handle_daily_quest_check

    quest_id = event.get('message', {}).get('questId')
    if quest_id:
        result = claim_quest_reward(player.id, quest_id)
        await sendDict(websocket, {'type': 'questRewardClaimed', 'result': result})
        if result['success']:
            await sendUserInfo(player, websocket)
            # Refresh quest state
            def send_to_client_wrapper(player_id, message):
                asyncio.create_task(sendDict(websocket, message))
            handle_daily_quest_check(player.id, send_to_client_wrapper)


async def handle_get_player_statistics(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get player statistics."""
    from retention.statistics import get_player_statistics

    stats = get_player_statistics(player.id)
    await sendDict(websocket, {'type': 'playerStatistics', 'statistics': stats})


async def handle_export_data(event: Dict, player: Any, websocket: Any) -> None:
    """Handle data export."""
    from api.data_management import DataManagementService
    from database import get_database_connection

    conn = None
    try:
        conn = get_database_connection()
        service = DataManagementService(db_connection=conn)
        export_data = service.export_player_data(player.id)
        await sendDict(websocket, {
            'type': 'dataExportComplete',
            'data': export_data
        })
    except Exception as e:
        logging.error(f"Data export error: {e}")
        await sendDict(websocket, {
            'type': 'error',
            'error_code': 'EXPORT_FAILED',
            'message': str(e)
        })
    finally:
        if conn:
            conn.close()


async def handle_delete_account(event: Dict, player: Any, websocket: Any) -> None:
    """Handle account deletion."""
    from api.data_management import DataManagementService
    from database import get_database_connection

    confirmation = event.get('message', {}).get('confirmation', '')
    if confirmation != 'DELETE':
        await sendDict(websocket, {
            'type': 'error',
            'error_code': 'INVALID_CONFIRMATION',
            'message': 'Must type DELETE to confirm'
        })
    else:
        conn = None
        try:
            conn = get_database_connection()
            service = DataManagementService(db_connection=conn)
            result = service.delete_player_account(player.id)
            await sendDict(websocket, {
                'type': 'accountDeletionScheduled',
                'gracePeriodDays': 30,
                'result': result
            })
        except Exception as e:
            logging.error(f"Account deletion error: {e}")
            await sendDict(websocket, {
                'type': 'error',
                'error_code': 'DELETION_FAILED',
                'message': str(e)
            })
        finally:
            if conn:
                conn.close()


async def handle_apply_for_job(event: Dict, player: Any, websocket: Any) -> None:
    """Handle apply for job."""
    from functions import applyForJob

    applyForJob(player, event['message'])
    await sendUserInfo(player, websocket)
    await _check_achievements_trigger(player.id, 'job_applied', websocket)


async def handle_quit_job(event: Dict, player: Any, websocket: Any) -> None:
    """Handle quit job."""
    from functions import quitJob

    quitJob(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_romance(event: Dict, player: Any, websocket: Any) -> None:
    """Handle romance action."""
    from functions import romance

    romance(player, event['message'])
    await sendUserInfo(player, websocket)
    await _check_achievements_trigger(player.id, 'relationship_started', websocket)


async def handle_date_night(event: Dict, player: Any, websocket: Any) -> None:
    """Handle date night."""
    from functions import dateNight

    result = dateNight(player, event['message'])
    if result is not False:
        await sendEventMessage(websocket, result)
    else:
        failure_message = {
            "type": "error",
            "title": "Date Night Failed",
            "message": "Not enough energy or money for the date night."
        }
        await sendEventMessage(websocket, failure_message)


async def handle_break_up(event: Dict, player: Any, websocket: Any) -> None:
    """Handle break up or divorce."""
    from functions import breakUp

    breakUp(player, event['message'])
    await sendUserInfo(player, websocket)


async def handle_partner_gift(event: Dict, player: Any, websocket: Any) -> None:
    """Handle partner gift."""
    from functions import partnerGift

    result = partnerGift(player, event['message'])
    if result is not False:
        await sendEventMessage(websocket, result)
    else:
        failure_message = {
            "type": "error",
            "title": "Date Night Failed",
            "message": "Not enough energy or money for the gift."
        }
        await sendEventMessage(websocket, failure_message)


async def handle_reset_speed(event: Dict, player: Any, websocket: Any) -> None:
    """Handle reset speed."""
    player.gameSpeed = player.previousGameSpeed


async def handle_swipe_match(event: Dict, player: Any, websocket: Any) -> None:
    """Handle swipe match."""
    player.swipeCharacter.relationships.append('dating_match')
    player.swipeCharacter.title = "Dating Match"
    player.messageQueue.append("You matched with " + player.swipeCharacter.firstname + " " + player.swipeCharacter.lastname + "!")
    player.swipeCharacter.affinity += 40
    player.r.append(player.swipeCharacter)
    player.swipeCharacter = False


async def handle_get_swipe_character(event: Dict, player: Any, websocket: Any) -> None:
    """Handle get swipe character."""
    from functions import create_character
    import random

    # if player.c.sex == Male, sex= female if player.c.sex == female sex= male
    sex = 'Male' if player.c.sex == 'Female' else 'Female'

    character = create_character(player, sex, age='random_adults', relationship='none')
    age_range_width = 5
    lower_bound = max(18, player.c.ageYears - age_range_width)
    upper_bound = player.c.ageYears + age_range_width

    # Generate a random age within the age range for the character
    random_age = random.randint(lower_bound, upper_bound)
    character.ageYears = random_age
    player.swipeCharacter = character
    print("sending character: " + character.firstname + " " + character.lastname)
    await sendDict(websocket, {"type": event['type'], 'swipeCharacter': character})


async def handle_generic_event(event: Dict, player: Any, websocket: Any) -> None:
    """Handle generic event with event handler system and cost deduction."""
    from event_handlers import call_event_handler, InvalidEventError
    from server.websocket_messaging import sendEventMessage
    from functions import messageFunction

    event['key'] = False
    if "---" in event['type']:
        event['key'] = event['type'].split("---")[1]
        event['type'] = event['type'].split("---")[0]

    # Store original values before deducting costs (for rollback)
    original_energy = player.c.energy
    original_money = player.c.money
    original_diamonds = player.c.diamonds

    try:
        # Deduct costs if present
        if 'message' in event and isinstance(event['message'], dict):
            message = event['message']
            if 'energyCost' in message:
                player.c.energy -= message['energyCost']
            if 'moneyCost' in message:
                player.c.money -= message['moneyCost']
            if 'diamondCost' in message:
                player.c.diamonds -= message['diamondCost']

        # Secure event dispatch
        call_event_handler(event['type'], player, 'answer', event.get('key'), event.get('message'))
        player.updateClient = True
        player.gameSpeed = player.previousGameSpeed
        player.askedQuestions.add(event['type'])
    except InvalidEventError as e:
        # Rollback costs
        player.c.energy = original_energy
        player.c.money = original_money
        player.c.diamonds = original_diamonds
        print(f"Invalid event rejected: {e}")
        from app import error
        await error(websocket, f"Invalid event type: {event['type']}")
    except Exception as e:
        # Rollback costs
        player.c.energy = original_energy
        player.c.money = original_money
        player.c.diamonds = original_diamonds
        print(f"Event handler error: {e}")
        logger.error(f"Handler crashed: {event['type']}", exc_info=True)
        from app import error
        await error(websocket, "An error occurred processing your request")

    # if player.messageQueue > 0 send the first message in the queue
    if player.messageQueue and len(player.messageQueue) > 0:
        player.message = player.messageQueue.pop(0)
        player.messageLog.append(player.message)
        print('sending message from queue ' + player.message)
        await sendEventMessage(websocket, messageFunction('answerQueue', player.message, player, True))
        player.message = ""


# ============================================================================
# Helper Functions
# ============================================================================

async def _check_achievements_trigger(player_id: str, event_type: str, websocket: Any) -> None:
    """Auto-check achievements when events occur."""
    from retention.achievements import check_achievements

    def send_to_client_wrapper(pid, message):
        asyncio.create_task(sendDict(websocket, message))

    # Check achievements and send any unlocked achievements to client
    unlocked_achievements = check_achievements(player_id, event_type)
    if unlocked_achievements:
        for achievement in unlocked_achievements:
            send_to_client_wrapper(player_id, {
                'type': 'achievementUnlocked',
                'achievement': achievement
            })


# ============================================================================
# Command Registry (O(1) lookup)
# ============================================================================

COMMAND_REGISTRY: Dict[str, CommandHandler] = {
    # Game control
    'stop': handle_stop,
    'start': handle_start,
    'restart': handle_restart,

    # Character & settings
    'characterSetup': handle_character_setup,
    'deviceToken': handle_device_token,

    # Extracurriculars
    'getExtraCurriculars': handle_get_extracurriculars,
    'applyForExtracurricular': handle_apply_for_extracurricular,
    'quitExtracurricular': handle_quit_extracurricular,

    # People & conversations
    'retrievePerson': handle_retrieve_person,
    'conversation': handle_conversation,
    'markConversationAsRead': handle_mark_conversation_as_read,

    # Game speed
    'speed': handle_speed,
    'resetSpeed': handle_reset_speed,

    # Activities & habits
    'focusUpdate': handle_focus_update,
    'quitHabit': handle_quit_habit,
    'stopQuitHabit': handle_stop_quit_habit,

    # Purchases
    'purchaseItem': handle_purchase_item,
    'purchaseInAppItem': handle_purchase_in_app_item,
    'purchaseEnergyRefill': handle_purchase_energy_refill,
    'purchaseTimeSkip': handle_purchase_time_skip,

    # Monetization tiers
    'getEnergyRefillTiers': handle_get_energy_refill_tiers,
    'getTimeSkipTiers': handle_get_time_skip_tiers,

    # Achievements
    'getAchievements': handle_get_achievements,
    'acknowledgeAchievement': handle_acknowledge_achievement,

    # Daily rewards
    'getDailyRewards': handle_get_daily_rewards,
    'claimDailyReward': handle_claim_daily_reward,

    # Daily quests
    'getDailyQuests': handle_get_daily_quests,
    'claimQuestReward': handle_claim_quest_reward,

    # Statistics
    'getPlayerStatistics': handle_get_player_statistics,

    # Data management
    'exportData': handle_export_data,
    'deleteAccount': handle_delete_account,

    # Jobs
    'applyForJob': handle_apply_for_job,
    'quitJob': handle_quit_job,

    # Romance
    'romance': handle_romance,
    'dateNight': handle_date_night,
    'breakUp': handle_break_up,
    'divorce': handle_break_up,  # Same handler as breakUp
    'partnerGift': handle_partner_gift,

    # Swipe/dating
    'swipeMatch': handle_swipe_match,
    'getSwipeCharacter': handle_get_swipe_character,
}


# ============================================================================
# Main Dispatch Function
# ============================================================================

async def dispatch_command(event: Dict[str, Any], player: Any, websocket: Any) -> None:
    """
    Dispatch a command to the appropriate handler using O(1) dictionary lookup.

    This function replaces the massive if/elif chain in consumer() with a clean,
    table-driven approach that's easier to maintain and extend.

    Args:
        event: The parsed JSON event from the client
        player: The player object from playerRecords
        websocket: The WebSocket connection

    Raises:
        Exception: Any exception raised by the handler
    """
    # Determine command type (either event['message'] or event['type'])
    command_type = None

    # Check for message-based commands (stop, start, restart)
    if event.get('message') in ['stop', 'start', 'restart']:
        command_type = event['message']
    # Otherwise use event type
    elif 'type' in event:
        command_type = event['type']

    if not command_type:
        logger.warning(f"No command type found in event: {event}")
        return

    # O(1) lookup in registry
    handler = COMMAND_REGISTRY.get(command_type)

    if handler:
        # Call the specific handler
        await handler(event, player, websocket)
    else:
        # Fall back to generic event handler for unrecognized commands
        # This handles all the event system commands
        await handle_generic_event(event, player, websocket)
