# Troubleshooting Guide

Common issues and their solutions for ThrillWiki.

---

## Authentication Issues

### Cannot Sign In

**Symptom:** Sign in button does nothing or shows error

**Possible Causes:**
1. Invalid credentials
2. Email not verified
3. Account banned/deactivated
4. Supabase connection error

**Solutions:**
```typescript
// Check browser console for errors
// Look for auth errors in console

// Verify Supabase connection
const { data, error } = await supabase.auth.getSession();
console.log('Session:', data, error);

// Check if email verified (if required)
SELECT email, email_confirmed_at FROM auth.users WHERE email = '[user-email]';
```

### MFA Not Working

**Symptom:** MFA code not accepted or not prompted

**Possible Causes:**
1. Time sync issue (TOTP codes are time-based)
2. Wrong factor ID
3. Challenge not created
4. AAL level not checked

**Solutions:**
```typescript
// Verify device time is correct (CRITICAL for TOTP)
// Check enrolled factors
const { data } = await supabase.auth.mfa.listFactors();
console.log('Enrolled factors:', data);

// Create challenge manually
const factor = data.totp[0];
const { data: challenge } = await supabase.auth.mfa.challenge({
  factorId: factor.id
});

// Verify code
const { error } = await supabase.auth.mfa.verify({
  factorId: factor.id,
  challengeId: challenge.id,
  code: '[6-digit-code]'
});
```

### Session Expired

**Symptom:** User logged out unexpectedly

**Possible Causes:**
1. Session timeout (default 1 hour)
2. Token refresh failed
3. Multiple tabs/windows
4. AAL2 expired (MFA users)

**Solutions:**
```typescript
// Check session status
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
  // Redirect to login
}

// Refresh session manually
const { data, error } = await supabase.auth.refreshSession();

// Monitor auth state changes
supabase.auth.onAuthStateChange((event, session) => {
  console.log('Auth event:', event, session);
});
```

---

## Submission Issues

### Submission Not Appearing in Queue

**Symptom:** Created submission doesn't show in moderation queue

**Possible Causes:**
1. Database write failed
2. Status filter hiding submission
3. RLS blocking view
4. Realtime subscription not connected

**Solutions:**
```sql
-- Check if submission created
SELECT * FROM content_submissions 
WHERE user_id = '[user-id]' 
ORDER BY created_at DESC LIMIT 5;

-- Check submission items
SELECT * FROM submission_items 
WHERE submission_id = '[submission-id]';

-- Check RLS policies
SET LOCAL ROLE authenticated;
SET LOCAL "request.jwt.claims" = '{"sub": "[moderator-user-id]"}';
SELECT * FROM content_submissions WHERE status = 'pending';
```

### Cannot Approve Submission

**Symptom:** Approve button disabled or approval fails

**Possible Causes:**
1. Lock expired
2. No active lock
3. Dependencies not approved
4. Edge function error

**Solutions:**
```typescript
// Check lock status
const { data: submission } = await supabase
  .from('content_submissions')
  .select('*, locked_until, assigned_to')
  .eq('id', submissionId)
  .single();

if (new Date(submission.locked_until) < new Date()) {
  console.error('Lock expired');
}

// Check dependencies
const { data: items } = await supabase
  .from('submission_items')
  .select('*, depends_on')
  .eq('submission_id', submissionId);

const blocked = items.filter(item => 
  item.depends_on && 
  items.find(dep => dep.id === item.depends_on && dep.status !== 'approved')
);
console.log('Blocked items:', blocked);
```

### Image Upload Fails

**Symptom:** Image upload error or stuck at uploading

**Possible Causes:**
1. File too large (>10MB)
2. Invalid file type
3. CloudFlare API error
4. Network error
5. CORS issue

**Solutions:**
```typescript
// Check file size and type
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];

if (file.size > MAX_FILE_SIZE) {
  throw new Error('File too large');
}
if (!ALLOWED_TYPES.includes(file.type)) {
  throw new Error('Invalid file type');
}

// Check CloudFlare credentials
console.log('CF Account Hash:', import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH);

// Test edge function
const { data, error } = await supabase.functions.invoke('upload-image', {
  body: { action: 'get-upload-url' }
});
console.log('Upload URL response:', data, error);

// Check network tab for CORS errors
```

---

## Moderation Queue Issues

### Lock Stuck

**Symptom:** Submission locked but moderator not actively reviewing

**Possible Causes:**
1. Moderator closed browser without releasing lock
2. Lock extension failed
3. Database lock not expired

**Solutions:**
```sql
-- Find stuck locks
SELECT 
  id,
  submission_type,
  assigned_to,
  assigned_at,
  locked_until,
  EXTRACT(EPOCH FROM (NOW() - locked_until))/60 AS minutes_over
FROM content_submissions
WHERE locked_until IS NOT NULL
  AND locked_until < NOW()
  AND status = 'pending';

-- Release stuck lock (as admin)
UPDATE content_submissions
SET assigned_to = NULL,
    assigned_at = NULL,
    locked_until = NULL
WHERE id = '[submission-id]';
```

### Realtime Not Working

**Symptom:** Queue doesn't update with new submissions

**Possible Causes:**
1. Realtime subscription not connected
2. RLS blocking realtime
3. Filter mismatch
4. Subscription channel wrong

**Solutions:**
```typescript
// Check subscription status
const subscription = supabase
  .channel('moderation_queue')
  .on('postgres_changes', {
    event: '*',
    schema: 'public',
    table: 'content_submissions',
    filter: 'status=eq.pending'
  }, payload => {
    console.log('Realtime update:', payload);
  })
  .subscribe((status) => {
    console.log('Subscription status:', status);
  });

// Verify RLS allows realtime
SELECT * FROM content_submissions WHERE status = 'pending';
-- If this works, realtime should too
```

---

## Versioning Issues

### Version Not Created

**Symptom:** Entity updated but no version record

**Possible Causes:**
1. Trigger not firing
2. Session variables not set
3. Trigger conditions not met
4. Database error

**Solutions:**
```sql
-- Check if trigger exists
SELECT tgname, tgenabled 
FROM pg_trigger 
WHERE tgrelid = 'parks'::regclass;

-- Check session variables during update
SELECT 
  current_setting('app.current_user_id', true) AS user_id,
  current_setting('app.submission_id', true) AS submission_id;

-- Manual version creation (as admin)
INSERT INTO park_versions (
  park_id, version_number, created_by, change_type,
  name, slug, description, -- ... all fields
)
SELECT 
  id, 
  COALESCE((SELECT MAX(version_number) FROM park_versions WHERE park_id = p.id), 0) + 1,
  '[user-id]',
  'updated',
  name, slug, description -- ... all fields
FROM parks p
WHERE id = '[park-id]';
```

### Version Comparison Fails

**Symptom:** Cannot compare versions or diff empty

**Possible Causes:**
1. Version IDs invalid
2. Function error
3. No changes between versions

**Solutions:**
```sql
-- Verify versions exist
SELECT version_id, version_number, change_type 
FROM park_versions 
WHERE park_id = '[park-id]'
ORDER BY version_number DESC;

-- Test diff function
SELECT get_version_diff(
  'park',
  '[from-version-id]'::UUID,
  '[to-version-id]'::UUID
);

-- If null, no changes detected
-- Check if fields actually different
SELECT 
  (SELECT name FROM park_versions WHERE version_id = '[from]') AS old_name,
  (SELECT name FROM park_versions WHERE version_id = '[to]') AS new_name;
```

---

## Performance Issues

### Slow Queries

**Symptom:** Pages load slowly, timeouts

**Possible Causes:**
1. Missing indexes
2. N+1 queries
3. Large dataset
4. Complex joins

**Solutions:**
```sql
-- Find slow queries
SELECT 
  query,
  calls,
  total_exec_time,
  mean_exec_time,
  max_exec_time
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY mean_exec_time DESC
LIMIT 20;

-- Add indexes
CREATE INDEX idx_rides_park_status ON rides(park_id, status);
CREATE INDEX idx_submissions_status_type ON content_submissions(status, submission_type);

-- Analyze query plan
EXPLAIN ANALYZE
SELECT * FROM rides WHERE park_id = '[park-id]' AND status = 'operating';
```

### React Query Stale Data

**Symptom:** UI shows old data after update

**Possible Causes:**
1. Cache not invalidated
2. Stale time too long
3. Background refetch disabled

**Solutions:**
```typescript
// Invalidate specific queries after mutation
const mutation = useMutation({
  mutationFn: updatePark,
  onSuccess: () => {
    queryClient.invalidateQueries(['parks', parkId]);
    queryClient.invalidateQueries(['parks']); // List
  }
});

// Force refetch
await queryClient.refetchQueries(['parks']);

// Disable cache for testing
const { data } = useQuery({
  queryKey: ['parks'],
  queryFn: fetchParks,
  staleTime: 0,
  gcTime: 0,
});
```

---

## Build/Deploy Issues

### Build Fails

**Symptom:** `npm run build` errors

**Possible Causes:**
1. TypeScript errors
2. Missing dependencies
3. Environment variables missing
4. Out of memory

**Solutions:**
```bash
# Type check first
npm run typecheck

# Clear cache
rm -rf node_modules dist .next
npm install

# Build with verbose logs
npm run build -- --verbose

# Increase memory limit
NODE_OPTIONS=--max_old_space_size=4096 npm run build
```

### Edge Function Fails

**Symptom:** 500 error from edge function

**Possible Causes:**
1. Secrets not set
2. Syntax error
3. Timeout
4. Memory limit

**Solutions:**
```bash
# Test locally
supabase functions serve upload-image

# Check logs
supabase functions logs upload-image

# Verify secrets
supabase secrets list

# Set secrets
supabase secrets set CLOUDFLARE_API_TOKEN=[value]
```

---

## Database Issues

### RLS Blocking Query

**Symptom:** Query returns empty when data exists

**Possible Causes:**
1. User not authenticated
2. Wrong user role
3. Policy conditions not met
4. AAL level insufficient

**Solutions:**
```sql
-- Test as authenticated user
SET LOCAL ROLE authenticated;
SET LOCAL "request.jwt.claims" = '{"sub": "[user-id]", "role": "authenticated"}';

-- Check policies
SELECT * FROM pg_policies WHERE tablename = 'content_submissions';

-- Disable RLS temporarily (DEVELOPMENT ONLY)
ALTER TABLE content_submissions DISABLE ROW LEVEL SECURITY;
-- Remember to re-enable!
```

### Migration Fails

**Symptom:** `supabase db push` fails

**Possible Causes:**
1. Conflicting constraints
2. Data violates new schema
3. Circular dependencies
4. Permission issues

**Solutions:**
```sql
-- Check for constraint violations
SELECT * FROM parks WHERE name IS NULL OR name = '';

-- Fix data before migration
UPDATE parks SET name = 'Unnamed Park' WHERE name IS NULL;

-- Drop constraints temporarily
ALTER TABLE parks DROP CONSTRAINT IF EXISTS parks_name_check;

-- Recreate after data fixed
ALTER TABLE parks ADD CONSTRAINT parks_name_check CHECK (name IS NOT NULL AND name != '');
```

---

## User Reports

### "I can't submit a park"

**Checklist:**
1. Is user authenticated?
2. Is user banned?
3. Are all required fields filled?
4. Is form validation passing?
5. Check browser console for errors

### "My submission disappeared"

**Checklist:**
1. Check submission status in database
2. Was it approved/rejected?
3. Check notification logs
4. Verify submission wasn't deleted

### "I can't see the moderation queue"

**Checklist:**
1. Is user a moderator?
2. Is MFA completed (if enrolled)?
3. Check RLS policies
4. Verify user_roles table

---

## Emergency Procedures

### Database Locked Up

```sql
-- Find blocking queries
SELECT 
  blocked_locks.pid AS blocked_pid,
  blocked_activity.usename AS blocked_user,
  blocking_locks.pid AS blocking_pid,
  blocking_activity.usename AS blocking_user,
  blocked_activity.query AS blocked_query,
  blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

-- Kill blocking query (CAREFUL!)
SELECT pg_terminate_backend([blocking_pid]);
```

### Edge Function Infinite Loop

```bash
# Stop all executions
supabase functions delete [function-name]

# Fix and redeploy
supabase functions deploy [function-name]
```

### Out of Database Connections

```sql
-- Check active connections
SELECT count(*) FROM pg_stat_activity;

-- Kill idle connections
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
  AND state_change < NOW() - INTERVAL '5 minutes';
```

---

## Getting Help

If stuck:

1. **Check Documentation**
   - [docs/](./docs/) folder
   - [API documentation](./versioning/API.md)

2. **Search Issues**
   - GitHub Issues
   - Supabase Discord
   - Stack Overflow

3. **Ask for Help**
   - Create detailed issue
   - Include error messages
   - Provide reproduction steps
   - Share relevant code/logs

4. **Contact Support**
   - For critical production issues
   - Email: [support email]

---

**Last Updated:** 2025-01-20
