# PlatformServiceMixin Best Practices Guide

## Overview
This guide establishes best practices for using PlatformServiceMixin in TimeSafari components to ensure consistent, maintainable, and secure code.

## Core Principles

### 1. **Single Source of Truth**
- Always use PlatformServiceMixin for database operations
- Never mix legacy patterns with mixin patterns in the same component
- Use mixin caching to avoid redundant database queries

### 2. **Component Context Awareness**
- Always include component name in error logging
- Use `this.$options.name` for consistent component identification
- Implement proper error boundaries with context

### 3. **Progressive Enhancement**
- Start with basic mixin methods (`$db`, `$exec`, `$one`)
- Use specialized methods when available (`$getAllContacts`, `$settings`)
- Leverage caching for frequently accessed data

## Implementation Patterns

### Database Operations

#### ✅ **Preferred Pattern: Use Specialized Methods**
```typescript
// Best: Use high-level specialized methods
const contacts = await this.$getAllContacts();
const settings = await this.$settings();
const userSettings = await this.$accountSettings(did);
```

#### ✅ **Good Pattern: Use Mapped Query Methods**
```typescript
// Good: Use query methods with automatic mapping
const results = await this.$query<Contact>(
  "SELECT * FROM contacts WHERE registered = ?", 
  [true]
);
```

#### ⚠️ **Acceptable Pattern: Use Raw Database Methods**
```typescript
// Acceptable: Use raw methods when specialized methods don't exist
const result = await this.$db("SELECT COUNT(*) as count FROM logs");
const count = result?.values?.[0]?.[0] || 0;
```

#### ❌ **Anti-Pattern: Direct Platform Service**
```typescript
// Anti-pattern: Avoid direct PlatformService usage
const platformService = PlatformServiceFactory.getInstance();
const result = await platformService.dbQuery(sql, params);
```

### Settings Management

#### ✅ **Best Practice: Use Mixin Methods**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  async loadSettings() {
    // ✅ Use cached settings retrieval
    const settings = await this.$settings();
    return settings;
  }

  async saveUserPreferences(changes: Partial<Settings>) {
    // ✅ Use specialized save method
    await this.$saveSettings(changes);
    await this.$log("User preferences saved");
  }

  async loadAccountSettings(did: string) {
    // ✅ Use account-specific settings
    const accountSettings = await this.$accountSettings(did);
    return accountSettings;
  }
}
```

#### ❌ **Anti-Pattern: Legacy Settings Access**
```typescript
// Anti-pattern: Avoid legacy databaseUtil methods
import * as databaseUtil from "../db/databaseUtil";

async loadSettings() {
  const settings = await databaseUtil.retrieveSettingsForActiveAccount();
  return settings;
}
```

### Error Handling

#### ✅ **Best Practice: Component-Aware Error Handling**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  async performOperation() {
    try {
      const result = await this.$getAllContacts();
      await this.$log("Operation completed successfully");
      return result;
    } catch (error) {
      // ✅ Include component context in error logging
      await this.$logError(`[${this.$options.name}] Operation failed: ${error}`);
      
      // ✅ Provide user-friendly error handling
      this.$notify({
        group: "alert",
        type: "danger",
        title: "Operation Failed",
        text: "Unable to load contacts. Please try again.",
      });
      
      throw error; // Re-throw for upstream handling
    }
  }
}
```

#### ❌ **Anti-Pattern: Generic Error Handling**
```typescript
// Anti-pattern: Generic error handling without context
try {
  // operation
} catch (error) {
  console.error("Error:", error);
  throw error;
}
```

### Logging

#### ✅ **Best Practice: Structured Logging**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  async performDatabaseOperation() {
    // ✅ Log operation start with context
    await this.$log(`[${this.$options.name}] Starting database operation`);

    try {
      const result = await this.$getAllContacts();
      
      // ✅ Log successful completion
      await this.$log(`[${this.$options.name}] Database operation completed, found ${result.length} contacts`);
      
      return result;
    } catch (error) {
      // ✅ Log errors with full context
      await this.$logError(`[${this.$options.name}] Database operation failed: ${error}`);
      throw error;
    }
  }

  // ✅ Use appropriate log levels
  async validateInput(input: string) {
    if (!input) {
      await this.$log(`[${this.$options.name}] Input validation failed: empty input`, 'warn');
      return false;
    }
    return true;
  }
}
```

### Caching Strategies

#### ✅ **Best Practice: Smart Caching Usage**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  async loadContactsWithCaching() {
    // ✅ Use cached contacts (automatically managed by mixin)
    const contacts = await this.$contacts();
    
    // ✅ Force refresh when needed
    if (this.needsFreshData) {
      const freshContacts = await this.$refreshContacts();
      return freshContacts;
    }
    
    return contacts;
  }

  async updateContactAndRefresh(did: string, changes: Partial<Contact>) {
    // ✅ Update contact and invalidate cache
    await this.$updateContact(did, changes);
    
    // ✅ Clear cache to ensure fresh data on next access
    this.$clearAllCaches();
    
    await this.$log(`[${this.$options.name}] Contact updated and cache cleared`);
  }
}
```

## Security Best Practices

### Input Validation

#### ✅ **Always Validate Database Inputs**
```typescript
async saveContact(contact: Partial<Contact>) {
  // ✅ Validate required fields
  if (!contact.did || !contact.name) {
    await this.$logError(`[${this.$options.name}] Invalid contact data: missing required fields`);
    throw new Error('Contact must have DID and name');
  }

  // ✅ Sanitize inputs
  const sanitizedContact = {
    ...contact,
    name: contact.name.trim(),
    // Remove any potential XSS vectors
    notes: contact.notes?.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
  };

  return await this.$insertContact(sanitizedContact);
}
```

### Error Information Disclosure

#### ✅ **Safe Error Handling**
```typescript
async performSensitiveOperation(did: string) {
  try {
    // Sensitive operation
    const result = await this.$accountSettings(did);
    return result;
  } catch (error) {
    // ✅ Log full error for debugging
    await this.$logError(`[${this.$options.name}] Sensitive operation failed: ${error}`);
    
    // ✅ Return generic error to user
    throw new Error('Unable to complete operation. Please try again.');
  }
}
```

### SQL Injection Prevention

#### ✅ **Always Use Parameterized Queries**
```typescript
// ✅ Safe: Parameterized query
async findContactsByName(searchTerm: string) {
  return await this.$query<Contact>(
    "SELECT * FROM contacts WHERE name LIKE ?",
    [`%${searchTerm}%`]
  );
}

// ❌ Dangerous: String concatenation
async findContactsByNameUnsafe(searchTerm: string) {
  return await this.$query<Contact>(
    `SELECT * FROM contacts WHERE name LIKE '%${searchTerm}%'`
  );
}
```

## Performance Optimization

### Database Query Optimization

#### ✅ **Efficient Query Patterns**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  async loadOptimizedData() {
    // ✅ Use transactions for multiple operations
    return await this.$withTransaction(async () => {
      const contacts = await this.$getAllContacts();
      const settings = await this.$settings();
      return { contacts, settings };
    });
  }

  async loadDataWithPagination(offset: number, limit: number) {
    // ✅ Use LIMIT and OFFSET for large datasets
    return await this.$query<Contact>(
      "SELECT * FROM contacts ORDER BY name LIMIT ? OFFSET ?",
      [limit, offset]
    );
  }
}
```

### Memory Management

#### ✅ **Proper Cache Management**
```typescript
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],

  beforeDestroy() {
    // ✅ Clear component caches on destroy
    this.$clearAllCaches();
  }

  async handleLargeDataset() {
    try {
      // Process large dataset
      const largeResult = await this.$query("SELECT * FROM large_table");
      
      // ✅ Process in chunks to avoid memory issues
      const chunkSize = 100;
      for (let i = 0; i < largeResult.length; i += chunkSize) {
        const chunk = largeResult.slice(i, i + chunkSize);
        await this.processChunk(chunk);
      }
    } finally {
      // ✅ Clear caches after processing large datasets
      this.$clearAllCaches();
    }
  }
}
```

## Testing Strategies

### Unit Testing

#### ✅ **Mock Mixin Methods**
```typescript
// test/MyComponent.spec.ts
import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
import { PlatformServiceMixin } from '@/utils/PlatformServiceMixin';

describe('MyComponent', () => {
  let wrapper;
  
  beforeEach(() => {
    // ✅ Mock mixin methods
    const mockMixin = {
      ...PlatformServiceMixin,
      methods: {
        ...PlatformServiceMixin.methods,
        $getAllContacts: jest.fn().mockResolvedValue([]),
        $settings: jest.fn().mockResolvedValue({}),
        $log: jest.fn().mockResolvedValue(undefined),
        $logError: jest.fn().mockResolvedValue(undefined),
      }
    };
    
    wrapper = mount(MyComponent, {
      mixins: [mockMixin]
    });
  });
  
  it('should load contacts on mount', async () => {
    await wrapper.vm.loadContacts();
    expect(wrapper.vm.$getAllContacts).toHaveBeenCalled();
  });
});
```

### Integration Testing

#### ✅ **Test Real Database Operations**
```typescript
// test/integration/ContactsView.spec.ts
import { createLocalVue, mount } from '@vue/test-utils';
import ContactsView from '@/views/ContactsView.vue';
import { PlatformServiceMixin } from '@/utils/PlatformServiceMixin';

describe('ContactsView Integration', () => {
  it('should perform real database operations', async () => {
    const wrapper = mount(ContactsView, {
      mixins: [PlatformServiceMixin]
    });
    
    // ✅ Test real mixin functionality
    const contacts = await wrapper.vm.$getAllContacts();
    expect(Array.isArray(contacts)).toBe(true);
  });
});
```

## Migration Checklist

When migrating components to PlatformServiceMixin:

### Pre-Migration
- [ ] Identify all database operations in the component
- [ ] List all logging operations
- [ ] Check for error handling patterns
- [ ] Note any specialized database queries

### During Migration
- [ ] Add PlatformServiceMixin to mixins array
- [ ] Replace all database operations with mixin methods
- [ ] Update logging to use mixin logging methods
- [ ] Add component context to error messages
- [ ] Replace settings operations with mixin methods
- [ ] Update error handling to use structured patterns

### Post-Migration
- [ ] Remove all legacy imports (databaseUtil, logConsoleAndDb)
- [ ] Test all component functionality
- [ ] Verify TypeScript compilation
- [ ] Check for any remaining anti-patterns
- [ ] Update component tests if needed
- [ ] Run migration validation script

## Troubleshooting Common Issues

### Issue: TypeScript errors after migration
**Solution**: Ensure proper type definitions and mixin interface implementation

### Issue: Methods not available on `this`
**Solution**: Verify PlatformServiceMixin is properly included in mixins array

### Issue: Caching not working as expected
**Solution**: Check cache TTL settings and clear cache when needed

### Issue: Database operations failing
**Solution**: Verify PlatformService is properly initialized and check error logs

### Issue: Performance degradation
**Solution**: Review query efficiency and cache usage patterns

## Version History

- **v1.0** - Initial best practices documentation
- **v1.1** - Added security and performance sections
- **v1.2** - Enhanced testing strategies and troubleshooting 