# API Request Architecture Documentation

## Overview

This document explains how to create and handle JSON-based API requests in the Translator application. The architecture has been refactored to use a unified, type-safe template system that ensures consistency and maintainability.

## Architecture Components

### 1. JsonHelper
**Location**: `app/src/main/java/eu/gaudian/translator/utils/JsonHelper.kt`

The JsonHelper provides unified JSON parsing, validation, and error handling for all API responses.

**Key Methods**:
```kotlin
// Parse JSON with comprehensive error handling
fun <T> parseJson(json: String, serializer: KSerializer<T>, serviceName: String): Result<T>

// Validate required fields exist in JSON
fun validateRequiredFields(json: String, requiredFields: List<String>): Boolean

// Extract specific field value
fun extractField(json: String, fieldName: String): String?

// Clean and validate JSON string
fun cleanAndValidateJson(json: String): String
```

### 2. ApiRequestHandler
**Location**: `app/src/main/java/eu/gaudian/translator/utils/ApiRequestHandler.kt`

The single entry point for all API calls. This is the ONLY component that should call `ApiManager.getCompletion()`.

**Key Methods**:
```kotlin
// Preferred method - use templates
suspend fun <T> executeRequest(template: ApiRequestTemplate<T>): Result<T>

// Legacy method - deprecated
suspend fun <T> executeRequest(prompt: String, serializer: KSerializer<T>, modelType: ModelType): Result<T>

// Text-only requests
suspend fun executeTextRequest(prompt: String, modelType: ModelType): Result<String>
```

### 3. ApiRequestTemplates
**Location**: `app/src/main/java/eu/gaudian/translator/utils/ApiRequestTemplates.kt`

Type-safe request templates that define the structure and validation for different API operations.

## Creating New JSON API Requests

### Step 1: Define Your Response Data Class

Create a serializable data class for your API response:

```kotlin
@kotlinx.serialization.Serializable
data class MyApiResponse(
    val requiredField: String,
    val optionalField: Int? = null,
    val items: List<MyItem> = emptyList()
)

@kotlinx.serialization.Serializable
data class MyItem(
    val id: String,
    val name: String,
    val value: Double
)
```

### Step 2: Create a Request Template

Extend `BaseApiRequestTemplate<T>` for type-safe requests:

```kotlin
class MyApiRequest(
    private val parameter1: String,
    private val parameter2: Int,
    private val language: String
) : BaseApiRequestTemplate<MyApiResponse>() {
    
    override val responseSerializer = MyApiResponse.serializer()
    override val modelType = ModelType.TRANSLATION // Choose appropriate type
    override val serviceName = "MyService"
    override val requiredFields = listOf("requiredField") // Fields that must exist
    
    init {
        promptBuilder.basePrompt = "Perform my API operation with $parameter1 and $parameter2"
        addDetail("Language: $language")
        addDetail("Parameter 2 value: $parameter2")
        withJsonResponse("a JSON object with 'requiredField' (string), 'optionalField' (integer), and 'items' array")
    }
}
```

### Step 3: Use the Template in Your Service

```kotlin
class MyService(context: Context) {
    private val apiRequestHandler = ApiRequestHandler(ApiManager(context), context)
    
    suspend fun performMyOperation(param1: String, param2: Int, language: String): Result<MyApiResponse> {
        return try {
            Log.i("MyService", "Performing operation with $param1, $param2 in $language")
            
            val template = MyApiRequest(
                parameter1 = param1,
                parameter2 = param2,
                language = language
            )
            
            val result = apiRequestHandler.executeRequest(template)
            
            result.map { response ->
                Log.i("MyService", "Successfully completed operation")
                response
            }.onFailure { exception ->
                Log.e("MyService", "Failed to perform operation", exception)
            }
        } catch (e: Exception) {
            Log.e("MyService", "Unexpected error in performMyOperation", e)
            Result.failure(e)
        }
    }
}
```

## Available Model Types

Choose the appropriate `ModelType` for your request:

- `ModelType.TRANSLATION` - For translation-related requests
- `ModelType.DICTIONARY` - For dictionary and vocabulary requests
- `ModelType.VOCABULARY` - For vocabulary generation and analysis
- `ModelType.EXERCISE` - For exercise generation
- `ModelType.CORRECTION` - For text correction

## JSON Response Structure Guidelines

### 1. Always Define Required Fields
```kotlin
override val requiredFields = listOf("id", "name", "data")
```

### 2. Use Clear Field Names
```kotlin
// Good
data class Response(val userId: String, val userName: String, val isActive: Boolean)

// Avoid
data class Response(val uid: String, val nm: String, val active: Boolean)
```

### 3. Include Type Information in Prompts
```kotlin
withJsonResponse("a JSON object with 'userId' (string), 'userName' (string), 'isActive' (boolean), and 'createdAt' (timestamp)")
```

## Error Handling Best Practices

### 1. Always Wrap in Try-Catch
```kotlin
suspend fun myMethod(): Result<MyType> = withContext(Dispatchers.IO) {
    try {
        // Your logic here
        result.map { response ->
            // Success handling
            response
        }.onFailure { exception ->
            // Failure logging
            Log.e("MyService", "Operation failed", exception)
        }
    } catch (e: Exception) {
        Log.e("MyService", "Unexpected error", e)
        Result.failure(e)
    }
}
```

### 2. Log at Appropriate Levels
```kotlin
Log.i("ServiceName", "Starting operation with parameters: $param1, $param2")  // Info level
Log.d("ServiceName", "Generated prompt: $prompt")  // Debug level
Log.e("ServiceName", "Operation failed", exception)  // Error level
```

### 3. Validate Responses
```kotlin
// The template automatically validates required fields
// But you can add custom validation if needed
result.map { response ->
    if (response.items.isEmpty()) {
        Log.w("MyService", "API returned empty items array")
    }
    response
}
```

## Testing Your API Requests

### 1. Unit Test Your Template
```kotlin
@Test
fun `MyApiRequest should build correct prompt`() {
    val template = MyApiRequest("test", 42, "English")
    val prompt = template.buildPrompt()
    
    assertTrue(prompt.contains("test"))
    assertTrue(prompt.contains("42"))
    assertTrue(prompt.contains("English"))
    assertTrue(prompt.contains("JSON object"))
}
```

### 2. Test JSON Parsing
```kotlin
@Test
fun `JsonHelper should parse valid response`() {
    val json = """{"requiredField": "value", "optionalField": 123}"""
    val result = jsonHelper.validateRequiredFields(json, listOf("requiredField"))
    
    assertTrue(result)
}
```

### 3. Integration Test
```kotlin
@Test
fun `end to end API request should work`() = runTest {
    // Mock the API manager
    every { 
        apiManager.getCompletion(
            prompt = any(),
            callback = any(),
            modelType = ModelType.TRANSLATION
        )
    } answers {
        val callback = thirdArg<(String?) -> Unit>()
        callback("""{"requiredField": "test", "optionalField": 123}""")
    }
    
    val template = MyApiRequest("test", 42, "English")
    val result = apiRequestHandler.executeRequest(template)
    
    assertTrue(result.isSuccess)
    assertEquals("test", result.getOrNull()?.requiredField)
}
```

## Migration from Legacy Code

### Before (Legacy):
```kotlin
val prompt = PromptBuilder("Do something")
    .addDetail("Parameter: $param")
    .withJsonResponse("JSON format")
    .build()

apiRequestHandler.executeRequest(prompt, MyResponse.serializer(), ModelType.TRANSLATION)
```

### After (New Architecture):
```kotlin
val template = MyApiRequest(param)
apiRequestHandler.executeRequest(template)
```

## Common Patterns

### 1. Language-Specific Requests
```kotlin
class LanguageSpecificRequest(
    private val text: String,
    private val sourceLanguage: String,
    private val targetLanguage: String
) : BaseApiRequestTemplate<TranslationResponse>() {
    
    override val serviceName = "TranslationService"
    
    init {
        promptBuilder.basePrompt = "Translate from $sourceLanguage to $targetLanguage: '$text'"
        withJsonResponse("a JSON object with 'translatedText' containing the translation")
    }
}
```

### 2. Batch Processing Requests
```kotlin
class BatchProcessingRequest(
    private val items: List<String>,
    private val operation: String
) : BaseApiRequestTemplate<BatchResponse>() {
    
    override val serviceName = "BatchService"
    
    init {
        val itemsJson = Json.encodeToString(items)
        promptBuilder.basePrompt = "Perform $operation on these items: $itemsJson"
        withJsonResponse("a JSON object with 'results' array containing processed items")
    }
}
```

### 3. Configuration-Based Requests
```kotlin
class ConfigurableRequest(
    private val baseText: String,
    private val customInstructions: String,
    private val outputFormat: String
) : BaseApiRequestTemplate<ConfigurableResponse>() {
    
    override val serviceName = "ConfigurableService"
    
    init {
        promptBuilder.basePrompt = baseText
        addDetail(customInstructions)
        withJsonResponse(outputFormat)
    }
}
```

## Troubleshooting

### Common Issues and Solutions

1. **"Unresolved reference" errors**
   - Make sure you're calling the correct method (private methods stay within their class)
   - Check imports are correct

2. **JSON parsing failures**
   - Use `jsonHelper.validateRequiredFields()` to check structure first
   - Check that your response data class matches the actual JSON structure
   - Look at logs for detailed parsing error messages

3. **Template not working**
   - Verify all required fields are listed in `requiredFields`
   - Check that the prompt clearly explains the expected JSON format
   - Ensure the serializer matches your data class

4. **API request timeouts**
   - Check network connectivity
   - Verify the API manager is properly initialized
   - Look at API logs for detailed error information

## Best Practices Summary

1. **Always use templates** for new API requests
2. **Define required fields** to catch malformed responses early
3. **Log comprehensively** at appropriate levels
4. **Handle errors gracefully** with proper Result types
5. **Write tests** for both templates and parsing logic
6. **Keep prompts clear** and specific about JSON structure
7. **Use descriptive service names** for better logging
8. **Validate inputs** before making API requests

## Future Considerations

When extending the architecture:

1. **Reuse existing templates** when possible
2. **Follow naming conventions** consistently
3. **Document custom logic** in comments
4. **Add comprehensive tests** for new functionality
5. **Consider backward compatibility** when changing existing APIs

This architecture is designed to be easily extensible while maintaining type safety and consistency across all API operations.
