# app.py Refactoring Plan

## Executive Summary
Refactor `/home/user/lichun/ws/app.py` from **1,291 lines** to **under 500 lines** by extracting logical sections into focused modules while maintaining the critical game loop performance.

**Target: app.py reduced to ~350-450 lines**

---

## Current Structure Analysis

### Line Distribution
```
Lines    Section                              Complexity    Extract Priority
======   ==================================   ===========   ================
1-32     Imports                              Low           Keep
34-263   Event Registration (230 lines)       Low           HIGH - Extract
267-344  Helper Classes (78 lines)            Medium        MEDIUM - Extract
346-385  WebSocket Utilities (40 lines)       Medium        HIGH - Extract
387-577  Game Loop (190 lines)                HIGH          LOW - Optimize only
578-645  Producer/Consumer (68 lines)         Medium        MEDIUM - Extract
646-1121 Consumer/Commands (476 lines)        HIGH          CRITICAL - Extract
1123-1292 WebSocket Handlers (170 lines)      Medium        HIGH - Extract
```

---

## Proposed Module Structure

### Module 1: `server/event_registration.py`
**Lines: ~250** | **Priority: HIGH** | **Risk: LOW**

Extract event registration systems that run once at startup.

```python
# server/event_registration.py
"""
Event registration systems for game events and handlers.
Called once at server startup.
"""

def register_all_event_handlers():
    """Register all valid event handlers from various modules"""
    # Move lines 34-135 here
    # Register events.py handlers (~60)
    # Register dayEvents.py handlers (~27)
    # Register conversationEvents.py handlers
    # Register tutorial_events.py handlers
    pass

def register_all_events():
    """Register all game events with conditions for efficient filtering"""
    # Move lines 138-263 here
    # Early childhood events (age 0-5)
    # School-age events (age 6-18)
    # Puberty and teen events (age 10-18)
    # College events (age 17-25)
    # Employment events (age 16+)
    # Relationship events (age 13+)
    # Family events (age 18+)
    # Adult events (age 18+)
    # Health and life events (any age)
    # Random events (any age)
    # Holiday events (day-specific, any age)
    # Dilemma events (age-specific)
    # Conversation events (any age with relationships)
    # Tutorial events (age 0-5)
    # Test events (for development)
    pass

def initialize_all_events():
    """Single function to call both registration functions"""
    register_all_event_handlers()
    register_all_events()
```

**What stays in app.py:**
```python
from server.event_registration import initialize_all_events

# At module level (after imports)
initialize_all_events()
print('startup')
```

**Estimated reduction: 230 lines → 5 lines**

---

### Module 2: `server/websocket_registry.py`
**Lines: ~120** | **Priority: HIGH** | **Risk: LOW**

Extract WebSocket connection management and helper classes.

```python
# server/websocket_registry.py
"""
WebSocket connection registry and batched update system.
Provides O(1) user lookup and efficient state updates.
"""

class UserRegistry:
    """Efficient O(1) user lookup registry (lines 267-299)"""
    pass

class BatchedUpdate:
    """Accumulator for batched game state updates (lines 301-335)"""
    pass

class ServerState:
    """Global server state (lines 341-344)"""
    pass

# Module-level instances
USERS = UserRegistry()
server = ServerState()

def get_user_registry():
    """Get the global user registry"""
    return USERS

def get_server_state():
    """Get the global server state"""
    return server
```

**What stays in app.py:**
```python
from server.websocket_registry import get_user_registry, get_server_state, BatchedUpdate
from player_cache import PlayerCache

USERS = get_user_registry()
server = get_server_state()
playerRecords = PlayerCache(max_size=config.MAX_CONNECTIONS)
lastIteration = False
```

**Estimated reduction: 78 lines → 10 lines**

---

### Module 3: `server/websocket_messaging.py`
**Lines: ~100** | **Priority: HIGH** | **Risk: LOW**

Extract WebSocket messaging utilities and offline game iteration.

```python
# server/websocket_messaging.py
"""
WebSocket messaging utilities for sending data to clients.
Handles JSON serialization and error handling.
"""

async def sendToUser(websocket, message):
    """Send message to user (O(1) lookup) - lines 346-353"""
    pass

async def sendEventMessage(websocket, m):
    """Send event message to user - lines 355-357"""
    pass

async def sendUserInfo(player, websocket):
    """Send full player state to user - lines 358-360"""
    pass

async def sendDict(websocket, obj):
    """Send dictionary as JSON to user - lines 361-363"""
    pass

def ComplexHandler(Obj):
    """Handle complex object serialization - lines 364-366"""
    pass

def get_websocket_for_player(player_id):
    """Get websocket connection for player ID - lines 367-371"""
    pass

async def iterateGames():
    """
    Iterate through offline games and update them.
    Called periodically for disconnected players - lines 372-385
    """
    pass
```

**What stays in app.py:**
```python
from server.websocket_messaging import (
    sendToUser, sendEventMessage, sendUserInfo, sendDict,
    ComplexHandler, get_websocket_for_player, iterateGames
)
```

**Estimated reduction: 40 lines → 4 lines**

---

### Module 4: `game_loop/loop_manager.py`
**Lines: ~250** | **Priority: LOW** | **Risk: HIGH** ⚠️

**CRITICAL PERFORMANCE PATH** - Minimal extraction, internal optimization only.

The `initLifeSim` function (lines 387-577) is the hot path that runs every game tick. We should:
1. **Keep the main loop structure intact** in app.py
2. **Extract only non-critical helper functions**
3. **Optimize without moving code**

```python
# game_loop/loop_manager.py
"""
Game loop helper functions.
Main loop stays in app.py for performance.
"""

def create_hourly_batch_update(player):
    """
    Create batched update object for hourly ticks.
    Extracted from lines 414-432
    """
    batch = BatchedUpdate()
    batch.add('date', player.date)
    batch.add('hourOfDay', player.hourOfDay)
    batch.add('minuteOfHour', player.minuteOfHour)
    batch.add('weekDayText', player.weekDayText)
    batch.add('energy', player.c.energy)
    batch.add('calcEnergy', player.c.calcEnergy)
    batch.add('money', player.c.money)
    batch.add('diamonds', player.c.diamonds)
    batch.add('prestige', player.c.prestige)
    batch.add('stress', player.c.stress)
    batch.add('happiness', player.c.happiness)
    batch.add('occupation', player.c.occupation)
    batch.add('location', player.c.location)
    batch.add('schedules', player.c.schedules)
    batch.add('intraDayMessage', player.c.intraDayMessage)
    batch.add('dailyPlan', player.c.dailyPlan)
    batch.add('gameSpeed', player.gameSpeed)
    return batch.to_dict()

def process_daily_tick(player):
    """
    Process daily tick logic.
    Extracted from lines 442-523
    """
    pass

def process_weekly_tick(player):
    """
    Process weekly tick logic.
    Extracted from lines 471-489
    """
    pass
```

**What changes in app.py:**
```python
from game_loop.loop_manager import (
    create_hourly_batch_update,
    process_daily_tick,
    process_weekly_tick
)

async def initLifeSim(websocket, oneTimePlayer=False):
    """Main game loop - KEEP THIS IN app.py FOR PERFORMANCE"""
    # Inline code stays here, use helpers where appropriate
    # Lines 387-577 mostly stay but cleaned up
    pass
```

**Estimated reduction: 190 lines → ~150 lines** (optimization, not extraction)

---

### Module 5: `game_loop/producer_consumer.py`
**Lines: ~80** | **Priority: MEDIUM** | **Risk: MEDIUM**

Extract producer/consumer pattern and FPS control.

```python
# game_loop/producer_consumer.py
"""
Producer/consumer pattern for game loop execution.
Handles FPS throttling and rate limiting.
"""

TARGET_FPS = 5000
FRAME_DURATION = 1.0 / TARGET_FPS

async def producer(websocket):
    """
    Call game loop for this websocket's player.
    Lines 578-580
    """
    pass

async def producer_handler(websocket):
    """
    Main producer loop with FPS control.
    Lines 585-628
    """
    pass

async def consumer_handler(websocket):
    """
    Main consumer loop with rate limiting.
    Lines 630-644
    """
    pass
```

**What stays in app.py:**
```python
from game_loop.producer_consumer import producer_handler, consumer_handler

# Used in start() function
await asyncio.gather(
    consumer_handler(websocket),
    producer_handler(websocket),
)
```

**Estimated reduction: 68 lines → 3 lines**

---

### Module 6: `server/command_dispatcher.py`
**Lines: ~500** | **Priority: CRITICAL** | **Risk: MEDIUM** ⚠️

**LARGEST REDUCTION OPPORTUNITY** - Extract the massive consumer() function.

This is the biggest win. The consumer() function is 476 lines of if/elif chains. Create a table-driven command dispatch system.

```python
# server/command_dispatcher.py
"""
Command dispatch system for WebSocket messages.
Converts massive if/elif chain into table-driven routing.
"""

from typing import Callable, Dict, Any
import asyncio

class CommandHandler:
    """Base class for command handlers"""

    async def handle(self, player, event, websocket):
        """Handle the command"""
        raise NotImplementedError

class StartCommandHandler(CommandHandler):
    """Handle start command (lines 695-697)"""
    async def handle(self, player, event, websocket):
        print('started!')
        player.controller = "active"

class StopCommandHandler(CommandHandler):
    """Handle stop command (lines 691-694)"""
    async def handle(self, player, event, websocket):
        from server.websocket_messaging import sendUserInfo
        await sendUserInfo(player, websocket)
        print('stopped!')
        player.controller = 'inactive'

class RestartCommandHandler(CommandHandler):
    """Handle restart command (lines 698-706)"""
    async def handle(self, player, event, websocket):
        # Restart logic
        pass

class CharacterSetupCommandHandler(CommandHandler):
    """Handle characterSetup command (lines 707-711)"""
    async def handle(self, player, event, websocket):
        # Character setup logic
        pass

class SpeedCommandHandler(CommandHandler):
    """Handle speed command (lines 778-838)"""
    async def handle(self, player, event, websocket):
        # Complex speed adjustment logic
        pass

class ConversationCommandHandler(CommandHandler):
    """Handle conversation command (lines 736-774)"""
    async def handle(self, player, event, websocket):
        # Conversation logic
        pass

# ... Continue for all ~40 command types ...

class CommandDispatcher:
    """
    Dispatch commands to appropriate handlers.
    Replaces 476-line if/elif chain with table lookup.
    """

    def __init__(self):
        self._handlers: Dict[str, CommandHandler] = {}
        self._register_handlers()

    def _register_handlers(self):
        """Register all command handlers"""
        # Simple commands (direct message field)
        self._handlers['stop'] = StopCommandHandler()
        self._handlers['start'] = StartCommandHandler()
        self._handlers['restart'] = RestartCommandHandler()

        # Type-based commands
        self._handlers['characterSetup'] = CharacterSetupCommandHandler()
        self._handlers['deviceToken'] = DeviceTokenCommandHandler()
        self._handlers['getExtraCurriculars'] = GetExtraCurricularsCommandHandler()
        self._handlers['applyForExtracurricular'] = ApplyForExtracurricularCommandHandler()
        self._handlers['quitExtracurricular'] = QuitExtracurricularCommandHandler()
        self._handlers['retrievePerson'] = RetrievePersonCommandHandler()
        self._handlers['conversation'] = ConversationCommandHandler()
        self._handlers['markConversationAsRead'] = MarkConversationAsReadCommandHandler()
        self._handlers['speed'] = SpeedCommandHandler()
        self._handlers['focusUpdate'] = FocusUpdateCommandHandler()
        self._handlers['quitHabit'] = QuitHabitCommandHandler()
        self._handlers['stopQuitHabit'] = StopQuitHabitCommandHandler()
        self._handlers['purchaseItem'] = PurchaseItemCommandHandler()
        self._handlers['purchaseInAppItem'] = PurchaseInAppItemCommandHandler()
        self._handlers['purchaseEnergyRefill'] = PurchaseEnergyRefillCommandHandler()
        self._handlers['purchaseTimeSkip'] = PurchaseTimeSkipCommandHandler()
        self._handlers['getEnergyRefillTiers'] = GetEnergyRefillTiersCommandHandler()
        self._handlers['getTimeSkipTiers'] = GetTimeSkipTiersCommandHandler()
        self._handlers['getAchievements'] = GetAchievementsCommandHandler()
        self._handlers['acknowledgeAchievement'] = AcknowledgeAchievementCommandHandler()
        self._handlers['getDailyRewards'] = GetDailyRewardsCommandHandler()
        self._handlers['claimDailyReward'] = ClaimDailyRewardCommandHandler()
        self._handlers['getDailyQuests'] = GetDailyQuestsCommandHandler()
        self._handlers['claimQuestReward'] = ClaimQuestRewardCommandHandler()
        self._handlers['getPlayerStatistics'] = GetPlayerStatisticsCommandHandler()
        self._handlers['exportData'] = ExportDataCommandHandler()
        self._handlers['deleteAccount'] = DeleteAccountCommandHandler()
        self._handlers['applyForJob'] = ApplyForJobCommandHandler()
        self._handlers['quitJob'] = QuitJobCommandHandler()
        self._handlers['romance'] = RomanceCommandHandler()
        self._handlers['dateNight'] = DateNightCommandHandler()
        self._handlers['breakUp'] = BreakUpCommandHandler()
        self._handlers['divorce'] = BreakUpCommandHandler()  # Same handler
        self._handlers['partnerGift'] = PartnerGiftCommandHandler()
        self._handlers['resetSpeed'] = ResetSpeedCommandHandler()
        self._handlers['swipeMatch'] = SwipeMatchCommandHandler()
        self._handlers['getSwipeCharacter'] = GetSwipeCharacterCommandHandler()

    async def dispatch(self, event: Dict[str, Any], player, websocket):
        """
        Dispatch command to appropriate handler.

        Returns:
            Tuple[bool, dict]: (success, updateObject)
        """
        # Check for simple 'message' field commands first
        if event.get('message') in self._handlers:
            command_key = event.get('message')
            handler = self._handlers[command_key]
            await handler.handle(player, event, websocket)
            return True

        # Check for 'type' field commands
        if event.get('type') in self._handlers:
            command_key = event.get('type')
            handler = self._handlers[command_key]
            await handler.handle(player, event, websocket)
            return True

        # Fallback to event handler system for unknown types (lines 1070-1110)
        return await self._handle_event_dispatch(event, player, websocket)

    async def _handle_event_dispatch(self, event, player, websocket):
        """
        Handle event-based dispatch (fallback for custom events).
        This is the else clause from lines 1069-1121
        """
        from event_handlers import call_event_handler, InvalidEventError

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

        # Store original values 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.append(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 server.websocket_handlers 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}")
            import logging
            logging.getLogger(__name__).error(f"Handler crashed: {event['type']}", exc_info=True)
            from server.websocket_handlers import error
            await error(websocket, "An error occurred processing your request")

        return True

# Global dispatcher instance
_dispatcher = CommandDispatcher()

async def dispatch_command(event: Dict[str, Any], player, websocket) -> bool:
    """
    Dispatch a command to the appropriate handler.

    This replaces the massive if/elif chain in consumer().
    """
    return await _dispatcher.dispatch(event, player, websocket)
```

**What stays in app.py (simplified consumer):**
```python
from server.command_dispatcher import dispatch_command

async def consumer(message, websocket):
    """
    Handle incoming WebSocket messages.
    Simplified from 476 lines to ~50 lines.
    """
    player = playerRecords.get(websocket.userID)

    # Create update object (lines 648-666)
    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

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

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

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

    # Handle message queue (lines 1111-1117)
    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 = ""

    # Send updates (lines 1118-1120)
    updateObject = handleUpdates(updateObject, player, websocket)
    if updateObject and updateObject != {}:
        await sendDict(websocket, updateObject)

    return True
```

**Estimated reduction: 476 lines → ~60 lines**

**Benefits:**
- **Maintainability**: Each command in its own class
- **Testability**: Easy to unit test each handler
- **Extensibility**: Add new commands by adding new handler classes
- **Security**: Centralized validation and error handling
- **Performance**: Dictionary lookup O(1) vs linear if/elif chain

---

### Module 7: `server/websocket_handlers.py`
**Lines: ~200** | **Priority: HIGH** | **Risk: LOW**

Extract WebSocket connection lifecycle handlers.

```python
# server/websocket_handlers.py
"""
WebSocket connection lifecycle handlers.
Manages connection, disconnection, and background tasks.
"""

async def start(websocket):
    """
    Initialize connection and start game loops.
    Lines 1123-1168
    """
    pass

async def shutdown(websocket):
    """
    Cleanup on disconnect.
    Lines 1170-1181
    """
    pass

async def error(websocket, message):
    """
    Send error message to client.
    Lines 1183-1188
    """
    pass

async def every_minute():
    """
    Background task for offline game iteration.
    Lines 1189-1201
    """
    pass

async def handler(websocket):
    """
    Main WebSocket connection handler.
    Lines 1203-1230
    """
    pass

async def initialize_dummy_user():
    """
    Initialize test connection.
    Lines 1232-1244
    """
    pass
```

**What stays in app.py (main function):**
```python
from server.websocket_handlers import handler, every_minute, initialize_dummy_user

async def main():
    """
    Main entry point.
    Lines 1246-1289
    """
    loop = asyncio.get_running_loop()
    stop = loop.create_future()

    loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)

    # Initialize async database pool
    from database_async import initialize_pool, close_pool
    from config import config
    await initialize_pool(pool_size=config.MAX_CONNECTIONS)
    print(f"Database connection pool initialized: size={config.MAX_CONNECTIONS}")

    # Initialize retention systems
    print("Initializing retention systems...")
    from retention.daily_quests import initialize_quest_templates
    from retention.daily_rewards import initialize_daily_rewards
    initialize_quest_templates()
    initialize_daily_rewards()
    print("Retention systems initialized")

    port = int(os.environ.get("PORT", "8001"))
    print('running...')
    server = websockets.serve(handler, "", port)

    # Initialize tasks
    tasks = asyncio.gather(
        server,
        every_minute()
    )

    await asyncio.sleep(1)
    await initialize_dummy_user()
    await stop

    # Cleanup
    await close_pool()
    print("Database pool closed")

    tasks.cancel()
    await asyncio.gather(tasks, return_exceptions=True)

if __name__ == "__main__":
    asyncio.run(main())
```

**Estimated reduction: 170 lines → ~50 lines** (main stays, handlers extracted)

---

## Final app.py Structure

After refactoring, the new app.py will be approximately **350-450 lines**:

```python
#!/usr/bin/env python
"""
BaoLife WebSocket Server - Main Entry Point

Refactored structure:
- server/event_registration.py - Event registration (~250 lines)
- server/websocket_registry.py - Connection registry (~120 lines)
- server/websocket_messaging.py - Messaging utilities (~100 lines)
- server/websocket_handlers.py - Connection lifecycle (~200 lines)
- server/command_dispatcher.py - Command routing (~500 lines)
- game_loop/loop_manager.py - Loop helpers (~250 lines)
- game_loop/producer_consumer.py - Producer/consumer (~80 lines)

Total extracted: ~1,500 lines across modules
Remaining in app.py: ~350-450 lines
"""

# ============================================================
# Section 1: Imports (~40 lines)
# ============================================================
import asyncio
import datetime
import time
import json
import os
import random
import signal
import logging
import websockets

# Import all extracted modules
from server.event_registration import initialize_all_events
from server.websocket_registry import get_user_registry, get_server_state, BatchedUpdate
from server.websocket_messaging import (
    sendToUser, sendEventMessage, sendUserInfo, sendDict,
    iterateGames
)
from server.command_dispatcher import dispatch_command
from game_loop.producer_consumer import producer_handler, consumer_handler

# Import existing modules
from functions import *
from events import *
from intradayActivity import *
from player_cache import PlayerCache
from config import config

logger = logging.getLogger(__name__)

# ============================================================
# Section 2: Initialization (~10 lines)
# ============================================================

# Register all events and handlers
initialize_all_events()
print('startup')

# Initialize registries
USERS = get_user_registry()
server = get_server_state()
playerRecords = PlayerCache(max_size=config.MAX_CONNECTIONS)
lastIteration = False

# ============================================================
# Section 3: Game Loop (~150 lines) - PERFORMANCE CRITICAL
# ============================================================

async def initLifeSim(websocket, oneTimePlayer=False):
    """
    Main game loop - runs every tick.

    PERFORMANCE CRITICAL: This is the hot path.
    Kept in app.py for minimal call overhead.
    """
    # Game loop logic stays here but uses helper functions
    # from game_loop.loop_manager where appropriate
    # Lines 387-577 with optimizations
    pass

# ============================================================
# Section 4: Consumer (~60 lines) - SIMPLIFIED
# ============================================================

async def consumer(message, websocket):
    """
    Handle incoming WebSocket messages.
    Simplified from 476 lines - routing delegated to command_dispatcher.
    """
    # Lines 646-1121 simplified to ~60 lines
    # Most logic moved to server.command_dispatcher
    pass

# ============================================================
# Section 5: Main Entry Point (~50 lines)
# ============================================================

async def main():
    """Main entry point"""
    # Lines 1246-1289
    # Connection lifecycle imported from server.websocket_handlers
    pass

if __name__ == "__main__":
    asyncio.run(main())
```

---

## Implementation Strategy

### Phase 1: Low-Risk Extractions (Week 1)
**Target: Reduce to ~950 lines**

1. ✅ Extract event registration → `server/event_registration.py` (~230 lines)
2. ✅ Extract helper classes → `server/websocket_registry.py` (~78 lines)
3. ✅ Extract WebSocket utilities → `server/websocket_messaging.py` (~40 lines)

**After Phase 1:** app.py = 1,291 - 348 = ~943 lines

### Phase 2: Medium-Risk Extractions (Week 2)
**Target: Reduce to ~650 lines**

4. ✅ Extract producer/consumer → `game_loop/producer_consumer.py` (~68 lines)
5. ✅ Extract WebSocket handlers → `server/websocket_handlers.py` (~170 lines)
6. ⚠️ Optimize game loop with helpers → `game_loop/loop_manager.py` (~40 lines saved)

**After Phase 2:** app.py = 943 - 278 = ~665 lines

### Phase 3: High-Value Extraction (Week 3)
**Target: Reduce to ~350-450 lines**

7. 🔥 **CRITICAL** Extract command dispatcher → `server/command_dispatcher.py` (~416 lines)

**After Phase 3:** app.py = 665 - 416 = ~249 lines + ~100 lines overhead = **~350-450 lines**

---

## Performance Considerations

### Critical Paths to Preserve

1. **initLifeSim() - THE GAME LOOP**
   - Runs every game tick (potentially 5000 times/second per player)
   - **Keep in app.py** - do not move to separate module
   - Only extract non-critical logic to helpers
   - Inline hot code paths
   - Minimize function calls in tight loops

2. **Message Serialization**
   - JSON serialization happens on every state update
   - Keep `sendToUser`, `sendEventMessage` optimized
   - Consider caching compiled JSON encoders

3. **Player Registry Lookups**
   - O(1) lookup already implemented in UserRegistry
   - Keep USERS and playerRecords in module scope
   - Don't add indirection layers

### Performance Testing Required

After each phase, run performance benchmarks:

```bash
# Load test with 100 concurrent players
python ws/tests/load_test.py --players 100 --duration 60

# Profile game loop
python -m cProfile -o profile.stats ws/app.py

# Check memory usage
python ws/tests/memory_test.py
```

**Acceptance criteria:**
- No regression in FPS for 100 concurrent players
- Memory usage increase < 5%
- Message latency < 50ms at p95

---

## Code Quality Improvements

### Benefits of Refactoring

1. **Maintainability**
   - Each module has single responsibility
   - Easier to locate code for specific features
   - Reduced cognitive load (files < 500 lines)

2. **Testability**
   - Command handlers can be unit tested in isolation
   - Mock websockets for testing
   - Easier to test edge cases

3. **Extensibility**
   - Add new commands without touching 500-line if/elif chain
   - Command handlers follow Open/Closed Principle
   - Easy to add middleware (logging, validation, etc.)

4. **Security**
   - Centralized input validation in CommandDispatcher
   - Consistent error handling
   - Easier to audit command permissions

5. **Documentation**
   - Each module is self-documenting
   - Clear separation of concerns
   - Easier onboarding for new developers

---

## Testing Strategy

### Unit Tests

```python
# tests/unit/test_command_dispatcher.py
def test_start_command():
    """Test start command handler"""
    pass

def test_stop_command():
    """Test stop command handler"""
    pass

def test_speed_command_validation():
    """Test speed command validates input"""
    pass

def test_invalid_command_rejection():
    """Test invalid commands are rejected"""
    pass
```

### Integration Tests

```python
# tests/integration/test_websocket_flow.py
async def test_full_game_loop():
    """Test complete WebSocket connection lifecycle"""
    pass

async def test_command_routing():
    """Test commands route to correct handlers"""
    pass
```

### Load Tests

```bash
# Test with 100 concurrent players
locust -f tests/load/websocket_load.py --users 100 --spawn-rate 10
```

---

## Migration Checklist

### Before Starting
- [ ] Create feature branch: `refactor/app-modularization`
- [ ] Backup current app.py
- [ ] Run full test suite (baseline)
- [ ] Document current performance metrics

### Phase 1 Checklist
- [ ] Create `server/` directory
- [ ] Create `server/__init__.py`
- [ ] Extract `server/event_registration.py`
- [ ] Extract `server/websocket_registry.py`
- [ ] Extract `server/websocket_messaging.py`
- [ ] Update imports in app.py
- [ ] Run tests
- [ ] Run performance benchmarks
- [ ] Git commit with clear message

### Phase 2 Checklist
- [ ] Create `game_loop/` directory
- [ ] Create `game_loop/__init__.py`
- [ ] Extract `game_loop/producer_consumer.py`
- [ ] Extract `game_loop/loop_manager.py`
- [ ] Extract `server/websocket_handlers.py`
- [ ] Update imports in app.py
- [ ] Run tests
- [ ] Run performance benchmarks
- [ ] Git commit

### Phase 3 Checklist
- [ ] Design command handler interface
- [ ] Create `server/command_dispatcher.py`
- [ ] Implement CommandHandler base class
- [ ] Migrate simple commands first (start, stop, restart)
- [ ] Migrate medium commands (speed, focus, habits)
- [ ] Migrate complex commands (conversation, dating, jobs)
- [ ] Migrate monetization commands (purchases, rewards)
- [ ] Migrate data management commands (export, delete)
- [ ] Update consumer() in app.py
- [ ] Run tests
- [ ] Run performance benchmarks
- [ ] Git commit

### After Completion
- [ ] Update CLAUDE.md documentation
- [ ] Update inline code comments
- [ ] Add docstrings to all new modules
- [ ] Create architecture diagram
- [ ] Run full regression test suite
- [ ] Deploy to staging environment
- [ ] Monitor production metrics
- [ ] Create pull request with detailed description

---

## Risk Assessment

| Module | Risk Level | Mitigation |
|--------|-----------|------------|
| event_registration | LOW | Runs once at startup, easy to test |
| websocket_registry | LOW | Simple data structures, no complex logic |
| websocket_messaging | LOW | Stateless functions, easy to test |
| producer_consumer | MEDIUM | Test FPS control, rate limiting |
| websocket_handlers | LOW | Well-defined lifecycle, existing patterns |
| loop_manager | HIGH ⚠️ | Minimize changes, extensive testing |
| command_dispatcher | MEDIUM | Most code movement, needs careful testing |

---

## Success Metrics

### Code Metrics
- ✅ app.py reduced from 1,291 lines to < 500 lines
- ✅ Cyclomatic complexity reduced by 60%+
- ✅ 7 new focused modules created
- ✅ 100% backward compatibility

### Performance Metrics
- ✅ No FPS regression (maintain 5000 FPS target)
- ✅ Memory usage increase < 5%
- ✅ Message latency unchanged (< 50ms p95)
- ✅ Startup time increase < 100ms

### Quality Metrics
- ✅ Test coverage > 80% for new modules
- ✅ Zero security vulnerabilities introduced
- ✅ All type hints added
- ✅ Docstring coverage 100%

---

## Estimated Timeline

- **Phase 1 (Low Risk)**: 2-3 days
- **Phase 2 (Medium Risk)**: 3-4 days
- **Phase 3 (Command Dispatcher)**: 5-7 days
- **Testing & Documentation**: 2-3 days
- **Buffer**: 2-3 days

**Total: 14-20 days (2-3 weeks)**

---

## Conclusion

This refactoring will:
1. ✅ Reduce app.py from **1,291 lines to ~350-450 lines** (65% reduction)
2. ✅ Improve maintainability through focused modules
3. ✅ Enable easier testing and debugging
4. ✅ Maintain critical game loop performance
5. ✅ Follow the same pattern as functions.py refactoring
6. ✅ Make future feature additions easier

The largest win is the **command_dispatcher** module, which converts a 476-line if/elif chain into a clean, table-driven dispatch system. This single change reduces app.py by ~35% while improving code quality significantly.

**Next Steps:**
1. Review and approve this plan
2. Create feature branch
3. Begin Phase 1 implementation
4. Iterate with testing and benchmarks
