/** * Utility functions for age-based fare calculations * Implements the "first child free" pricing policy */ export interface PassengerWithAge { dateOfBirth?: string; name?: string; } /** * Calculate age from date of birth */ export function calculateAge(dateOfBirth: string | Date): number { const today = new Date(); const birth = new Date(dateOfBirth); let age = today.getFullYear() - birth.getFullYear(); const monthDiff = today.getMonth() - birth.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) { age--; } return Math.max(0, age); } /** * Check if passenger is a child (under 5 years old) */ export function isChild(passenger: PassengerWithAge): boolean { if (!passenger.dateOfBirth) return false; return calculateAge(passenger.dateOfBirth) < 5; } /** * Check if this child gets a free fare (1 free child per adult) */ export function isFirstChild(passengers: PassengerWithAge[], currentIndex: number): boolean { const currentPassenger = passengers[currentIndex]; if (!isChild(currentPassenger)) return false; const adultCount = passengers.filter(p => !isChild(p)).length; const childrenBefore = passengers.slice(0, currentIndex).filter(p => isChild(p)); return childrenBefore.length < adultCount; } /** * Calculate fare for a single passenger with first child free logic */ export function calculatePassengerFare( passengers: PassengerWithAge[], passengerIndex: number, baseFare: number ): number { const passenger = passengers[passengerIndex]; if (isChild(passenger) && isFirstChild(passengers, passengerIndex)) { return 0; // First child travels free } return baseFare; } /** * Calculate total fare for all passengers with first child free logic */ export function calculateTotalFare( passengers: PassengerWithAge[], baseFare: number ): number { return passengers.reduce((total, _, index) => { return total + calculatePassengerFare(passengers, index, baseFare); }, 0); } /** * Get passenger category for display purposes */ export function getPassengerCategory(passenger: PassengerWithAge): 'ADULT' | 'CHILD' { return isChild(passenger) ? 'CHILD' : 'ADULT'; } /** * Format fare amount for display */ export function formatFare(amountMinor: number, currency: string = 'ETB'): string { return `${currency} ${(amountMinor / 100).toFixed(2)}`; } /** * Get pricing summary for a list of passengers */ export function getPricingSummary(passengers: PassengerWithAge[], baseFare: number) { const adults = passengers.filter(p => !isChild(p)); const children = passengers.filter(p => isChild(p)); const freeChildren = Math.min(children.length, adults.length); const paidChildren = Math.max(0, children.length - adults.length); return { adultCount: adults.length, childCount: children.length, freeChildrenCount: freeChildren, paidChildrenCount: paidChildren, adultFare: adults.length * baseFare, paidChildFare: paidChildren * baseFare, totalFare: calculateTotalFare(passengers, baseFare) }; }