# Migration Guide: .env to YAML Configuration

This guide helps you migrate from `.env` file configuration to the new YAML-based configuration system.

## Why Migrate to YAML?

The YAML configuration format offers several advantages:

- **Better readability**: Clear hierarchical structure
- **Comments**: Document your configuration inline
- **Complex structures**: Native support for arrays, nested objects
- **Type safety**: Automatic type conversion
- **Environment-specific configs**: Easy to maintain separate configs per environment
- **No prefix noise**: No need for `ATTUNE__` prefixes everywhere

## Quick Migration Steps

1. **Copy the example config**
   ```bash
   cp config.example.yaml config.yaml
   ```

2. **Convert your .env values** (see conversion examples below)

3. **Test the new configuration**
   ```bash
   cargo run --bin attune-api
   ```

4. **Remove old .env file** (optional, after verification)

## Conversion Reference

### Database Configuration

**Before (.env):**
```bash
ATTUNE__DATABASE__URL=postgresql://postgres:postgres@localhost:5432/attune
ATTUNE__DATABASE__MAX_CONNECTIONS=50
ATTUNE__DATABASE__MIN_CONNECTIONS=5
ATTUNE__DATABASE__CONNECT_TIMEOUT=30
ATTUNE__DATABASE__IDLE_TIMEOUT=600
ATTUNE__DATABASE__LOG_STATEMENTS=true
```

**After (config.yaml):**
```yaml
database:
  url: postgresql://postgres:postgres@localhost:5432/attune
  max_connections: 50
  min_connections: 5
  connect_timeout: 30
  idle_timeout: 600
  log_statements: true
```

### Server Configuration

**Before (.env):**
```bash
ATTUNE__SERVER__HOST=0.0.0.0
ATTUNE__SERVER__PORT=8080
ATTUNE__SERVER__REQUEST_TIMEOUT=30
ATTUNE__SERVER__ENABLE_CORS=true
ATTUNE__SERVER__CORS_ORIGINS=http://localhost:3000,http://localhost:5173
ATTUNE__SERVER__MAX_BODY_SIZE=10485760
```

**After (config.yaml):**
```yaml
server:
  host: 0.0.0.0
  port: 8080
  request_timeout: 30
  enable_cors: true
  cors_origins:
    - http://localhost:3000
    - http://localhost:5173
  max_body_size: 10485760
```

### Security Configuration

**Before (.env):**
```bash
ATTUNE__SECURITY__JWT_SECRET=your-secret-key-here
ATTUNE__SECURITY__JWT_ACCESS_EXPIRATION=3600
ATTUNE__SECURITY__JWT_REFRESH_EXPIRATION=604800
ATTUNE__SECURITY__ENCRYPTION_KEY=your-32-char-encryption-key
ATTUNE__SECURITY__ENABLE_AUTH=true
```

**After (config.yaml):**
```yaml
security:
  jwt_secret: your-secret-key-here
  jwt_access_expiration: 3600
  jwt_refresh_expiration: 604800
  encryption_key: your-32-char-encryption-key
  enable_auth: true
```

### Logging Configuration

**Before (.env):**
```bash
ATTUNE__LOG__LEVEL=info
ATTUNE__LOG__FORMAT=json
ATTUNE__LOG__CONSOLE=true
ATTUNE__LOG__FILE=/var/log/attune/attune.log
```

**After (config.yaml):**
```yaml
log:
  level: info
  format: json
  console: true
  file: /var/log/attune/attune.log
```

### Redis Configuration

**Before (.env):**
```bash
ATTUNE__REDIS__URL=redis://localhost:6379
ATTUNE__REDIS__POOL_SIZE=10
```

**After (config.yaml):**
```yaml
redis:
  url: redis://localhost:6379
  pool_size: 10
```

### Message Queue Configuration

**Before (.env):**
```bash
ATTUNE__MESSAGE_QUEUE__URL=amqp://guest:guest@localhost:5672/%2f
ATTUNE__MESSAGE_QUEUE__EXCHANGE=attune
ATTUNE__MESSAGE_QUEUE__ENABLE_DLQ=true
ATTUNE__MESSAGE_QUEUE__MESSAGE_TTL=3600
```

**After (config.yaml):**
```yaml
message_queue:
  url: amqp://guest:guest@localhost:5672/%2f
  exchange: attune
  enable_dlq: true
  message_ttl: 3600
```

## Automated Conversion Script

If you have a complex `.env` file, you can use this Python script to help convert it:

```python
#!/usr/bin/env python3
"""
Convert .env file to YAML configuration
Usage: python env_to_yaml.py .env > config.yaml
"""

import sys
import re

def parse_env_line(line):
    """Parse a single .env line"""
    line = line.strip()
    if not line or line.startswith('#'):
        return None, None
    
    match = re.match(r'ATTUNE__(.+?)=(.+)', line)
    if not match:
        return None, None
    
    key = match.group(1)
    value = match.group(2)
    
    # Remove quotes if present
    value = value.strip('"').strip("'")
    
    # Convert boolean strings
    if value.lower() in ('true', 'false'):
        value = value.lower()
    # Try to convert to number
    elif value.isdigit():
        value = int(value)
    
    return key, value

def build_yaml_structure(env_vars):
    """Build nested YAML structure from flat env vars"""
    structure = {}
    
    for key, value in env_vars.items():
        parts = key.lower().split('__')
        
        # Navigate/create nested structure
        current = structure
        for part in parts[:-1]:
            if part not in current:
                current[part] = {}
            current = current[part]
        
        # Handle special cases (arrays)
        final_key = parts[-1]
        if isinstance(value, str) and ',' in value:
            # Assume comma-separated list
            current[final_key] = [v.strip() for v in value.split(',')]
        else:
            current[final_key] = value
    
    return structure

def print_yaml(data, indent=0):
    """Print YAML structure"""
    for key, value in data.items():
        if isinstance(value, dict):
            print(f"{'  ' * indent}{key}:")
            print_yaml(value, indent + 1)
        elif isinstance(value, list):
            print(f"{'  ' * indent}{key}:")
            for item in value:
                print(f"{'  ' * (indent + 1)}- {item}")
        elif isinstance(value, bool):
            print(f"{'  ' * indent}{key}: {str(value).lower()}")
        elif isinstance(value, (int, float)):
            print(f"{'  ' * indent}{key}: {value}")
        else:
            # Quote strings that might be ambiguous
            if any(c in str(value) for c in [':', '#', '@', '%']):
                print(f"{'  ' * indent}{key}: \"{value}\"")
            else:
                print(f"{'  ' * indent}{key}: {value}")

def main():
    if len(sys.argv) < 2:
        print("Usage: python env_to_yaml.py <.env file>", file=sys.stderr)
        sys.exit(1)
    
    env_file = sys.argv[1]
    env_vars = {}
    
    # Parse .env file
    with open(env_file, 'r') as f:
        for line in f:
            key, value = parse_env_line(line)
            if key and value is not None:
                env_vars[key] = value
    
    # Build and print YAML structure
    structure = build_yaml_structure(env_vars)
    
    print("# Attune Configuration")
    print("# Generated from .env file")
    print("# Please review and adjust as needed")
    print()
    
    print_yaml(structure)

if __name__ == '__main__':
    main()
```

**Usage:**
```bash
python env_to_yaml.py .env > config.yaml
```

**Note:** Always review the generated YAML file and adjust as needed.

## Handling Environment-Specific Configurations

One of the benefits of YAML is easy environment-specific configurations.

### Create Base Configuration

`config.yaml` (common settings):
```yaml
service_name: attune

database:
  max_connections: 50
  connect_timeout: 30

server:
  enable_cors: true
  max_body_size: 10485760

log:
  console: true
```

### Development Override

`config.development.yaml`:
```yaml
environment: development

database:
  url: postgresql://postgres:postgres@localhost:5432/attune
  log_statements: true

server:
  host: 127.0.0.1
  port: 8080

log:
  level: debug
  format: pretty

security:
  jwt_secret: dev-secret-not-for-production
  jwt_access_expiration: 86400  # 24 hours
```

### Production Configuration

`config.production.yaml`:
```yaml
environment: production

database:
  url: postgresql://user:password@db.prod.example.com/attune
  max_connections: 100

server:
  host: 0.0.0.0
  port: 8080
  cors_origins:
    - https://app.example.com
    - https://www.example.com

log:
  level: info
  format: json
  file: /var/log/attune/attune.log

# Use environment variables for secrets!
# ATTUNE__SECURITY__JWT_SECRET
# ATTUNE__SECURITY__ENCRYPTION_KEY
```

## Still Using Environment Variables

Environment variables still work for overrides! This is especially useful for:

- **Container deployments** (Docker, Kubernetes)
- **Production secrets** (don't commit secrets to YAML files)
- **CI/CD pipelines**
- **Quick overrides** without editing files

**Example:**
```bash
# Base config in config.yaml, override database URL
export ATTUNE__DATABASE__URL=postgresql://prod-db/attune
cargo run --bin attune-api
```

## Migration Checklist

- [ ] Back up your existing `.env` file
- [ ] Copy `config.example.yaml` to `config.yaml`
- [ ] Convert `.env` values to YAML format
- [ ] Add comments to document your configuration
- [ ] Test the application starts successfully
- [ ] Verify all features work (database, authentication, etc.)
- [ ] Create environment-specific configs if needed
- [ ] Update deployment scripts/documentation
- [ ] Set up environment variables for production secrets
- [ ] Add `config.yaml` to `.gitignore` (if it contains secrets)
- [ ] Commit example configs to version control

## Rollback Plan

If you need to roll back to `.env` files:

1. The old `.env` file still works (keep your backup)
2. Rename/remove `config.yaml` temporarily
3. Environment variables with `ATTUNE__` prefix still work

**Note:** The application will still load config from environment variables if no YAML file is present.

## Common Issues

### Issue: Configuration not loading

**Problem:** Application can't find `config.yaml`

**Solution:** 
- Ensure `config.yaml` is in the current working directory
- Or set `ATTUNE_CONFIG` environment variable:
  ```bash
  export ATTUNE_CONFIG=/path/to/config.yaml
  ```

### Issue: YAML syntax error

**Problem:** YAML parsing fails

**Solution:**
- Check indentation (use spaces, not tabs)
- Validate YAML syntax:
  ```bash
  # Using Python
  python -c "import yaml; yaml.safe_load(open('config.yaml'))"
  
  # Using yq
  yq eval config.yaml
  ```

### Issue: Boolean values not working

**Problem:** Boolean value treated as string

**Solution:** Use lowercase without quotes:
```yaml
# Correct
enable_auth: true
log_statements: false

# Incorrect
enable_auth: "true"    # String, not boolean
LOG_STATEMENTS: FALSE  # Wrong case
```

### Issue: Array values not parsing

**Problem:** CORS origins not loading correctly

**Solution:** Use YAML array syntax:
```yaml
# Correct
cors_origins:
  - http://localhost:3000
  - http://localhost:5173

# Also correct (inline)
cors_origins: [http://localhost:3000, http://localhost:5173]

# For env vars, use comma-separated
export ATTUNE__SERVER__CORS_ORIGINS="http://localhost:3000,http://localhost:5173"
```

### Issue: Special characters in strings

**Problem:** URL or path not parsing correctly

**Solution:** Quote strings with special characters:
```yaml
# With special characters
database:
  url: "postgresql://user:p@ssw0rd!@localhost/attune"

# Or use single quotes
redis:
  url: 'redis://localhost:6379'
```

## Tips and Best Practices

### 1. Use Comments

Document your configuration:
```yaml
database:
  # Production database endpoint
  url: postgresql://user@db.example.com/attune
  
  # Adjust based on expected load
  max_connections: 100  # Increased for production traffic
```

### 2. Group Related Settings

Keep related configuration together:
```yaml
# All server-related settings
server:
  host: 0.0.0.0
  port: 8080
  cors_origins:
    - https://app.example.com
```

### 3. Use Environment-Specific Files

Don't duplicate settings—use base + overrides:
```
config.yaml              # Common settings
config.development.yaml  # Dev overrides
config.production.yaml   # Prod overrides
```

### 4. Never Commit Secrets

Use environment variables for sensitive data:
```yaml
# config.production.yaml (committed to git)
security:
  # Override these with environment variables!
  jwt_secret: CHANGE_ME_USE_ENV_VAR
  encryption_key: CHANGE_ME_USE_ENV_VAR
```

```bash
# In production (not in git)
export ATTUNE__SECURITY__JWT_SECRET=$(openssl rand -base64 64)
export ATTUNE__SECURITY__ENCRYPTION_KEY=$(openssl rand -base64 32)
```

### 5. Validate Configuration

Always validate after migration:
```bash
# The application will validate on startup
cargo run --bin attune-api

# Check for validation errors in logs
```

## Getting Help

If you encounter issues during migration:

1. **Check the logs**: Enable debug logging
   ```bash
   ATTUNE__LOG__LEVEL=debug cargo run --bin attune-api
   ```

2. **Review documentation**:
   - [Configuration Guide](configuration.md)
   - [Quick Start Guide](quick-start.md)

3. **Validate YAML syntax**: Use online validators or CLI tools

4. **Compare with examples**: Check `config.example.yaml`

## Summary

The migration from `.env` to YAML provides:

✅ Better readability and maintainability  
✅ Native support for complex data types  
✅ Easy environment-specific configurations  
✅ Inline documentation with comments  
✅ Backward compatibility with environment variables  

The YAML configuration system gives you more flexibility while maintaining the security and override capabilities of environment variables.