import { Controller, Post, Get, Body, Query, Param, UseGuards, HttpException, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiQuery, ApiParam, ApiSecurity } from '@nestjs/swagger'; import { TelemetryService } from './telemetry.service'; import { DriverTelemetryDto, DriverTelemetryBatchDto } from './dto/driver-telemetry.dto'; import { ApiKeyGuard } from '../common/guards/api-key.guard'; @ApiTags('telemetry') @ApiSecurity('x-api-key') @Controller('telemetry') @UseGuards(ApiKeyGuard) export class TelemetryController { constructor(private readonly telemetryService: TelemetryService) {} @Post() @ApiOperation({ summary: 'Ingest real-time driver telemetry with elevation & distance ๐Ÿ“กโ›ฐ๏ธ', description: 'Receives GPS position, speed, heading, distance, and AMSL elevation from driver app.', }) async ingest(@Body() data: DriverTelemetryDto) { if (!data.driver_id) { throw new HttpException('Missing driver_id', HttpStatus.BAD_REQUEST); } return this.telemetryService.ingest(data); } @Post('batch') @ApiOperation({ summary: 'Queue navigation telemetry batch ๐Ÿ“ฆ', description: 'Acknowledges a navigation batch immediately; the server persists it from Redis in the background.', }) async ingestBatch(@Body() body: DriverTelemetryBatchDto) { if (!body || !Array.isArray(body.points)) { throw new HttpException('Invalid payload: expected { points: [...] }', HttpStatus.BAD_REQUEST); } if (body.points.length > 100) { throw new HttpException('A telemetry batch may contain at most 100 points', HttpStatus.BAD_REQUEST); } return this.telemetryService.enqueueBatch(body.points); } @Get('nearby') @ApiOperation({ summary: 'Query active drivers within spatial radius with elevation & bearing ๐Ÿš—' }) @ApiQuery({ name: 'lat', required: true, type: Number, description: 'Center latitude' }) @ApiQuery({ name: 'lng', required: true, type: Number, description: 'Center longitude' }) @ApiQuery({ name: 'radius', required: false, type: Number, description: 'Radius in meters (default: 5000m)' }) async getNearby( @Query('lat') lat: number, @Query('lng') lng: number, @Query('radius') radius?: number, ) { const latNum = Number(lat); const lngNum = Number(lng); if (isNaN(latNum) || isNaN(lngNum)) { throw new HttpException('lat and lng must be valid numbers', HttpStatus.BAD_REQUEST); } return this.telemetryService.getRecentDrivers(latNum, lngNum, radius ? Number(radius) : 5000); } @Get('driver/:driverId/profile') @ApiOperation({ summary: 'Get 3D elevation profile, climb, and terrain grade for a driver ๐Ÿ“ˆ' }) @ApiParam({ name: 'driverId', required: true, description: 'Driver unique ID' }) @ApiQuery({ name: 'hours', required: false, description: 'Window in hours (default: 24)' }) async getDriverElevationProfile( @Param('driverId') driverId: string, @Query('hours') hours?: number, ) { return this.telemetryService.getElevationProfile(driverId, hours ? Number(hours) : 24); } }