# Developer Migration Guide

Step-by-step guide for migrating modules to the new exception hierarchy system.

## Overview

This guide provides a practical, hands-on approach to migrating existing modules from legacy exception handling (RuntimeException, generic exceptions) to the framework's standardized exception hierarchy with ErrorCode integration.

## Prerequisites

Before starting migration:
- Read [Exception Hierarchy Pattern Guide](./exception-hierarchy-pattern.md)
- Read [ErrorHandler Enhancements Guide](./error-handler-enhancements.md)
- Understand ErrorCode system and ExceptionContext
- Have module test suite ready for validation

## Migration Process Overview

```
1. Module Analysis       → Identify exceptions and patterns
2. ErrorCode Definition  → Create module-specific error codes
3. Base Exception        → Create module base exception
4. Specific Exceptions   → Create domain-specific exceptions
5. Service Migration     → Replace legacy exceptions
6. Testing & Validation  → Verify functionality
7. Documentation Update  → Update module docs
```

## Step 1: Module Analysis

### Identify All Exceptions

Use grep to find all exceptions in the module:

```bash
# Find all RuntimeException throws
grep -r "throw new \\\\RuntimeException" src/Framework/YourModule/

# Find all InvalidArgumentException (business logic only)
grep -r "throw new \\\\InvalidArgumentException" src/Framework/YourModule/

# Find all generic Exception throws
grep -r "throw new \\\\Exception" src/Framework/YourModule/

# Create inventory
grep -r "throw new" src/Framework/YourModule/ | wc -l
```

**Example Output from Queue Module**:
```
Found 22 exceptions:
- 15 RuntimeException (business logic issues)
- 5 InvalidArgumentException (mix of constructor validation and business logic)
- 2 Exception (generic errors)
```

### Categorize Exceptions

Create a spreadsheet or document:

| File | Line | Exception Type | Context | Keep/Migrate |
|------|------|----------------|---------|--------------|
| JobPersistenceLayer.php | 123 | RuntimeException | Job not found | **MIGRATE** |
| JobChainExecutionCoordinator.php | 45 | InvalidArgumentException | Empty chain ID in constructor | **KEEP** |
| StepProgressTracker.php | 43 | RuntimeException | All steps completed | **MIGRATE** |

**Decision Rules**:
- **MIGRATE**: Business logic exceptions, state violations, not found errors
- **KEEP**: Constructor parameter validation, type validation, invalid arguments in constructors

### Identify Common Patterns

Group exceptions by scenario:
```
Entity Not Found: JobNotFoundException, ChainNotFoundException, WorkerNotFoundException
State Violations: InvalidChainStateException, AllStepsCompletedException
Infrastructure: RedisExtensionNotLoadedException
```

## Step 2: ErrorCode Definition

### Create Module ErrorCode Enum

**Location**: `src/Framework/Exception/Core/{Module}ErrorCode.php`

**Template**:
```php
<?php

declare(strict_types=1);

namespace App\Framework\Exception\Core;

enum YourModuleErrorCode: string implements ErrorCode
{
    // Entity not found errors (001-010)
    case ENTITY_NOT_FOUND = 'MODULE001';
    case RESOURCE_MISSING = 'MODULE002';

    // State violation errors (011-020)
    case INVALID_STATE = 'MODULE011';
    case OPERATION_NOT_ALLOWED = 'MODULE012';

    // Infrastructure errors (021-030)
    case DEPENDENCY_MISSING = 'MODULE021';
    case SERVICE_UNAVAILABLE = 'MODULE022';

    // Business logic errors (031-040)
    case VALIDATION_FAILED = 'MODULE031';
    case CONSTRAINT_VIOLATION = 'MODULE032';

    public function getValue(): string
    {
        return $this->value;
    }

    public function getCategory(): string
    {
        return 'MODULE';
    }

    public function getNumericCode(): int
    {
        return (int) substr($this->value, -3);
    }

    public function getSeverity(): ErrorSeverity
    {
        return match($this) {
            self::ENTITY_NOT_FOUND => ErrorSeverity::ERROR,
            self::RESOURCE_MISSING => ErrorSeverity::ERROR,
            self::INVALID_STATE => ErrorSeverity::WARNING,
            self::OPERATION_NOT_ALLOWED => ErrorSeverity::WARNING,
            self::DEPENDENCY_MISSING => ErrorSeverity::CRITICAL,
            self::SERVICE_UNAVAILABLE => ErrorSeverity::ERROR,
            self::VALIDATION_FAILED => ErrorSeverity::WARNING,
            self::CONSTRAINT_VIOLATION => ErrorSeverity::ERROR,
        };
    }

    public function getDescription(): string
    {
        return match($this) {
            self::ENTITY_NOT_FOUND => 'Requested entity not found',
            self::RESOURCE_MISSING => 'Required resource is missing',
            self::INVALID_STATE => 'Operation not allowed in current state',
            self::OPERATION_NOT_ALLOWED => 'Operation not permitted',
            self::DEPENDENCY_MISSING => 'Required dependency not available',
            self::SERVICE_UNAVAILABLE => 'Service temporarily unavailable',
            self::VALIDATION_FAILED => 'Input validation failed',
            self::CONSTRAINT_VIOLATION => 'Business constraint violated',
        };
    }

    public function getRecoveryHint(): string
    {
        return match($this) {
            self::ENTITY_NOT_FOUND => 'Verify the entity ID exists in the system',
            self::RESOURCE_MISSING => 'Check if required resources are configured',
            self::INVALID_STATE => 'Check entity state and ensure prerequisites are met',
            self::OPERATION_NOT_ALLOWED => 'Verify permissions and operation context',
            self::DEPENDENCY_MISSING => 'Install required dependency or use fallback',
            self::SERVICE_UNAVAILABLE => 'Retry operation after brief delay',
            self::VALIDATION_FAILED => 'Review input data and correct validation errors',
            self::CONSTRAINT_VIOLATION => 'Adjust operation to meet business constraints',
        };
    }

    public function isRecoverable(): bool
    {
        return match($this) {
            self::DEPENDENCY_MISSING => false,
            default => true,
        };
    }

    public function getRetryAfterSeconds(): ?int
    {
        return match($this) {
            self::SERVICE_UNAVAILABLE => 60,
            default => null,
        };
    }
}
```

**Real-World Example from Queue Module**:
```php
enum QueueErrorCode: string implements ErrorCode
{
    case JOB_NOT_FOUND = 'QUEUE007';
    case CHAIN_NOT_FOUND = 'QUEUE008';
    case INVALID_STATE = 'QUEUE009';
    case WORKER_UNAVAILABLE = 'QUEUE010';

    // ... implementations
}
```

### ErrorCode Naming Conventions

- **Category Prefix**: 2-4 uppercase letters (QUEUE, DB, AUTH, VAL)
- **Numeric Suffix**: 3 digits (001-999)
- **Severity Mapping**: CRITICAL for system failures, ERROR for operation failures, WARNING for state issues
- **Recovery**: true for temporary issues, false for permanent failures
- **Retry**: Set seconds only for infrastructure issues (DB connection, network, etc.)

## Step 3: Create Module Base Exception

**Location**: `src/Framework/{Module}/Exceptions/{Module}Exception.php`

**Template**:
```php
<?php

declare(strict_types=1);

namespace App\Framework\YourModule\Exceptions;

use App\Framework\Exception\FrameworkException;

/**
 * Base exception for YourModule
 *
 * All YourModule-related exceptions should extend this class.
 */
class YourModuleException extends FrameworkException
{
}
```

**Real-World Example from Queue Module**:
```php
<?php

declare(strict_types=1);

namespace App\Framework\Queue\Exceptions;

use App\Framework\Exception\FrameworkException;

class QueueException extends FrameworkException
{
}
```

**Why Important**:
- Single catch point for all module exceptions
- Type safety in exception handling
- Module-scoped exception hierarchy
- Framework compliance

## Step 4: Create Specific Exceptions

### Pattern 1: Entity Not Found Exception

**Before** (Legacy):
```php
// In UserRepository.php
public function find(UserId $id): User
{
    $user = $this->database->query(/* ... */);

    if ($user === null) {
        throw new \RuntimeException("User with ID {$id->toString()} not found");
    }

    return $user;
}
```

**After** (Framework-Compliant):
```php
// New file: src/Framework/YourModule/Exceptions/UserNotFoundException.php
<?php

declare(strict_types=1);

namespace App\Framework\YourModule\Exceptions;

use App\Framework\Exception\Core\YourModuleErrorCode;
use App\Framework\Exception\ExceptionContext;

final class UserNotFoundException extends YourModuleException
{
    public static function byId(UserId $id): self
    {
        $context = ExceptionContext::forOperation('user.lookup', 'UserRepository')
            ->withData([
                'user_id' => $id->toString(),
                'search_type' => 'by_id',
            ]);

        return self::create(
            YourModuleErrorCode::ENTITY_NOT_FOUND,
            "User with ID '{$id->toString()}' not found",
            $context
        );
    }

    public static function byEmail(Email $email): self
    {
        $context = ExceptionContext::forOperation('user.lookup', 'UserRepository')
            ->withData([
                'email' => $email->getMasked(),
                'search_type' => 'by_email',
            ]);

        return self::create(
            YourModuleErrorCode::ENTITY_NOT_FOUND,
            "User with email not found",
            $context
        );
    }
}
```

### Pattern 2: State Violation Exception

**Before** (Legacy):
```php
// In OrderProcessor.php
public function cancel(Order $order): void
{
    if ($order->status === OrderStatus::SHIPPED) {
        throw new \RuntimeException('Cannot cancel shipped order');
    }

    // Cancel logic
}
```

**After** (Framework-Compliant):
```php
// New file: src/Framework/YourModule/Exceptions/OrderAlreadyShippedException.php
<?php

declare(strict_types=1);

namespace App\Framework\YourModule\Exceptions;

use App\Framework\Exception\Core\YourModuleErrorCode;
use App\Framework\Exception\ExceptionContext;

final class OrderAlreadyShippedException extends YourModuleException
{
    public static function forOrder(string $orderId): self
    {
        $context = ExceptionContext::forOperation('order.cancel', 'OrderProcessor')
            ->withData([
                'order_id' => $orderId,
                'current_status' => 'shipped',
                'operation' => 'cancel',
            ]);

        return self::create(
            YourModuleErrorCode::INVALID_STATE,
            "Cannot cancel order '{$orderId}' - order already shipped",
            $context
        );
    }
}
```

### Pattern 3: Infrastructure Exception

**Before** (Legacy):
```php
// In ServiceInitializer.php
public function initialize(): Service
{
    if (!extension_loaded('redis')) {
        throw new \RuntimeException('Redis extension not loaded');
    }

    return new RedisService();
}
```

**After** (Framework-Compliant):
```php
// New file: src/Framework/YourModule/Exceptions/RedisExtensionNotLoadedException.php
<?php

declare(strict_types=1);

namespace App\Framework\YourModule\Exceptions;

use App\Framework\Exception\Core\YourModuleErrorCode;
use App\Framework\Exception\ExceptionContext;

final class RedisExtensionNotLoadedException extends YourModuleException
{
    public static function create(): self
    {
        $context = ExceptionContext::forOperation('service.init', 'ServiceInitializer')
            ->withData([
                'required_extension' => 'redis',
                'loaded_extensions' => get_loaded_extensions(),
                'fallback_available' => 'FileService',
            ]);

        return self::fromContext(
            'Redis PHP extension is not loaded',
            $context,
            YourModuleErrorCode::DEPENDENCY_MISSING
        );
    }
}
```

## Step 5: Service Migration

### Migration Checklist for Each File

- [ ] Identify all legacy exception throws
- [ ] Determine which exceptions to keep (constructor validation)
- [ ] Create or reuse specific exception classes
- [ ] Replace RuntimeException throws with specific exceptions
- [ ] Add use statements for new exception classes
- [ ] Update error messages to be more descriptive
- [ ] Add relevant context data
- [ ] Test each replaced exception

### Migration Example: Complete File

**Before** - `StepProgressTracker.php`:
```php
<?php

declare(strict_types=1);

namespace App\Framework\Queue\Services;

final readonly class StepProgressTracker
{
    public function __construct(
        private string $jobId,
        private array $steps,
        private JobProgressTrackerInterface $progressTracker
    ) {
        if (empty($this->jobId)) {
            throw new \InvalidArgumentException('Job ID cannot be empty');
        }

        if (empty($this->steps)) {
            throw new \InvalidArgumentException('Steps array cannot be empty');
        }
    }

    public function completeCurrentStep(): void
    {
        $progress = $this->progressTracker->getProgress($this->jobId);
        $currentStepIndex = $progress->current_step;

        if ($currentStepIndex >= count($this->steps)) {
            throw new \RuntimeException('All steps have already been completed');
        }

        // Complete step logic
    }

    public function updateStepProgress(int $percentage): void
    {
        $progress = $this->progressTracker->getProgress($this->jobId);
        $currentStepIndex = $progress->current_step;

        if ($currentStepIndex >= count($this->steps)) {
            throw new \RuntimeException('All steps have already been completed');
        }

        // Update logic
    }
}
```

**After** - `StepProgressTracker.php`:
```php
<?php

declare(strict_types=1);

namespace App\Framework\Queue\Services;

use App\Framework\Queue\Exceptions\AllStepsCompletedException;

final readonly class StepProgressTracker
{
    public function __construct(
        private string $jobId,
        private array $steps,
        private JobProgressTrackerInterface $progressTracker
    ) {
        // KEEP constructor validation - these are InvalidArgumentException
        if (empty($this->jobId)) {
            throw new \InvalidArgumentException('Job ID cannot be empty');
        }

        if (empty($this->steps)) {
            throw new \InvalidArgumentException('Steps array cannot be empty');
        }
    }

    public function completeCurrentStep(): void
    {
        $progress = $this->progressTracker->getProgress($this->jobId);
        $currentStepIndex = $progress->current_step;

        if ($currentStepIndex >= count($this->steps)) {
            // MIGRATED: Business logic exception
            throw AllStepsCompletedException::forJob($this->jobId, count($this->steps));
        }

        // Complete step logic
    }

    public function updateStepProgress(int $percentage): void
    {
        $progress = $this->progressTracker->getProgress($this->jobId);
        $currentStepIndex = $progress->current_step;

        if ($currentStepIndex >= count($this->steps)) {
            // MIGRATED: Business logic exception
            throw AllStepsCompletedException::forJob($this->jobId, count($this->steps));
        }

        // Update logic
    }
}
```

**Changes Made**:
1. Added `use App\Framework\Queue\Exceptions\AllStepsCompletedException;`
2. Kept constructor InvalidArgumentException throws (parameter validation)
3. Replaced both RuntimeException throws with `AllStepsCompletedException::forJob()`
4. More descriptive error messages via factory method
5. Rich context automatically added by exception

## Step 6: Testing & Validation

### Create Test File

**Location**: `tests/debug/test-{module}-exception-migration.php`

```php
<?php

declare(strict_types=1);

require_once __DIR__ . '/../../vendor/autoload.php';

use App\Framework\YourModule\Exceptions\EntityNotFoundException;
use App\Framework\YourModule\ValueObjects\EntityId;

echo "=== Testing YourModule Exception Migration ===\n\n";

// Test 1: Entity Not Found Exception
echo "Test 1: EntityNotFoundException\n";
echo str_repeat('-', 60) . "\n";

try {
    $entityId = EntityId::fromString('test-entity-123');
    throw EntityNotFoundException::byId($entityId);
} catch (EntityNotFoundException $e) {
    echo "Exception: " . get_class($e) . "\n";
    echo "Message: " . $e->getMessage() . "\n";
    echo "Error Code: " . $e->getErrorCode()->getValue() . "\n";
    echo "Category: " . $e->getErrorCode()->getCategory() . "\n";
    echo "Severity: " . $e->getErrorCode()->getSeverity()->value . "\n";
    echo "Is Recoverable: " . ($e->getErrorCode()->isRecoverable() ? 'Yes' : 'No') . "\n";
    echo "Recovery Hint: " . $e->getErrorCode()->getRecoveryHint() . "\n";
}

echo "\n" . str_repeat('=', 60) . "\n\n";

// Test 2: State Violation Exception
echo "Test 2: State Violation Exception\n";
echo str_repeat('-', 60) . "\n";

// ... test state violation

echo "\n✅ All migration tests completed!\n";
```

### Run Test Suite

```bash
# Run debug test
php tests/debug/test-yourmodule-exception-migration.php

# Run module test suite
./vendor/bin/pest tests/Unit/Framework/YourModule/

# Verify no regressions
./vendor/bin/pest
```

### Validate ErrorHandler Integration

```php
// Test ErrorHandler with new exceptions
use App\Framework\ErrorHandling\ErrorHandler;
use App\Framework\DI\DefaultContainer;
use App\Framework\Http\ResponseEmitter;
use App\Framework\Http\RequestIdGenerator;

$container = new DefaultContainer();
$emitter = new ResponseEmitter();
$requestIdGenerator = new RequestIdGenerator();

$errorHandler = new ErrorHandler(
    $emitter,
    $container,
    $requestIdGenerator,
    null,
    true // Debug mode to see recovery hints
);

try {
    throw EntityNotFoundException::byId($entityId);
} catch (EntityNotFoundException $e) {
    $response = $errorHandler->createHttpResponse($e);

    // Verify HTTP status based on category
    echo "HTTP Status: " . $response->status->value . "\n";

    // Verify metadata includes ErrorCode info
    // Check response body for recovery hints (debug mode)
}
```

## Step 7: Documentation Update

### Update Module Documentation

Add section to module's documentation:

```markdown
## Exception Handling

This module uses framework-compliant exception hierarchies.

**Module Exceptions**:
- `EntityNotFoundException` - Thrown when entity not found (ENTITY_NOT_FOUND)
- `InvalidStateException` - Thrown for state violations (INVALID_STATE)
- `DependencyMissingException` - Thrown for missing dependencies (DEPENDENCY_MISSING)

**Error Codes**:
- `MODULE001` - Entity not found (ERROR)
- `MODULE011` - Invalid state (WARNING)
- `MODULE021` - Dependency missing (CRITICAL)

**Usage Example**:
\`\`\`php
try {
    $entity = $this->repository->find($id);
} catch (EntityNotFoundException $e) {
    // Handle not found
    $this->logger->error('Entity not found', [
        'error_code' => $e->getErrorCode()->getValue(),
        'entity_id' => $id->toString()
    ]);

    return $this->notFoundResponse($e->getMessage());
}
\`\`\`
```

## Migration Strategy by Module Size

### Small Modules (<10 exceptions)
- **Approach**: Migrate all at once in single session
- **Time**: 1-2 hours
- **Steps**: All 7 steps in sequence
- **Example**: Vault module (4 exceptions)

### Medium Modules (10-30 exceptions)
- **Approach**: Migrate by service/component
- **Time**: 2-4 hours
- **Steps**: Multiple sessions, one component at a time
- **Example**: Queue module (22 exceptions)

### Large Modules (>30 exceptions)
- **Approach**: Migrate by subdomain or priority
- **Time**: 4-8 hours
- **Steps**: Multiple days, prioritize critical paths
- **Example**: Database module (38 exceptions)

## Common Pitfalls & Solutions

### Pitfall 1: Keeping vs. Migrating InvalidArgumentException

**Problem**: Unclear when to keep InvalidArgumentException

**Solution**:
```php
// ✅ KEEP - Constructor parameter validation
public function __construct(string $id)
{
    if (empty($id)) {
        throw new \InvalidArgumentException('ID cannot be empty');
    }
}

// ❌ MIGRATE - Business logic validation
public function process(Order $order): void
{
    if ($order->items->isEmpty()) {
        // Should be OrderEmptyException
        throw new \InvalidArgumentException('Order has no items');
    }
}
```

### Pitfall 2: Over-Generic Exception Messages

**Problem**: Messages too generic, not actionable

**Solution**:
```php
// ❌ Bad
throw new \RuntimeException('Operation failed');

// ✅ Good
throw OperationFailedException::forPayment(
    $paymentId,
    'Gateway timeout after 30s'
);
```

### Pitfall 3: Missing Context Data

**Problem**: Exception thrown without context

**Solution**:
```php
// ❌ Bad
throw EntityNotFoundException::byId($id);
// Context auto-generated but minimal

// ✅ Good
$context = ExceptionContext::forOperation('entity.delete', 'EntityService')
    ->withData([
        'entity_id' => $id->toString(),
        'entity_type' => 'Product',
        'operation' => 'delete',
        'user_id' => $currentUser->id,
    ])
    ->withDebug([
        'query' => $query,
        'execution_time_ms' => $executionTime,
    ]);

throw EntityNotFoundException::fromContext(
    "Product with ID '{$id->toString()}' not found",
    $context,
    YourModuleErrorCode::ENTITY_NOT_FOUND
);
```

### Pitfall 4: Wrong ErrorCode Severity

**Problem**: Severity doesn't match actual impact

**Solution**:
```php
// ❌ Bad - State violation marked as CRITICAL
ErrorSeverity::CRITICAL  // Will page ops at 3am

// ✅ Good - State violation is WARNING
ErrorSeverity::WARNING   // Logged but not paged
```

### Pitfall 5: Creating Too Many Exception Classes

**Problem**: One exception class per scenario

**Solution**:
```php
// ❌ Bad - Too many classes
UserNotFoundByIdException
UserNotFoundByEmailException
UserNotFoundByUsernameException

// ✅ Good - One class with factory methods
final class UserNotFoundException
{
    public static function byId(UserId $id): self { }
    public static function byEmail(Email $email): self { }
    public static function byUsername(string $username): self { }
}
```

## Real-World Migration: Queue Module

### Module Statistics
- **Files Analyzed**: 15 service files
- **Total Exceptions Found**: 22 exceptions
- **Exceptions Migrated**: 22 (100%)
- **Exception Classes Created**: 7
- **Error Codes Added**: 4
- **Services Migrated**: 6
- **Migration Time**: ~3 hours

### Exception Classes Created
1. `JobNotFoundException` - Job not found by ID or in queue
2. `ChainNotFoundException` - Chain not found by ID or name
3. `InvalidChainStateException` - Chain not in correct state
4. `CircularDependencyException` - Circular dependency detected
5. `AllStepsCompletedException` - All steps already completed
6. `RedisExtensionNotLoadedException` - Redis extension missing
7. `WorkerNotFoundException` - Worker not registered

### Error Codes Added
```php
enum QueueErrorCode: string implements ErrorCode
{
    case JOB_NOT_FOUND = 'QUEUE007';
    case CHAIN_NOT_FOUND = 'QUEUE008';
    case INVALID_STATE = 'QUEUE009';
    case WORKER_UNAVAILABLE = 'QUEUE010';
}
```

### Migration Order
1. **Day 1**: Analysis + ErrorCode definition (1h)
2. **Day 1**: Base exception + 3 entity not found exceptions (1h)
3. **Day 2**: State violation exceptions (0.5h)
4. **Day 2**: Service migrations (0.5h)
5. **Day 2**: Testing + validation (0.5h)

### Lessons Learned
- Start with most common exception patterns (not found, state violation)
- Migrate similar exceptions together (all not found, then all state)
- Test incrementally after each exception class created
- Constructor InvalidArgumentException are always kept
- Factory method naming: `byId()`, `forJob()`, `notPending()`

## Quick Reference Checklist

### Pre-Migration
- [ ] Read pattern guide and enhancement guide
- [ ] Analyze module and count exceptions
- [ ] Categorize exceptions (keep vs. migrate)
- [ ] Identify common patterns

### ErrorCode Creation
- [ ] Create `{Module}ErrorCode.php` in `Exception/Core/`
- [ ] Define all error codes with proper numbering
- [ ] Implement all required methods (getValue, getCategory, etc.)
- [ ] Set appropriate severity levels
- [ ] Add recovery hints for recoverable errors
- [ ] Set retry seconds for infrastructure errors

### Exception Classes
- [ ] Create `{Module}Exception.php` base class
- [ ] Create specific exception classes in `{Module}/Exceptions/`
- [ ] Use factory methods instead of constructors
- [ ] Build rich ExceptionContext
- [ ] Choose appropriate ErrorCode

### Service Migration
- [ ] Keep constructor InvalidArgumentException
- [ ] Replace business logic RuntimeException
- [ ] Add use statements for new exceptions
- [ ] Improve error messages
- [ ] Add context data

### Testing
- [ ] Create debug test file
- [ ] Run module test suite
- [ ] Verify ErrorHandler integration
- [ ] Check HTTP status codes
- [ ] Validate recovery hints (debug mode)

### Documentation
- [ ] Update module documentation
- [ ] Add exception handling section
- [ ] Document error codes
- [ ] Provide usage examples

## Next Steps After Migration

Once your module is migrated:

1. **Monitor Production**: Watch for any regression issues
2. **Collect Feedback**: Get team feedback on error clarity
3. **Iterate**: Refine error messages based on feedback
4. **Share Learnings**: Update this guide with lessons learned
5. **Help Others**: Assist with other module migrations

## Support & Resources

- **Pattern Guide**: [exception-hierarchy-pattern.md](./exception-hierarchy-pattern.md)
- **Enhancement Guide**: [error-handler-enhancements.md](./error-handler-enhancements.md)
- **ErrorCode Reference**: See `src/Framework/Exception/Core/` directory
- **Example Module**: Queue module (`src/Framework/Queue/`)

## Framework Exception Migrations (Completed)

The framework's core exceptions have been successfully migrated to the new exception hierarchy system.

### Migration Summary

**Total Time**: ~6 hours across multiple phases
**Exception Classes Migrated**: 40+ framework exceptions
**ErrorCode Enums Created**: 5 category-specific enums
**Test Coverage**: 100% integration test pass rate

### Phase 1: ErrorCode Enum Creation

**Created Category-Specific Error Code Enums**:

1. **DatabaseErrorCode** (`src/Framework/Exception/Core/DatabaseErrorCode.php`)
   - 8 cases covering connection, query, transaction, and schema errors
   - Severity: CONNECTION_FAILED (CRITICAL), QUERY_FAILED (ERROR), etc.
   - Recovery: Retry logic for temporary issues, non-recoverable for constraint violations

2. **AuthErrorCode** (`src/Framework/Exception/Core/AuthErrorCode.php`)
   - 10 cases covering authentication and authorization
   - Severity: TOKEN_EXPIRED (WARNING), ACCOUNT_LOCKED (ERROR), etc.
   - Recovery: Most auth errors are non-recoverable, require user action

3. **HttpErrorCode** (`src/Framework/Exception/Core/HttpErrorCode.php`)
   - 8 cases covering HTTP protocol errors
   - Severity: BAD_REQUEST (WARNING), INTERNAL_SERVER_ERROR (CRITICAL)
   - Recovery: Client errors non-recoverable, server errors may retry

4. **SecurityErrorCode** (`src/Framework/Exception/Core/SecurityErrorCode.php`)
   - 10 cases covering security threats and attacks
   - Severity: SQL_INJECTION_DETECTED (CRITICAL), CSRF_TOKEN_INVALID (ERROR)
   - Recovery: Security violations are never recoverable

5. **ValidationErrorCode** (`src/Framework/Exception/Core/ValidationErrorCode.php`)
   - 8 cases covering input and business validation
   - Severity: INVALID_INPUT (WARNING), BUSINESS_RULE_VIOLATION (ERROR)
   - Recovery: Validation errors recoverable with corrected input

### Phase 2: Database Exception Migration

**Migrated Exceptions**:
- `DatabaseConnectionException` → DatabaseErrorCode::CONNECTION_FAILED
- `QueryExecutionException` → DatabaseErrorCode::QUERY_FAILED
- `TransactionException` → DatabaseErrorCode::TRANSACTION_FAILED
- `EntityNotFoundException` → DatabaseErrorCode::ENTITY_NOT_FOUND
- `ConstraintViolationException` → DatabaseErrorCode::CONSTRAINT_VIOLATION

**Example Migration**:
```php
// Before
throw new \RuntimeException("Database connection failed");

// After
throw DatabaseConnectionException::connectionRefused(
    $host,
    $port,
    $previous
);
// Uses DatabaseErrorCode::CONNECTION_FAILED with rich context
```

### Phase 3.2: Authentication/Authorization Exception Migration

**Migrated Exceptions**:
- `InvalidCredentialsException` → AuthErrorCode::CREDENTIALS_INVALID
- `TokenExpiredException` → AuthErrorCode::TOKEN_EXPIRED
- `SessionExpiredException` → AuthErrorCode::SESSION_EXPIRED
- `AccountLockedException` → AuthErrorCode::ACCOUNT_LOCKED
- `InsufficientPermissionsException` → AuthErrorCode::INSUFFICIENT_PERMISSIONS

**Example Migration**:
```php
// Before
throw new \RuntimeException("Invalid credentials");

// After
throw InvalidCredentialsException::forUser($username);
// Uses AuthErrorCode::CREDENTIALS_INVALID with security context
```

### Phase 3.3: HTTP Exception Migration

**Migrated Exceptions**:
- `BadRequestException` → HttpErrorCode::BAD_REQUEST
- `NotFoundException` → HttpErrorCode::NOT_FOUND
- `MethodNotAllowedException` → HttpErrorCode::METHOD_NOT_ALLOWED
- `InternalServerErrorException` → HttpErrorCode::INTERNAL_SERVER_ERROR

**Example Migration**:
```php
// Before
throw new \RuntimeException("Resource not found");

// After
throw NotFoundException::forResource($resourceType, $resourceId);
// Uses HttpErrorCode::NOT_FOUND with HTTP context
```

### Phase 3.4: Security Exception Migration

**Migrated Exceptions** (OWASP-Compliant):
- `CsrfValidationFailedException` → SecurityErrorCode::CSRF_TOKEN_INVALID
- `SqlInjectionAttemptException` → SecurityErrorCode::SQL_INJECTION_DETECTED
- `XssAttemptException` → SecurityErrorCode::XSS_DETECTED
- `PathTraversalAttemptException` → SecurityErrorCode::PATH_TRAVERSAL_DETECTED

**Example Migration**:
```php
// Before
throw new \RuntimeException("CSRF token validation failed");

// After
throw CsrfValidationFailedException::tokenValidationFailed($formId);
// Uses SecurityErrorCode::CSRF_TOKEN_INVALID with security event logging
```

**Security Features**:
- Automatic OWASP security event logging
- Attack pattern analysis (SQL injection types, XSS vectors)
- IOC (Indicator of Compromise) generation
- WAF rule suggestions
- Threat severity assessment

### Phase 3.5: Validation Exception Migration

**Result**: No validation exceptions found using old ErrorCode constants.

All validation exceptions in the framework already use the new ValidationErrorCode enum pattern.

### Migration Benefits Achieved

**1. Type Safety**
```php
// Before: Generic catch
catch (\RuntimeException $e) { }

// After: Specific exception handling
catch (DatabaseConnectionException $e) {
    // Handle database connection specifically
    $this->fallbackToCache();
}
```

**2. Rich Error Context**
```php
// Automatic context includes:
[
    'operation' => 'user.login',
    'component' => 'AuthService',
    'error_code' => 'AUTH002',
    'category' => 'AUTH',
    'severity' => 'ERROR',
    'timestamp' => '2024-01-15 14:32:10',
    'request_id' => 'req_abc123',
    // + custom data
]
```

**3. Category-Based Monitoring**
```php
// Monitor by category
if ($exception->isCategory('DB')) {
    $this->metricsCollector->incrementDatabaseErrors();
}

// Check specific error code
if ($exception->isErrorCode(DatabaseErrorCode::CONNECTION_FAILED)) {
    $this->alertOps('Database connection down');
}
```

**4. Automatic HTTP Status Mapping**
```php
// ErrorHandler automatically maps:
DatabaseErrorCode::ENTITY_NOT_FOUND → 404 Not Found
AuthErrorCode::CREDENTIALS_INVALID → 401 Unauthorized
HttpErrorCode::BAD_REQUEST → 400 Bad Request
SecurityErrorCode::CSRF_TOKEN_INVALID → 403 Forbidden
ValidationErrorCode::INVALID_INPUT → 422 Unprocessable Entity
```

**5. Recovery Hints**
```php
// ErrorCode provides recovery guidance
$errorCode->getRecoveryHint();
// "Check database connection and retry operation"

$errorCode->isRecoverable(); // true/false
$errorCode->getRetryAfterSeconds(); // 60 for temporary issues
```

### Testing Results

**Integration Tests**: 100% Pass Rate
```bash
./vendor/bin/pest tests/Unit/ErrorHandling/ErrorHandlerFullPipelineTest.php

✓ it handles database connection exceptions correctly
✓ it handles authentication exceptions with proper status codes
✓ it handles HTTP exceptions with correct status mapping
✓ it handles security exceptions with OWASP event logging
✓ it provides recovery hints in debug mode

5 tests passed, 26 assertions
```

### Next Steps

**Recommended Migrations** (Optional):
1. **Domain Module Exceptions**: Migrate domain-specific exceptions (User, Order, etc.)
2. **Infrastructure Exceptions**: Migrate external service exceptions (Email, Storage, etc.)
3. **Application Exceptions**: Migrate controller and API exceptions

**Maintenance**:
- Monitor error logs for any regressions
- Update error messages based on user feedback
- Add new ErrorCode cases as needed for new features
- Keep exception tests up to date

## Conclusion

Exception migration improves:
- ✅ **Error Clarity**: Descriptive messages with context
- ✅ **Type Safety**: Catch specific exceptions, not generic ones
- ✅ **Debugging**: Rich context for troubleshooting
- ✅ **Monitoring**: Category-based alerting and metrics
- ✅ **Recovery**: Automatic retry strategies and hints
- ✅ **Documentation**: Self-documenting exception hierarchy
- ✅ **HTTP Status**: Automatic status code mapping
- ✅ **Logging**: Severity-based log levels

Take your time, test thoroughly, and don't hesitate to refactor as you learn better patterns!
