# BaoLife Phase 5: Polish & Stability

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Production-quality UI/UX with smooth interactions and bulletproof error handling.

**Architecture:** Backend error handling + transactions + performance. Frontend loading states + animations + haptics + accessibility.

**Tech Stack:**
- Backend: Python error handling, database transactions
- Frontend: SwiftUI animations, UIKit haptics, accessibility

**Total Time Estimate:** 35-40 hours (Week 8)

---

## Backend Components (../lichun)

### Component 42: Comprehensive Error Handling

**Files:**
- Create: `../lichun/errors/error_handler.py`
- Modify: All WebSocket message handlers

**Implementation:**

```python
# errors/error_handler.py
import logging
from functools import wraps

class GameError(Exception):
    """Base exception for game errors"""
    def __init__(self, code: str, message: str, retry_possible: bool = True):
        self.code = code
        self.message = message
        self.retry_possible = retry_possible
        super().__init__(message)

class InsufficientResourcesError(GameError):
    """Not enough diamonds, energy, or money"""
    def __init__(self, resource: str, required: int, available: int):
        super().__init__(
            code=f"INSUFFICIENT_{resource.upper()}",
            message=f"Not enough {resource}. Need {required}, have {available}",
            retry_possible=False
        )

class ServerError(GameError):
    """Internal server error"""
    def __init__(self, details: str):
        super().__init__(
            code="SERVER_ERROR",
            message="Something went wrong. Please try again.",
            retry_possible=True
        )

def handle_errors(func):
    """Decorator to wrap message handlers with error handling"""
    @wraps(func)
    def wrapper(player_id: int, *args, **kwargs):
        try:
            return func(player_id, *args, **kwargs)
        except GameError as e:
            logging.warning(f"Game error for player {player_id}: {e.code} - {e.message}")
            send_error_to_client(player_id, e.code, e.message, e.retry_possible)
            return None
        except Exception as e:
            logging.error(f"Unexpected error for player {player_id}: {e}", exc_info=True)
            send_error_to_client(player_id, "SERVER_ERROR", "Something went wrong", True)
            return None
    return wrapper

def send_error_to_client(player_id: int, code: str, message: str, retry_possible: bool):
    """Send structured error to client"""
    send_to_client(player_id, {
        'type': 'error',
        'error_code': code,
        'message': message,
        'retry_possible': retry_possible
    })

# Example usage
@handle_errors
def handle_purchase_energy_refill(player_id: int, message_data: dict):
    """Handle energy refill with error handling"""
    refill_type = message_data.get('refillType')
    
    # Get player diamonds
    player = get_player(player_id)
    cost = REFILL_COSTS[refill_type]
    
    if player['diamonds'] < cost:
        raise InsufficientResourcesError('diamonds', cost, player['diamonds'])
    
    # Proceed with purchase
    # ...
```

**Commit:**
```bash
git add errors/error_handler.py
git commit -m "feat(errors): add comprehensive error handling

- Custom exception hierarchy
- Error decorator for all handlers
- Structured error messages to client
- Retry flags for client logic

🤖 Generated with Claude Code"
```

**Estimated Time:** 4-5 hours

---

### Component 43: Database Transaction Safety

**Files:**
- Create: `../lichun/database/transactions.py`
- Modify: `../lichun/database.py`

**Implementation:**

```python
from contextlib import contextmanager

@contextmanager
def transaction(connection=None):
    """Context manager for database transactions"""
    conn = connection or db.get_connection()
    cursor = conn.cursor()
    
    try:
        yield cursor
        conn.commit()
    except Exception as e:
        conn.rollback()
        logging.error(f"Transaction rolled back: {e}")
        raise
    finally:
        cursor.close()
        if not connection:  # Only close if we created it
            conn.close()

# Example usage
def purchase_energy_refill(player_id: int, refill_type: str):
    """Purchase with atomic transaction"""
    with transaction() as cursor:
        # 1. Check balance
        cursor.execute("SELECT diamonds FROM players WHERE id = %s FOR UPDATE", (player_id,))
        player = cursor.fetchone()
        
        if player['diamonds'] < cost:
            raise InsufficientResourcesError('diamonds', cost, player['diamonds'])
        
        # 2. Deduct diamonds
        cursor.execute(
            "UPDATE players SET diamonds = diamonds - %s WHERE id = %s",
            (cost, player_id)
        )
        
        # 3. Add energy
        cursor.execute(
            "UPDATE players SET energy = LEAST(energy + %s, max_energy) WHERE id = %s",
            (energy_amount, player_id)
        )
        
        # 4. Log transaction
        cursor.execute(
            "INSERT INTO energy_refill_purchases (player_id, refill_type, diamond_cost) VALUES (%s, %s, %s)",
            (player_id, refill_type, cost)
        )
    
    # All succeed or all rollback
```

**Commit:**
```bash
git add database/transactions.py
git commit -m "feat(database): add transaction safety

- Context manager for atomic operations
- Automatic rollback on errors
- Row-level locking for concurrent updates

🤖 Generated with Claude Code"
```

**Estimated Time:** 3-4 hours

---

### Component 44-45: Performance Optimization & Graceful Degradation

**Files:**
- Modify: `../lichun/server.py` (performance)
- Create: `../lichun/offline/queue.py` (graceful degradation)

**Key Optimizations:**

```python
# Query caching
from functools import lru_cache

@lru_cache(maxsize=1000)
def get_player_cached(player_id: int):
    """Cache frequently accessed player data"""
    return db.execute_query("SELECT * FROM players WHERE id = %s", (player_id,))[0]

# Batch message sending
pending_messages = defaultdict(list)

def queue_message(player_id: int, message: dict):
    """Queue messages for batch sending"""
    pending_messages[player_id].append(message)

def flush_messages():
    """Send all queued messages in batch"""
    for player_id, messages in pending_messages.items():
        send_to_client(player_id, {'type': 'batch', 'messages': messages})
    pending_messages.clear()

# Offline mode
def enable_offline_mode(player_id: int):
    """Continue simulation offline, queue updates"""
    db.execute_query(
        "UPDATE players SET offline_mode = TRUE WHERE id = %s",
        (player_id,),
        fetch=False
    )
    # Continue game loop, store updates in offline_events table
```

**Commit:**
```bash
git add server.py offline/queue.py
git commit -m "feat(performance): optimize queries and add offline mode

- LRU caching for player data
- Batch message sending
- Offline simulation with catch-up
- Query optimization with indexes

🤖 Generated with Claude Code"
```

**Estimated Time:** 8-9 hours combined

---

## Frontend Components (lichunWebsocket)

### Component 46-47: Loading States & Error Recovery

**Files:**
- Create: `LoadingIndicators.swift`
- Create: `ErrorRecoveryView.swift`

**Implementation:**

```swift
// LoadingIndicators.swift
struct LoadingSpinner: View {
    var body: some View {
        ProgressView()
            .scaleEffect(1.5)
            .tint(BaoColors.primary)
    }
}

struct SkeletonView: View {
    @State private var isAnimating = false
    
    var body: some View {
        Rectangle()
            .fill(Color.gray.opacity(0.3))
            .overlay(
                Rectangle()
                    .fill(
                        LinearGradient(
                            colors: [.clear, .white.opacity(0.4), .clear],
                            startPoint: .leading,
                            endPoint: .trailing
                        )
                    )
                    .offset(x: isAnimating ? 400 : -400)
            )
            .cornerRadius(8)
            .onAppear {
                withAnimation(.linear(duration: 1.5).repeatForever(autoreverses: false)) {
                    isAnimating = true
                }
            }
    }
}

// ErrorRecoveryView.swift
struct ErrorRecoveryView: View {
    let error: WebSocketService.WebSocketError
    let onRetry: () -> Void
    let onDismiss: () -> Void
    
    var body: some View {
        VStack(spacing: BaoSpacing.lg) {
            Image(systemName: "exclamationmark.triangle.fill")
                .font(.system(size: 60))
                .foregroundColor(.orange)
            
            Text("Oops!")
                .font(BaoTypography.heading1)
            
            Text(error.userMessage)
                .font(BaoTypography.body)
                .multilineTextAlignment(.center)
            
            HStack(spacing: BaoSpacing.md) {
                Button("Dismiss") {
                    onDismiss()
                }
                .foregroundColor(.secondary)
                
                Button("Try Again") {
                    onRetry()
                }
                .baoPrimaryButton()
            }
        }
        .padding(BaoSpacing.xl)
        .background(BaoColors.cardBackground)
        .cornerRadius(BaoRadius.lg)
        .shadow(radius: 20)
    }
}
```

**Estimated Time:** 4-5 hours

---

### Component 48-49: Animations & Haptics

**Files:**
- Create: `Animations.swift`
- Create: `HapticManager.swift`

**Implementation:**

```swift
// Animations.swift
extension View {
    func scaleOnTap() -> some View {
        self.buttonStyle(ScaleButtonStyle())
    }
}

struct ScaleButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
            .animation(.spring(response: 0.3, dampingFraction: 0.6), value: configuration.isPressed)
    }
}

struct CountUpAnimation: Animatable View {
    var value: Double
    
    var animatableData: Double {
        get { value }
        set { value = newValue }
    }
    
    var body: some View {
        Text("\(Int(value))")
            .font(BaoTypography.heading2)
    }
}

// HapticManager.swift
import UIKit

class HapticManager {
    static let shared = HapticManager()
    
    private let successFeedback = UINotificationFeedbackGenerator()
    private let warningFeedback = UINotificationFeedbackGenerator()
    private let selectionFeedback = UISelectionFeedbackGenerator()
    private let impactFeedback = UIImpactFeedbackGenerator(style: .medium)
    
    func success() {
        successFeedback.notificationOccurred(.success)
    }
    
    func warning() {
        warningFeedback.notificationOccurred(.warning)
    }
    
    func selection() {
        selectionFeedback.selectionChanged()
    }
    
    func impact() {
        impactFeedback.impactOccurred()
    }
}

// Usage in views
Button("Purchase") {
    HapticManager.shared.success()
    makePurchase()
}
```

**Estimated Time:** 4-5 hours

---

### Component 50-53: Toasts, Confirmations, Sounds, Accessibility

**Summary Implementation:**

**Component 50: Toast System** (2h)
- Success, error, info, warning toasts
- Auto-dismiss queue
- Position at top

**Component 51: Confirmation Dialogs** (2h)
- Destructive action confirmations
- SwiftUI `.confirmationDialog()`

**Component 52: Sound Effects** (2-3h)
- Purchase, achievement, notification sounds
- Respect silent mode
- Settings toggle

**Component 53: Accessibility** (3-4h)
- VoiceOver labels
- Dynamic Type support
- Color contrast
- Reduce motion option

**Combined Time:** 9-11 hours

---

## Phase 5 Summary

**Total Time:** 35-40 hours (Week 8)

**Backend (15-18h):**
- ✅ Component 42: Error handling (4-5h)
- ✅ Component 43: Transactions (3-4h)
- ✅ Component 44-45: Performance & offline (8-9h)

**Frontend (20-22h):**
- ✅ Component 46-47: Loading & errors (4-5h)
- ✅ Component 48-49: Animations & haptics (4-5h)
- ✅ Component 50-53: Toasts, sounds, a11y (9-11h)

**What's Working:**
- Bulletproof error handling
- Atomic transactions
- Smooth animations
- Haptic feedback
- Accessibility support

**Next Phase:** Analytics & Testing (Week 9)

