> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeus.ttr.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache Layer

> Local-first offline caching with SQLite for mobile

## Overview

The Flutter cache layer provides a local-first, offline-capable caching system with automatic synchronization. It uses SQLite for persistent storage on mobile devices and supports soft deletes with deferred hard deletion until after server sync.

## Features

* **Generic Cache**: Key-value storage with optional TTL
* **Entity Caching**: Typed repositories for Wallet, Category, and Transaction
* **Offline-First**: Works without network, queues changes for sync
* **Soft Deletes**: Entities are marked deleted, hard deleted only after sync
* **Auto-Sync**: Automatically syncs pending changes on app startup
* **LRU Eviction**: Configurable cache size with automatic eviction

## Architecture

```
lib/cache/
├── cache.dart                    # Barrel exports
├── cache_manager.dart            # Main entry point
├── database/
│   ├── cache_database.dart       # SQLite database setup
│   └── migrations/               # Schema migrations
├── models/                       # Data models
├── providers/                    # Storage implementations
├── repositories/                 # Entity repositories
└── services/                     # Sync service
```

## Quick Start

### Initialize

The cache is automatically initialized in `bootstrap.dart`:

```dart theme={null}
// In bootstrap.dart - already configured
final cache = await CacheManager.initialize();
cache.configureSync(YourSyncApiClient());
await cache.initializeSync();
```

### Using the Cache

```dart theme={null}
// Get the cache instance
final cache = CacheManager.instance;

// Generic cache operations
await cache.put('user_settings', '{"theme": "dark"}');
final settings = await cache.get<String>('user_settings');

// With TTL
await cache.put('temp_data', 'value', ttl: Duration(hours: 1));

// Create entities
final wallet = await cache.createWallet(
  name: 'Cash',
  currency: 'USD',
  initialBalance: 1000.0,
);

final category = await cache.createCategory(
  name: 'Food',
  type: 'expense',
  color: 0xFFFF5722,
);

final transaction = await cache.createTransaction(
  walletId: wallet.id,
  categoryId: category.id,
  amount: 25.50,
  type: 'expense',
  description: 'Lunch',
);
```

### Querying Entities

```dart theme={null}
// Get all wallets
final wallets = await cache.wallets.findAll();

// Get transactions by wallet
final walletTx = await cache.transactions.findByWallet(wallet.id);

// Get transactions by date range
final monthlyTx = await cache.transactions.findByDateRange(
  DateTime.now().subtract(Duration(days: 30)),
  DateTime.now(),
);

// Get categories by type
final expenseCategories = await cache.categories.findByType('expense');
```

### Deleting Entities

```dart theme={null}
// Soft delete (marks as pending_delete, syncs to server first)
await cache.wallets.delete(wallet.id);

// Hard delete (only if already synced or forced)
await cache.wallets.delete(wallet.id, hardDelete: true);
```

### Manual Sync

```dart theme={null}
// Trigger manual sync
final result = await cache.sync();
if (result.success) {
  print('Sync completed');
} else {
  print('Sync failed: ${result.error}');
}

// Listen to sync progress
cache.syncProgress?.listen((progress) {
  print('${progress.step}: ${progress.percent}%');
});
```

## Data Models

### Wallet

| Field        | Type       | Description                   |
| ------------ | ---------- | ----------------------------- |
| `id`         | String     | Local UUID (primary key)      |
| `serverId`   | String?    | Server-assigned ID after sync |
| `name`       | String     | Wallet name                   |
| `currency`   | String     | Currency code (default: USD)  |
| `balance`    | double     | Current balance               |
| `isDeleted`  | bool       | Soft delete flag              |
| `syncStatus` | SyncStatus | sync state                    |
| `createdAt`  | DateTime   | Creation timestamp            |
| `updatedAt`  | DateTime   | Last update timestamp         |
| `syncedAt`   | DateTime?  | Last successful sync          |

### Category

| Field        | Type       | Description           |
| ------------ | ---------- | --------------------- |
| `id`         | String     | Local UUID            |
| `serverId`   | String?    | Server ID after sync  |
| `name`       | String     | Category name         |
| `type`       | String     | 'income' or 'expense' |
| `color`      | int?       | Color value           |
| `icon`       | String?    | Icon identifier       |
| `isDeleted`  | bool       | Soft delete flag      |
| `syncStatus` | SyncStatus | Sync state            |

### Transaction

| Field         | Type       | Description                        |
| ------------- | ---------- | ---------------------------------- |
| `id`          | String     | Local UUID                         |
| `serverId`    | String?    | Server ID after sync               |
| `walletId`    | String     | Reference to wallet                |
| `categoryId`  | String?    | Reference to category              |
| `amount`      | double     | Transaction amount                 |
| `type`        | String     | 'income', 'expense', or 'transfer' |
| `description` | String?    | Optional description               |
| `date`        | DateTime   | Transaction date                   |
| `isDeleted`   | bool       | Soft delete flag                   |
| `syncStatus`  | SyncStatus | Sync state                         |

## Sync Status

Entities have one of four sync states:

* **`synced`**: Entity is in sync with server
* **`pendingCreate`**: New entity, waiting to be created on server
* **`pendingUpdate`**: Modified entity, waiting to update on server
* **`pendingDelete`**: Marked for deletion, waiting to delete on server

## Implementing Your Sync API

Create a class implementing `SyncApiClient`:

```dart theme={null}
class MySyncApiClient implements SyncApiClient {
  final Dio _dio;
  
  MySyncApiClient(this._dio);
  
  @override
  Future<SyncResult<Wallet>> createWallet(Wallet wallet) async {
    try {
      final response = await _dio.post('/wallets', data: {
        'name': wallet.name,
        'currency': wallet.currency,
        'balance': wallet.balance,
      });
      
      return SyncResult(
        success: true,
        serverId: response.data['id'],
        data: wallet.copyWith(serverId: response.data['id']),
      );
    } catch (e) {
      return SyncResult(success: false, error: e.toString());
    }
  }
  
  // ... implement other methods
}
```

## Cache Configuration

```dart theme={null}
// Initialize with max size (LRU eviction)
final cache = await CacheManager.initialize(maxSize: 1000);

// Or set later
cache.setMaxSize(1000);

// Clear all generic cache
await cache.clear();
```

## Database Schema

The cache uses SQLite with these tables:

* **`wallets`**: Wallet entities
* **`categories`**: Category entities
* **`transactions`**: Transaction entities
* **`generic_cache`**: Key-value generic cache
* **`pending_operations`**: Retry queue for failed operations

## Testing

Use the provided `StubSyncApiClient` for testing without a backend:

```dart theme={null}
final cache = await CacheManager.initialize();
cache.configureSync(StubSyncApiClient());
await cache.initializeSync();
```

## Migration Guide

When the schema changes:

1. Update `_databaseVersion` in `cache_database.dart`
2. Add migration logic in `_onUpgrade`
3. Test migration with existing data
