# Phase 3: API Migration

## Overview

This phase updates the vehicles API controller to use the new MVP Platform database for all dropdown endpoints while maintaining exact API compatibility. All existing response formats and authentication patterns are preserved.

## Prerequisites

- Phase 2 backend migration completed successfully
- VIN decoder service functional
- MVP Platform repository working correctly
- Backend service can query MVP Platform database
- All TypeScript compilation successful

## Current API Endpoints to Update

**Existing endpoints that will be updated**:
- `GET /api/vehicles/dropdown/makes` (unauthenticated)
- `GET /api/vehicles/dropdown/models/:make` (unauthenticated)
- `GET /api/vehicles/dropdown/transmissions` (unauthenticated)
- `GET /api/vehicles/dropdown/engines` (unauthenticated)  
- `GET /api/vehicles/dropdown/trims` (unauthenticated)

**Existing endpoints that remain unchanged**:
- `POST /api/vehicles` (authenticated - uses VIN decoder)
- `GET /api/vehicles` (authenticated)
- `GET /api/vehicles/:id` (authenticated)
- `PUT /api/vehicles/:id` (authenticated)
- `DELETE /api/vehicles/:id` (authenticated)

## Tasks

### Task 3.1: Update Vehicles Controller

**Location**: `backend/src/features/vehicles/api/vehicles.controller.ts`

**Action**: Replace external API dropdown methods with MVP Platform database calls:

```typescript
// UPDATE imports - REMOVE:
// import { vpicClient } from '../external/vpic/vpic.client';

// ADD new imports:
import { VehiclesService } from '../domain/vehicles.service';

export class VehiclesController {
  private vehiclesService: VehiclesService;

  constructor() {
    this.vehiclesService = new VehiclesService();
  }

  // UPDATE existing dropdown methods:

  async getDropdownMakes(request: FastifyRequest, reply: FastifyReply) {
    try {
      logger.info('Getting dropdown makes from MVP Platform');
      const makes = await this.vehiclesService.getDropdownMakes();
      
      // Maintain exact same response format
      const response = makes.map(make => ({
        Make_ID: make.id,
        Make_Name: make.name
      }));

      reply.status(200).send(response);
    } catch (error) {
      logger.error('Get dropdown makes failed', { error });
      reply.status(500).send({ error: 'Failed to retrieve makes' });
    }
  }

  async getDropdownModels(request: FastifyRequest<{ Params: { make: string } }>, reply: FastifyReply) {
    try {
      const { make } = request.params;
      logger.info('Getting dropdown models from MVP Platform', { make });
      
      const models = await this.vehiclesService.getDropdownModels(make);
      
      // Maintain exact same response format
      const response = models.map(model => ({
        Model_ID: model.id,
        Model_Name: model.name
      }));

      reply.status(200).send(response);
    } catch (error) {
      logger.error('Get dropdown models failed', { error });
      reply.status(500).send({ error: 'Failed to retrieve models' });
    }
  }

  async getDropdownTransmissions(request: FastifyRequest, reply: FastifyReply) {
    try {
      logger.info('Getting dropdown transmissions from MVP Platform');
      const transmissions = await this.vehiclesService.getDropdownTransmissions();
      
      // Maintain exact same response format
      const response = transmissions.map(transmission => ({
        Name: transmission.name
      }));

      reply.status(200).send(response);
    } catch (error) {
      logger.error('Get dropdown transmissions failed', { error });
      reply.status(500).send({ error: 'Failed to retrieve transmissions' });
    }
  }

  async getDropdownEngines(request: FastifyRequest, reply: FastifyReply) {
    try {
      logger.info('Getting dropdown engines from MVP Platform');
      const engines = await this.vehiclesService.getDropdownEngines();
      
      // Maintain exact same response format
      const response = engines.map(engine => ({
        Name: engine.name
      }));

      reply.status(200).send(response);
    } catch (error) {
      logger.error('Get dropdown engines failed', { error });
      reply.status(500).send({ error: 'Failed to retrieve engines' });
    }
  }

  async getDropdownTrims(request: FastifyRequest, reply: FastifyReply) {
    try {
      logger.info('Getting dropdown trims from MVP Platform');
      const trims = await this.vehiclesService.getDropdownTrims();
      
      // Maintain exact same response format  
      const response = trims.map(trim => ({
        Name: trim.name
      }));

      reply.status(200).send(response);
    } catch (error) {
      logger.error('Get dropdown trims failed', { error });
      reply.status(500).send({ error: 'Failed to retrieve trims' });
    }
  }

  // All other methods remain unchanged (createVehicle, getUserVehicles, etc.)
}
```

### Task 3.2: Verify Routes Configuration

**Location**: `backend/src/features/vehicles/api/vehicles.routes.ts`

**Action**: Ensure dropdown routes remain unauthenticated (no changes needed, just verification):

```typescript
// VERIFY these routes remain unauthenticated:
fastify.get('/vehicles/dropdown/makes', {
  handler: vehiclesController.getDropdownMakes.bind(vehiclesController)
});

fastify.get<{ Params: { make: string } }>('/vehicles/dropdown/models/:make', {
  handler: vehiclesController.getDropdownModels.bind(vehiclesController)
});

fastify.get('/vehicles/dropdown/transmissions', {
  handler: vehiclesController.getDropdownTransmissions.bind(vehiclesController)
});

fastify.get('/vehicles/dropdown/engines', {
  handler: vehiclesController.getDropdownEngines.bind(vehiclesController)
});

fastify.get('/vehicles/dropdown/trims', {
  handler: vehiclesController.getDropdownTrims.bind(vehiclesController)
});
```

**Note**: These routes should NOT have `preHandler: fastify.authenticate` to maintain unauthenticated access as required by security.md.

### Task 3.3: Update Response Error Handling

**Action**: Add specific error handling for database connectivity issues:

```typescript
// Add to VehiclesController class:

private handleDatabaseError(error: any, operation: string, reply: FastifyReply) {
  logger.error(`${operation} database error`, { error });
  
  // Check for specific database connection errors
  if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
    reply.status(503).send({ 
      error: 'Service temporarily unavailable',
      message: 'Database connection issue'
    });
    return;
  }
  
  // Generic database error
  if (error.code && error.code.startsWith('P')) { // PostgreSQL error codes
    reply.status(500).send({
      error: 'Database query failed',
      message: 'Please try again later'
    });
    return;
  }
  
  // Generic error
  reply.status(500).send({
    error: `Failed to ${operation}`,
    message: 'Internal server error'
  });
}

// Update all dropdown methods to use this error handler:
// Replace each catch block with:
} catch (error) {
  this.handleDatabaseError(error, 'retrieve makes', reply);
}
```

### Task 3.4: Add Performance Monitoring

**Action**: Add response time logging for performance monitoring:

```typescript
// Add to VehiclesController class:

private async measurePerformance<T>(
  operation: string,
  fn: () => Promise<T>
): Promise<T> {
  const startTime = Date.now();
  try {
    const result = await fn();
    const duration = Date.now() - startTime;
    logger.info(`MVP Platform ${operation} completed`, { duration });
    return result;
  } catch (error) {
    const duration = Date.now() - startTime;
    logger.error(`MVP Platform ${operation} failed`, { duration, error });
    throw error;
  }
}

// Update dropdown methods to use performance monitoring:
async getDropdownMakes(request: FastifyRequest, reply: FastifyReply) {
  try {
    logger.info('Getting dropdown makes from MVP Platform');
    const makes = await this.measurePerformance('makes query', () =>
      this.vehiclesService.getDropdownMakes()
    );
    
    // ... rest of method unchanged
  } catch (error) {
    this.handleDatabaseError(error, 'retrieve makes', reply);
  }
}
```

### Task 3.5: Update Health Check

**Location**: `backend/src/features/vehicles/api/vehicles.controller.ts`

**Action**: Add MVP Platform database health check method:

```typescript
// Add new health check method:
async healthCheck(request: FastifyRequest, reply: FastifyReply) {
  try {
    // Test MVP Platform database connection
    await this.measurePerformance('health check', async () => {
      const testResult = await this.vehiclesService.testMvpPlatformConnection();
      if (!testResult) {
        throw new Error('MVP Platform database connection failed');
      }
    });
    
    reply.status(200).send({ 
      status: 'healthy',
      mvpPlatform: 'connected',
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    logger.error('Health check failed', { error });
    reply.status(503).send({
      status: 'unhealthy', 
      error: error.message,
      timestamp: new Date().toISOString()
    });
  }
}
```

**Location**: `backend/src/features/vehicles/domain/vehicles.service.ts`

**Action**: Add health check method to service:

```typescript
// Add to VehiclesService class:
async testMvpPlatformConnection(): Promise<boolean> {
  try {
    await mvpPlatformRepository.getMakes();
    return true;
  } catch (error) {
    logger.error('MVP Platform connection test failed', { error });
    return false;
  }
}
```

### Task 3.6: Update Route Registration for Health Check

**Location**: `backend/src/features/vehicles/api/vehicles.routes.ts`

**Action**: Add health check route:

```typescript
// Add health check route (unauthenticated for monitoring):
fastify.get('/vehicles/health', {
  handler: vehiclesController.healthCheck.bind(vehiclesController)
});
```

## Validation Steps

### Step 1: Test API Response Formats

```bash
# Test makes endpoint
curl -s http://localhost:3001/api/vehicles/dropdown/makes | jq '.[0]'
# Should return: {"Make_ID": number, "Make_Name": "string"}

# Test models endpoint  
curl -s "http://localhost:3001/api/vehicles/dropdown/models/Honda" | jq '.[0]'
# Should return: {"Model_ID": number, "Model_Name": "string"}

# Test transmissions endpoint
curl -s http://localhost:3001/api/vehicles/dropdown/transmissions | jq '.[0]'
# Should return: {"Name": "string"}
```

### Step 2: Test Performance

```bash
# Test response times (should be < 100ms)
time curl -s http://localhost:3001/api/vehicles/dropdown/makes > /dev/null

# Load test with multiple concurrent requests
for i in {1..10}; do
  curl -s http://localhost:3001/api/vehicles/dropdown/makes > /dev/null &
done
wait
```

### Step 3: Test Error Handling

```bash
# Test with invalid make name
curl -s "http://localhost:3001/api/vehicles/dropdown/models/InvalidMake" | jq '.'
# Should return empty array or appropriate error

# Test health check
curl -s http://localhost:3001/api/vehicles/health | jq '.'
# Should return: {"status": "healthy", "mvpPlatform": "connected", "timestamp": "..."}
```

### Step 4: Verify Authentication Patterns

```bash
# Test that dropdown endpoints are unauthenticated (should work without token)
curl -s http://localhost:3001/api/vehicles/dropdown/makes | jq '. | length'
# Should return number > 0

# Test that vehicle CRUD endpoints still require authentication  
curl -s http://localhost:3001/api/vehicles
# Should return 401 Unauthorized
```

## Error Handling

### Common Issues and Solutions

**Issue**: Empty response arrays
**Solution**: Check MVP Platform database has data, verify SQL queries, check table names

**Issue**: Slow response times (> 100ms)
**Solution**: Add database indexes, optimize queries, check connection pool settings

**Issue**: Authentication errors on dropdown endpoints
**Solution**: Verify routes don't have authentication middleware, check security.md compliance

**Issue**: Wrong response format
**Solution**: Compare with original vPIC API responses, adjust mapping in controller

### Rollback Procedure

1. Revert vehicles.controller.ts:
   ```bash
   git checkout HEAD -- backend/src/features/vehicles/api/vehicles.controller.ts
   ```

2. Revert vehicles.routes.ts if modified:
   ```bash
   git checkout HEAD -- backend/src/features/vehicles/api/vehicles.routes.ts
   ```

3. Restart backend service:
   ```bash
   docker-compose restart backend
   ```

## Next Steps

After successful completion of Phase 3:

1. Proceed to [Phase 4: Scheduled ETL](./phase-04-scheduled-etl.md)
2. Monitor API response times in production
3. Set up alerts for health check failures

## Dependencies for Next Phase

- All dropdown APIs returning correct data
- Response times consistently under 100ms
- Health check endpoint functional
- No authentication issues with dropdown endpoints
- Error handling working properly