import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { PrismaService } from '../prisma/prisma.service'; import { CreateFlightDto, UpdateFlightDto } from './dto'; import { firstValueFrom } from 'rxjs'; import { convertOptionalDates } from '../common/utils/date.utils'; @Injectable() export class FlightsService { private readonly logger = new Logger(FlightsService.name); private readonly apiKey: string; private readonly baseUrl = 'http://api.aviationstack.com/v1'; constructor( private prisma: PrismaService, private httpService: HttpService, private configService: ConfigService, ) { this.apiKey = this.configService.get('AVIATIONSTACK_API_KEY') || ''; } async create(createFlightDto: CreateFlightDto) { this.logger.log( `Creating flight: ${createFlightDto.flightNumber} for VIP ${createFlightDto.vipId}`, ); const data = convertOptionalDates( { ...createFlightDto, flightDate: new Date(createFlightDto.flightDate), }, ['scheduledDeparture', 'scheduledArrival'], ); return this.prisma.flight.create({ data, include: { vip: true }, }); } async findAll() { return this.prisma.flight.findMany({ include: { vip: true }, orderBy: { flightDate: 'desc' }, }); } async findByVip(vipId: string) { return this.prisma.flight.findMany({ where: { vipId }, orderBy: [{ flightDate: 'asc' }, { segment: 'asc' }], }); } async findOne(id: string) { const flight = await this.prisma.flight.findUnique({ where: { id }, include: { vip: true }, }); if (!flight) { throw new NotFoundException(`Flight with ID ${id} not found`); } return flight; } async update(id: string, updateFlightDto: UpdateFlightDto) { const flight = await this.findOne(id); this.logger.log(`Updating flight ${id}: ${flight.flightNumber}`); const updateData = convertOptionalDates(updateFlightDto, [ 'flightDate', 'scheduledDeparture', 'scheduledArrival', 'actualDeparture', 'actualArrival', ]); return this.prisma.flight.update({ where: { id: flight.id }, data: updateData, include: { vip: true }, }); } async remove(id: string, hardDelete = false) { const flight = await this.findOne(id); this.logger.log(`Deleting flight: ${flight.flightNumber}`); // Flights are always hard deleted (no soft delete for flights) return this.prisma.flight.delete({ where: { id: flight.id }, }); } /** * Fetch real-time flight status from AviationStack API */ async getFlightStatus(flightNumber: string, flightDate?: string) { if (!this.apiKey) { this.logger.warn('AviationStack API key not configured'); return { message: 'Flight tracking API not configured', flightNumber, }; } try { const params: any = { access_key: this.apiKey, flight_iata: flightNumber, }; if (flightDate) { params.flight_date = flightDate; } const response = await firstValueFrom( this.httpService.get(`${this.baseUrl}/flights`, { params }), ); const data = response.data as any; if (data && data.data && data.data.length > 0) { const flightData = data.data[0]; return { flightNumber: flightData.flight.iata, status: flightData.flight_status, departure: { airport: flightData.departure.iata, scheduled: flightData.departure.scheduled, actual: flightData.departure.actual, }, arrival: { airport: flightData.arrival.iata, scheduled: flightData.arrival.scheduled, estimated: flightData.arrival.estimated, actual: flightData.arrival.actual, }, }; } return { message: 'Flight not found', flightNumber, }; } catch (error) { this.logger.error( `Failed to fetch flight status: ${error.message}`, error.stack, ); throw error; } } }