# Error Handling Guide

This guide outlines the standardized error handling patterns used throughout ThrillWiki to ensure consistent, debuggable, and user-friendly error management.

## Core Principles

1. **All errors must be logged** - Never silently swallow errors
2. **Provide context** - Include relevant metadata for debugging
3. **User-friendly messages** - Show clear, actionable error messages to users
4. **Preserve error chains** - Don't lose original error information
5. **Use structured logging** - Avoid raw `console.*` statements

## When to Use What

### `handleError()` - Application Errors (User-Facing)

Use `handleError()` for errors that affect user operations and should be visible in the Admin Panel.

**When to use:**
- Database operation failures
- API call failures
- Form submission errors
- Authentication/authorization failures
- Any error that impacts user workflows

**Example:**
```typescript
import { handleError } from '@/lib/errorHandler';
import { useAuth } from '@/hooks/useAuth';

try {
  await supabase.from('parks').insert(parkData);
  handleSuccess('Park Created', 'Your park has been added successfully');
} catch (error) {
  handleError(error, {
    action: 'Create Park',
    userId: user?.id,
    metadata: { parkName: parkData.name }
  });
  throw error; // Re-throw for parent error boundaries
}
```

**Key features:**
- Logs to `request_metadata` table with full context
- Shows user-friendly toast with error reference ID
- Captures breadcrumbs (last 10 user actions)
- Visible in Admin Panel at `/admin/error-monitoring`

### `logger.*` - Development & Debugging Logs

Use `logger.*` for information that helps developers debug issues without sending data to the database.

**When to use:**
- Development debugging information
- Performance monitoring
- Expected failures that don't need Admin Panel visibility
- Component lifecycle events
- Non-critical informational messages

**Available methods:**
```typescript
import { logger } from '@/lib/logger';

// Development only - not logged in production
logger.log('Component mounted', { props });
logger.info('User action completed', { action: 'click' });
logger.warn('Deprecated API used', { api: 'oldMethod' });
logger.debug('State updated', { newState });

// Always logged - even in production
logger.error('Critical failure', { context });

// Specialized logging
logger.performance('ComponentName', durationMs);
logger.moderationAction('approve', itemId, durationMs);
```

**Example - Expected periodic failures:**
```typescript
// Don't show toast or log to Admin Panel for expected periodic failures
try {
  await supabase.rpc('release_expired_locks');
} catch (error) {
  logger.debug('Periodic lock release failed', { 
    operation: 'release_expired_locks',
    error: getErrorMessage(error)
  });
}
```

### `toast.*` - User Notifications

Use toast notifications directly for informational messages, warnings, or confirmations.

**When to use:**
- Success confirmations (use `handleSuccess()` helper)
- Informational messages
- Non-error warnings
- User confirmations

**Example:**
```typescript
import { handleSuccess, handleInfo } from '@/lib/errorHandler';

// Success messages
handleSuccess('Changes Saved', 'Your profile has been updated');

// Informational messages
handleInfo('Processing', 'Your request is being processed');

// Custom toast for special cases
toast.info('Feature Coming Soon', {
  description: 'This feature will be available next month',
  duration: 4000
});
```

### ❌ `console.*` - NEVER USE DIRECTLY

**DO NOT USE** `console.*` statements in application code. They are blocked by ESLint.

```typescript
// ❌ WRONG - Will fail ESLint check
console.log('User clicked button');
console.error('Database error:', error);

// ✅ CORRECT - Use logger or handleError
logger.log('User clicked button');
handleError(error, { action: 'Database Operation', userId });
```

**The only exceptions:**
- Inside `src/lib/logger.ts` itself
- Edge function logging (use `edgeLogger.*`)
- Test files (*.test.ts, *.test.tsx)

## Error Handling Patterns

### Pattern 1: Component/Hook Errors (Most Common)

For errors in components or custom hooks that affect user operations:

```typescript
import { handleError } from '@/lib/errorHandler';
import { useAuth } from '@/hooks/useAuth';

const MyComponent = () => {
  const { user } = useAuth();

  const handleSubmit = async (data: FormData) => {
    try {
      await saveData(data);
      handleSuccess('Saved', 'Your changes have been saved');
    } catch (error) {
      handleError(error, {
        action: 'Save Form Data',
        userId: user?.id,
        metadata: { formType: 'parkEdit' }
      });
      throw error; // Re-throw for error boundaries
    }
  };
};
```

**Key points:**
- Always include descriptive action name
- Include userId when available
- Add relevant metadata for debugging
- Re-throw after handling to let error boundaries catch it

### Pattern 2: TanStack Query Errors

For errors within React Query hooks:

```typescript
import { useQuery } from '@tanstack/react-query';
import { handleError } from '@/lib/errorHandler';

const { data, error, isLoading } = useQuery({
  queryKey: ['parks', parkId],
  queryFn: async () => {
    const { data, error } = await supabase
      .from('parks')
      .select('*')
      .eq('id', parkId)
      .single();
    
    if (error) {
      handleError(error, {
        action: 'Fetch Park Details',
        userId: user?.id,
        metadata: { parkId }
      });
      throw error;
    }
    
    return data;
  }
});

// Handle error state in UI
if (error) {
  return <ErrorState message="Failed to load park" />;
}
```

### Pattern 3: Expected/Recoverable Errors

For operations that may fail expectedly and should be logged but not shown to users:

```typescript
import { logger } from '@/lib/logger';
import { getErrorMessage } from '@/lib/errorHandler';

// Background operation that may fail without impacting user
const syncCache = async () => {
  try {
    await performCacheSync();
  } catch (error) {
    // Log for debugging without user notification
    logger.warn('Cache sync failed', { 
      operation: 'syncCache',
      error: getErrorMessage(error)
    });
    // Continue execution - cache sync is non-critical
  }
};
```

### Pattern 4: Error Boundaries (Top-Level)

React Error Boundaries catch unhandled component errors:

```typescript
import { Component, ReactNode } from 'react';
import { handleError } from '@/lib/errorHandler';

class ErrorBoundary extends Component<
  { children: ReactNode },
  { hasError: boolean }
> {
  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    handleError(error, {
      action: 'Component Error Boundary',
      metadata: { 
        componentStack: errorInfo.componentStack 
      }
    });
  }

  render() {
    if (this.state.hasError) {
      return <ErrorFallback />;
    }
    return this.props.children;
  }
}
```

### Pattern 5: Preserve Error Context in Chains

When catching and re-throwing errors, preserve the original error information:

```typescript
// ❌ WRONG - Loses original error
try {
  await operation();
} catch (error) {
  throw new Error('Operation failed'); // Original error lost!
}

// ❌ WRONG - Silent catch loses context
const data = await fetch(url)
  .then(res => res.json())
  .catch(() => ({ message: 'Failed' })); // Error details lost!

// ✅ CORRECT - Preserve and log error
try {
  const response = await fetch(url);
  if (!response.ok) {
    const errorData = await response.json().catch((parseError) => {
      logger.warn('Failed to parse error response', { 
        error: getErrorMessage(parseError),
        status: response.status
      });
      return { message: 'Request failed' };
    });
    throw new Error(errorData.message);
  }
  return await response.json();
} catch (error) {
  handleError(error, {
    action: 'Fetch Data',
    userId: user?.id,
    metadata: { url }
  });
  throw error;
}
```

## Automatic Breadcrumb Tracking

The application automatically tracks breadcrumbs (last 10 user actions) to provide context for errors.

### Automatic Tracking (No Code Needed)

1. **API Calls** - All Supabase operations are tracked automatically via the wrapped client
2. **Navigation** - Route changes are tracked automatically
3. **Mutation Errors** - TanStack Query mutations log failures automatically

### Manual Breadcrumb Tracking

Add breadcrumbs for important user actions:

```typescript
import { breadcrumb } from '@/lib/errorBreadcrumbs';

// Navigation breadcrumb (usually automatic)
breadcrumb.navigation('/parks/123', '/parks');

// User action breadcrumb
breadcrumb.userAction('clicked submit', 'ParkEditForm', { 
  parkId: '123' 
});

// API call breadcrumb (usually automatic via wrapped client)
breadcrumb.apiCall('/api/parks', 'POST', 200);

// State change breadcrumb
breadcrumb.stateChange('filter changed', { 
  filter: 'status=open' 
});
```

**When to add manual breadcrumbs:**
- Critical user actions (form submissions, deletions)
- Important state changes (filter updates, mode switches)
- Non-Supabase API calls
- Complex user workflows

**When NOT to add breadcrumbs:**
- Inside loops or frequently called functions
- For every render or effect
- For trivial state changes
- Inside already tracked operations

## Edge Function Error Handling

Edge functions use a separate logger to prevent sensitive data exposure:

```typescript
import { edgeLogger, startRequest, endRequest } from '../_shared/logger.ts';

Deno.serve(async (req) => {
  const tracking = startRequest();
  
  try {
    // Your edge function logic
    const result = await performOperation();
    
    const duration = endRequest(tracking);
    edgeLogger.info('Operation completed', {
      requestId: tracking.requestId,
      duration
    });
    
    return new Response(JSON.stringify(result), {
      headers: { 'Content-Type': 'application/json' }
    });
  } catch (error) {
    const duration = endRequest(tracking);
    
    edgeLogger.error('Operation failed', {
      requestId: tracking.requestId,
      error: error.message,
      duration
    });
    
    return new Response(
      JSON.stringify({ 
        error: 'Operation failed',
        requestId: tracking.requestId 
      }),
      { status: 500, headers: { 'Content-Type': 'application/json' } }
    );
  }
});
```

**Key features:**
- Automatic sanitization of sensitive fields
- Request correlation IDs
- Structured JSON logging
- Duration tracking

## Testing Error Handling

### Manual Testing

1. Visit `/test-error-logging` (dev only)
2. Click "Generate Test Error"
3. Check Admin Panel at `/admin/error-monitoring`
4. Verify error appears with:
   - Full stack trace
   - Breadcrumbs (including API calls)
   - Environment context
   - User information

### Automated Testing

```typescript
import { handleError } from '@/lib/errorHandler';

describe('Error Handling', () => {
  it('should log errors to database', async () => {
    const mockError = new Error('Test error');
    
    handleError(mockError, {
      action: 'Test Action',
      metadata: { test: true }
    });
    
    // Verify error logged to request_metadata table
    const { data } = await supabase
      .from('request_metadata')
      .select('*')
      .eq('error_message', 'Test error')
      .single();
    
    expect(data).toBeDefined();
    expect(data.endpoint).toBe('Test Action');
  });
});
```

## Common Mistakes to Avoid

### ❌ Mistake 1: Silent Error Catching
```typescript
// ❌ WRONG
try {
  await operation();
} catch (error) {
  // Nothing - error disappears!
}

// ✅ CORRECT
try {
  await operation();
} catch (error) {
  logger.debug('Expected operation failure', { 
    operation: 'name',
    error: getErrorMessage(error)
  });
}
```

### ❌ Mistake 2: Using console.* Directly
```typescript
// ❌ WRONG - Blocked by ESLint
console.log('Debug info', data);
console.error('Error occurred', error);

// ✅ CORRECT
logger.log('Debug info', data);
handleError(error, { action: 'Operation Name', userId });
```

### ❌ Mistake 3: Not Re-throwing After Handling
```typescript
// ❌ WRONG - Error doesn't reach error boundary
try {
  await operation();
} catch (error) {
  handleError(error, { action: 'Operation' });
  // Error stops here - error boundary never sees it
}

// ✅ CORRECT
try {
  await operation();
} catch (error) {
  handleError(error, { action: 'Operation' });
  throw error; // Let error boundary handle UI fallback
}
```

### ❌ Mistake 4: Generic Error Messages
```typescript
// ❌ WRONG - No context
handleError(error, { action: 'Error' });

// ✅ CORRECT - Descriptive context
handleError(error, {
  action: 'Update Park Opening Hours',
  userId: user?.id,
  metadata: { 
    parkId: park.id,
    parkName: park.name
  }
});
```

### ❌ Mistake 5: Losing Error Context
```typescript
// ❌ WRONG
.catch(() => ({ error: 'Failed' }))

// ✅ CORRECT
.catch((error) => {
  logger.warn('Operation failed', { error: getErrorMessage(error) });
  return { error: 'Failed' };
})
```

## Error Monitoring Dashboard

Access the error monitoring dashboard at `/admin/error-monitoring`:

**Features:**
- Real-time error list with filtering
- Search by error ID, message, or user
- Full stack traces
- Breadcrumb trails showing user actions before error
- Environment context (browser, device, network)
- Request metadata (endpoint, method, status)

**Error ID Lookup:**
Visit `/admin/error-lookup` to search for specific errors by their 8-character reference ID shown to users.

## Related Files

**Core Error Handling:**
- `src/lib/errorHandler.ts` - Main error handling utilities
- `src/lib/errorBreadcrumbs.ts` - Breadcrumb tracking system
- `src/lib/environmentContext.ts` - Environment data capture
- `src/lib/logger.ts` - Structured logging utility
- `src/lib/supabaseClient.ts` - Wrapped client with auto-tracking

**Admin Tools:**
- `src/pages/admin/ErrorMonitoring.tsx` - Error dashboard
- `src/pages/admin/ErrorLookup.tsx` - Error ID search
- `src/components/admin/ErrorDetailsModal.tsx` - Error details view

**Edge Functions:**
- `supabase/functions/_shared/logger.ts` - Edge function logger

**Database:**
- `request_metadata` table - Stores all error logs
- `request_breadcrumbs` table - Stores breadcrumb trails
- `log_request_metadata` RPC - Logs errors from client

## Summary

**Golden Rules:**
1. ✅ Use `handleError()` for user-facing application errors
2. ✅ Use `logger.*` for development debugging and expected failures
3. ✅ Use `toast.*` for success/info notifications
4. ✅ Use `edgeLogger.*` in edge functions
5. ❌ NEVER use `console.*` directly in application code
6. ✅ Always preserve error context when catching
7. ✅ Re-throw errors after handling for error boundaries
8. ✅ Include descriptive action names and metadata
9. ✅ Manual breadcrumbs for critical user actions only
10. ✅ Test error handling in Admin Panel

**Quick Reference:**
```typescript
// Application error (user-facing)
handleError(error, { action: 'Action Name', userId, metadata });

// Debug log (development only)
logger.debug('Debug info', { context });

// Expected failure (log but don't show toast)
logger.warn('Expected failure', { error: getErrorMessage(error) });

// Success notification
handleSuccess('Title', 'Description');

// Edge function error
edgeLogger.error('Error message', { requestId, error: error.message });
```
