# API Client Setup - Complete Implementation

## Overview

I have created a robust API client for your Flutter warehouse management app with comprehensive features including:

- **Automatic token management** from secure storage
- **401 error handling** with automatic token clearing
- **Request/response logging** with sensitive data redaction
- **Configurable timeouts** (30 seconds)
- **Proper error transformation** to custom exceptions
- **Support for all HTTP methods** (GET, POST, PUT, DELETE)

## Files Created

### 1. Core Network Files

#### `/lib/core/network/api_client.dart`
- Main API client implementation using Dio
- Automatic Bearer token injection from secure storage
- Request/response/error interceptors with comprehensive logging
- 401 error handler that clears tokens and triggers logout callback
- Methods: `get()`, `post()`, `put()`, `delete()`
- Utility methods: `testConnection()`, `isAuthenticated()`, `getAccessToken()`, `clearAuth()`

#### `/lib/core/network/api_response.dart`
- Generic API response wrapper matching your backend format
- Structure: `Value`, `IsSuccess`, `IsFailure`, `Errors`, `ErrorCodes`
- Helper methods: `hasData`, `getErrorMessage()`, `getAllErrorsAsString()`, `hasErrorCode()`

#### `/lib/core/network/api_client_example.dart`
- Comprehensive usage examples for all scenarios
- Examples for: Login, GET/POST/PUT/DELETE requests, error handling, etc.

#### `/lib/core/network/README.md`
- Complete documentation for the API client
- Usage guides, best practices, troubleshooting

### 2. Secure Storage

#### `/lib/core/storage/secure_storage.dart`
- Singleton wrapper for flutter_secure_storage
- Token management: `saveAccessToken()`, `getAccessToken()`, `clearTokens()`
- User data: `saveUserId()`, `saveUsername()`, etc.
- Utility methods: `isAuthenticated()`, `clearAll()`, `containsKey()`
- Platform-specific secure options (Android: encrypted shared prefs, iOS: Keychain)

### 3. Constants

#### `/lib/core/constants/api_endpoints.dart`
- Centralized API endpoint definitions
- Authentication: `/auth/login`, `/auth/logout`
- Warehouses: `/warehouses`
- Products: `/products` with query parameter helpers
- Scans: `/api/scans`

### 4. Core Exports

#### `/lib/core/core.dart` (Updated)
- Added exports for new modules:
  - `api_endpoints.dart`
  - `api_response.dart`
  - `secure_storage.dart`

### 5. Dependencies

#### `pubspec.yaml` (Updated)
- Added `flutter_secure_storage: ^9.0.0`

## Key Features

### 1. Automatic Token Management

The API client automatically injects Bearer tokens into all requests:

```dart
// Initialize with secure storage
final secureStorage = SecureStorage();
final apiClient = ApiClient(secureStorage);

// Token is automatically added to all requests
final response = await apiClient.get('/warehouses');
// Request header: Authorization: Bearer <token>
```

### 2. 401 Error Handling

When a 401 Unauthorized error occurs:
1. Error is logged to console
2. All tokens are cleared from secure storage
3. `onUnauthorized` callback is triggered
4. App can navigate to login screen

```dart
final apiClient = ApiClient(
  secureStorage,
  onUnauthorized: () {
    // This callback is triggered on 401 errors
    context.go('/login'); // Navigate to login
  },
);
```

### 3. Comprehensive Logging

All requests, responses, and errors are logged with sensitive data redacted:

```
REQUEST[GET] => https://api.example.com/warehouses
Headers: {Authorization: ***REDACTED***, Content-Type: application/json}

RESPONSE[200] => https://api.example.com/warehouses
Data: {...}

ERROR[401] => https://api.example.com/products
401 Unauthorized - Clearing tokens and triggering logout
```

### 4. Error Handling

Dio exceptions are transformed into custom app exceptions:

```dart
try {
  final response = await apiClient.get('/products');
} on NetworkException catch (e) {
  // Timeout, no internet, etc.
  print('Network error: ${e.message}');
} on ServerException catch (e) {
  // 4xx, 5xx errors
  print('Server error: ${e.message}');
  print('Error code: ${e.code}'); // e.g., '401', '404', '500'
}
```

Specific error codes:
- `401`: Unauthorized (automatically handled)
- `403`: Forbidden
- `404`: Not Found
- `422`: Validation Error
- `429`: Rate Limited
- `500-504`: Server Errors

### 5. API Response Parsing

All API responses follow the standard format:

```dart
final response = await apiClient.get('/warehouses');

final apiResponse = ApiResponse.fromJson(
  response.data,
  (json) => (json as List).map((e) => Warehouse.fromJson(e)).toList(),
);

if (apiResponse.isSuccess && apiResponse.value != null) {
  final warehouses = apiResponse.value;
  print('Found ${warehouses.length} warehouses');
} else {
  print('Error: ${apiResponse.getErrorMessage()}');
}
```

## Usage Examples

### Initialize API Client

```dart
import 'package:minhthu/core/core.dart';

final secureStorage = SecureStorage();
final apiClient = ApiClient(
  secureStorage,
  onUnauthorized: () {
    // Navigate to login on 401 errors
    context.go('/login');
  },
);
```

### Login Flow

```dart
// 1. Login request
final response = await apiClient.post(
  ApiEndpoints.login,
  data: {
    'username': 'user@example.com',
    'password': 'password123',
  },
);

// 2. Parse response
final apiResponse = ApiResponse.fromJson(
  response.data,
  (json) => User.fromJson(json),
);

// 3. Save tokens (typically done in LoginUseCase)
if (apiResponse.isSuccess && apiResponse.value != null) {
  final user = apiResponse.value!;
  await secureStorage.saveAccessToken(user.accessToken);
  await secureStorage.saveRefreshToken(user.refreshToken);
  await secureStorage.saveUserId(user.userId);
}
```

### Get Warehouses (Authenticated)

```dart
// Token is automatically added by the interceptor
final response = await apiClient.get(ApiEndpoints.warehouses);

final apiResponse = ApiResponse.fromJson(
  response.data,
  (json) => (json as List).map((e) => Warehouse.fromJson(e)).toList(),
);

if (apiResponse.isSuccess && apiResponse.value != null) {
  final warehouses = apiResponse.value!;
  // Use the data
}
```

### Get Products with Query Parameters

```dart
final response = await apiClient.get(
  ApiEndpoints.products,
  queryParameters: ApiEndpoints.productQueryParams(
    warehouseId: 1,
    type: 'import',
  ),
);
```

### Save Scan Data

```dart
final response = await apiClient.post(
  ApiEndpoints.scans,
  data: {
    'barcode': '1234567890',
    'field1': 'Value 1',
    'field2': 'Value 2',
    'field3': 'Value 3',
    'field4': 'Value 4',
  },
);
```

### Check Authentication Status

```dart
final isAuthenticated = await apiClient.isAuthenticated();
if (!isAuthenticated) {
  // Navigate to login
}
```

### Logout

```dart
await apiClient.clearAuth(); // Clears all tokens
```

## Integration with Repository Pattern

The API client is designed to work with your clean architecture:

```dart
class WarehouseRemoteDataSourceImpl implements WarehouseRemoteDataSource {
  final ApiClient apiClient;

  WarehouseRemoteDataSourceImpl(this.apiClient);

  @override
  Future<List<Warehouse>> getWarehouses() async {
    final response = await apiClient.get(ApiEndpoints.warehouses);

    final apiResponse = ApiResponse.fromJson(
      response.data,
      (json) => (json as List).map((e) => Warehouse.fromJson(e)).toList(),
    );

    if (apiResponse.isSuccess && apiResponse.value != null) {
      return apiResponse.value!;
    } else {
      throw ServerException(apiResponse.getErrorMessage());
    }
  }
}
```

## Configuration

### Timeouts (in `app_constants.dart`)

```dart
static const int connectionTimeout = 30000; // 30 seconds
static const int receiveTimeout = 30000;    // 30 seconds
static const int sendTimeout = 30000;       // 30 seconds
```

### Base URL (in `app_constants.dart`)

```dart
static const String apiBaseUrl = 'https://api.example.com';
```

Or update dynamically:

```dart
apiClient.updateBaseUrl('https://dev-api.example.com');
```

## Security Features

1. **Token Encryption**: Tokens stored using platform-specific secure storage
   - Android: Encrypted SharedPreferences
   - iOS: Keychain with `first_unlock` accessibility

2. **Automatic Token Clearing**: 401 errors automatically clear all tokens

3. **Log Sanitization**: Authorization headers redacted in logs
   ```
   Headers: {Authorization: ***REDACTED***}
   ```

4. **Singleton Pattern**: SecureStorage uses singleton to prevent multiple instances

## Testing

To test the API connection:

```dart
final isConnected = await apiClient.testConnection();
if (!isConnected) {
  print('Cannot connect to API');
}
```

## Dependency Injection (GetIt)

Register with GetIt service locator:

```dart
final getIt = GetIt.instance;

// Register SecureStorage
getIt.registerLazySingleton<SecureStorage>(() => SecureStorage());

// Register ApiClient
getIt.registerLazySingleton<ApiClient>(
  () => ApiClient(
    getIt<SecureStorage>(),
    onUnauthorized: () {
      // Handle unauthorized
    },
  ),
);
```

## File Structure

```
lib/
  core/
    constants/
      app_constants.dart          # Existing - timeouts and base URL
      api_endpoints.dart          # NEW - API endpoint constants
    network/
      api_client.dart             # UPDATED - Full implementation
      api_response.dart           # NEW - API response wrapper
      api_client_example.dart     # NEW - Usage examples
      README.md                   # NEW - Documentation
    storage/
      secure_storage.dart         # NEW - Secure storage wrapper
    errors/
      exceptions.dart             # Existing - Used by API client
      failures.dart               # Existing - Used by repositories
    core.dart                     # UPDATED - Added new exports
```

## Next Steps

1. **Update Existing Repositories**: Update your remote data sources to use the new API client
2. **Configure Base URL**: Set the correct API base URL in `app_constants.dart`
3. **Set Up Navigation**: Implement the `onUnauthorized` callback to navigate to login
4. **Add API Endpoints**: Add any missing endpoints to `api_endpoints.dart`
5. **Test Authentication Flow**: Test login, token injection, and 401 handling

## Testing the Setup

Run the example:

```dart
import 'package:minhthu/core/network/api_client_example.dart';

void main() async {
  await runExamples();
}
```

## Troubleshooting

### Token not being injected
- Verify token is saved: `await secureStorage.getAccessToken()`
- Check logs for: `REQUEST[...] Headers: {Authorization: ***REDACTED***}`

### 401 errors not clearing tokens
- Verify `onUnauthorized` callback is set
- Check logs for: `401 Unauthorized - Clearing tokens and triggering logout`

### Connection timeouts
- Check network connectivity
- Verify base URL is correct
- Increase timeout values if needed

## Analysis Results

All files pass Flutter analysis with no issues:
- ✅ `api_client.dart` - No issues found
- ✅ `secure_storage.dart` - No issues found
- ✅ `api_response.dart` - No issues found
- ✅ `api_endpoints.dart` - No issues found

## Documentation

For detailed documentation, see:
- `/lib/core/network/README.md` - Complete API client documentation
- `/lib/core/network/api_client_example.dart` - Code examples

## Summary

The API client is production-ready with:
- ✅ Automatic token management from secure storage
- ✅ Request interceptor to inject Bearer tokens
- ✅ Response interceptor for logging
- ✅ Error interceptor to handle 401 errors
- ✅ Automatic token clearing on unauthorized access
- ✅ Comprehensive error handling
- ✅ Request/response logging with sensitive data redaction
- ✅ Support for all HTTP methods (GET, POST, PUT, DELETE)
- ✅ Configurable timeouts (30 seconds)
- ✅ Environment-specific base URLs
- ✅ Connection testing
- ✅ Clean integration with repository pattern
- ✅ Comprehensive documentation and examples
- ✅ All files pass static analysis

The API client is ready to use and follows Flutter best practices and clean architecture principles!
