import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between } from 'typeorm';
import { Activity, ActivityType } from '../../database/entities/activity.entity';
import { Child } from '../../database/entities/child.entity';

/**
 * Growth Percentile Service
 *
 * Implements WHO and CDC growth charts for children 0-6 years
 * Calculates percentiles, z-scores, and growth velocity
 * Detects growth concerns and provides recommendations
 */

export interface GrowthMeasurement {
  date: Date;
  weight?: number; // kg
  height?: number; // cm
  headCircumference?: number; // cm
}

export interface GrowthPercentile {
  weight?: PercentileData;
  height?: PercentileData;
  headCircumference?: PercentileData;
  bmi?: PercentileData;
  weightForLength?: PercentileData;
}

export interface PercentileData {
  value: number;
  percentile: number;
  zScore: number;
  interpretation: 'underweight' | 'normal' | 'overweight' | 'obese' | 'short' | 'tall';
  ageInMonths: number;
}

export interface GrowthVelocity {
  weightVelocity?: VelocityData;
  heightVelocity?: VelocityData;
  interpretation: string;
  concerns: string[];
}

export interface VelocityData {
  value: number; // kg/month or cm/month
  percentile: number;
  isNormal: boolean;
  trend: 'accelerating' | 'steady' | 'decelerating';
}

export interface GrowthAnalysis {
  currentPercentiles: GrowthPercentile;
  growthVelocity: GrowthVelocity;
  growthCurve: GrowthCurveData;
  projections: GrowthProjection;
  recommendations: string[];
  alerts: GrowthAlert[];
}

export interface GrowthCurveData {
  measurements: GrowthMeasurement[];
  percentileCurves: {
    p3: number[];
    p15: number[];
    p50: number[];
    p85: number[];
    p97: number[];
  };
  childCurve: number[];
}

export interface GrowthProjection {
  projectedWeight: { value: number; date: Date; confidence: number };
  projectedHeight: { value: number; date: Date; confidence: number };
  expectedAdultHeight?: number; // Using mid-parental height method
}

export interface GrowthAlert {
  type: 'crossing_percentiles' | 'stunting' | 'wasting' | 'obesity_risk' | 'failure_to_thrive';
  severity: 'info' | 'warning' | 'critical';
  message: string;
  recommendation: string;
}

// WHO Growth Standard Data (simplified - in production, use complete tables)
const WHO_WEIGHT_FOR_AGE_BOYS = {
  // Age in months -> [P3, P15, P50, P85, P97] in kg
  0: [2.5, 2.9, 3.3, 3.9, 4.4],
  1: [3.4, 3.9, 4.5, 5.1, 5.8],
  2: [4.3, 4.9, 5.6, 6.3, 7.1],
  3: [5.0, 5.7, 6.4, 7.2, 8.0],
  6: [6.4, 7.1, 7.9, 8.8, 9.8],
  9: [7.2, 8.0, 8.9, 9.9, 11.0],
  12: [7.7, 8.5, 9.6, 10.8, 12.0],
  18: [8.8, 9.8, 11.0, 12.5, 13.7],
  24: [9.7, 10.8, 12.2, 13.6, 15.3],
  36: [11.3, 12.7, 14.3, 16.2, 18.3],
  48: [12.7, 14.3, 16.3, 18.5, 21.2],
  60: [14.1, 15.9, 18.3, 21.0, 24.2],
};

const WHO_WEIGHT_FOR_AGE_GIRLS = {
  0: [2.4, 2.8, 3.2, 3.7, 4.2],
  1: [3.2, 3.6, 4.2, 4.8, 5.5],
  2: [3.9, 4.5, 5.1, 5.8, 6.6],
  3: [4.5, 5.2, 5.8, 6.6, 7.5],
  6: [5.7, 6.4, 7.3, 8.2, 9.3],
  9: [6.5, 7.3, 8.2, 9.3, 10.5],
  12: [7.0, 7.9, 8.9, 10.1, 11.5],
  18: [8.2, 9.2, 10.4, 11.8, 13.5],
  24: [9.0, 10.2, 11.5, 13.0, 15.0],
  36: [10.8, 12.1, 13.9, 15.8, 18.1],
  48: [12.3, 13.9, 15.9, 18.5, 21.5],
  60: [13.7, 15.8, 18.2, 21.2, 24.9],
};

const WHO_HEIGHT_FOR_AGE_BOYS = {
  // Age in months -> [P3, P15, P50, P85, P97] in cm
  0: [46.1, 48.0, 49.9, 51.8, 53.7],
  1: [50.8, 52.8, 54.7, 56.7, 58.6],
  2: [54.4, 56.4, 58.4, 60.4, 62.4],
  3: [57.3, 59.4, 61.4, 63.5, 65.5],
  6: [63.3, 65.5, 67.6, 69.8, 71.9],
  9: [68.0, 70.3, 72.0, 74.5, 76.5],
  12: [71.0, 73.4, 75.7, 78.1, 80.5],
  18: [76.9, 79.6, 82.3, 85.0, 87.8],
  24: [81.7, 84.8, 87.8, 90.9, 94.0],
  36: [88.7, 92.4, 96.1, 99.8, 103.5],
  48: [94.9, 99.1, 103.3, 107.5, 111.7],
  60: [100.7, 105.3, 110.0, 114.6, 119.2],
};

const WHO_HEIGHT_FOR_AGE_GIRLS = {
  0: [45.4, 47.3, 49.1, 51.0, 52.9],
  1: [49.8, 51.7, 53.7, 55.6, 57.6],
  2: [53.0, 55.0, 57.1, 59.1, 61.1],
  3: [55.6, 57.7, 59.8, 61.9, 64.0],
  6: [61.2, 63.5, 65.7, 68.0, 70.3],
  9: [65.3, 67.7, 70.1, 72.6, 75.0],
  12: [68.9, 71.4, 74.0, 76.6, 79.2],
  18: [74.9, 77.8, 80.7, 83.6, 86.5],
  24: [80.0, 83.2, 86.4, 89.6, 92.9],
  36: [87.4, 91.2, 95.1, 98.9, 102.7],
  48: [94.1, 98.4, 102.7, 107.0, 111.3],
  60: [99.9, 104.7, 109.4, 114.2, 118.9],
};

@Injectable()
export class GrowthPercentileService {
  private readonly logger = new Logger('GrowthPercentileService');

  constructor(
    @InjectRepository(Activity)
    private activityRepository: Repository<Activity>,
    @InjectRepository(Child)
    private childRepository: Repository<Child>,
  ) {}

  /**
   * Calculate growth percentiles for a child
   */
  async calculateGrowthPercentiles(
    childId: string,
    measurement: GrowthMeasurement,
  ): Promise<GrowthPercentile> {
    const child = await this.childRepository.findOne({ where: { id: childId } });
    if (!child) {
      throw new Error('Child not found');
    }

    const ageInMonths = this.calculateAgeInMonths(child.birthDate, measurement.date);
    const gender = child.gender as 'male' | 'female';

    const result: GrowthPercentile = {};

    if (measurement.weight !== undefined) {
      result.weight = this.calculateWeightPercentile(
        measurement.weight,
        ageInMonths,
        gender,
      );
    }

    if (measurement.height !== undefined) {
      result.height = this.calculateHeightPercentile(
        measurement.height,
        ageInMonths,
        gender,
      );
    }

    if (measurement.weight && measurement.height) {
      result.bmi = this.calculateBMIPercentile(
        measurement.weight,
        measurement.height,
        ageInMonths,
        gender,
      );

      if (ageInMonths < 24) {
        result.weightForLength = this.calculateWeightForLength(
          measurement.weight,
          measurement.height,
          gender,
        );
      }
    }

    if (measurement.headCircumference !== undefined && ageInMonths < 36) {
      result.headCircumference = this.calculateHeadCircumferencePercentile(
        measurement.headCircumference,
        ageInMonths,
        gender,
      );
    }

    return result;
  }

  /**
   * Analyze growth patterns and detect concerns
   */
  async analyzeGrowth(childId: string): Promise<GrowthAnalysis> {
    const child = await this.childRepository.findOne({ where: { id: childId } });
    if (!child) {
      throw new Error('Child not found');
    }

    // Get growth measurements from activities
    const measurements = await this.getGrowthMeasurements(childId);

    if (measurements.length === 0) {
      throw new Error('No growth measurements found');
    }

    // Calculate current percentiles
    const latestMeasurement = measurements[measurements.length - 1];
    const currentPercentiles = await this.calculateGrowthPercentiles(
      childId,
      latestMeasurement,
    );

    // Calculate growth velocity
    const growthVelocity = this.calculateGrowthVelocity(measurements, child);

    // Build growth curves
    const growthCurve = this.buildGrowthCurve(measurements, child);

    // Generate projections
    const projections = this.generateGrowthProjections(
      measurements,
      child,
      growthVelocity,
    );

    // Detect alerts and generate recommendations
    const alerts = this.detectGrowthAlerts(
      currentPercentiles,
      growthVelocity,
      measurements,
      child,
    );

    const recommendations = this.generateGrowthRecommendations(
      currentPercentiles,
      growthVelocity,
      alerts,
    );

    return {
      currentPercentiles,
      growthVelocity,
      growthCurve,
      projections,
      recommendations,
      alerts,
    };
  }

  /**
   * Calculate weight percentile
   */
  private calculateWeightPercentile(
    weight: number,
    ageInMonths: number,
    gender: 'male' | 'female',
  ): PercentileData {
    const growthData = gender === 'male' ? WHO_WEIGHT_FOR_AGE_BOYS : WHO_WEIGHT_FOR_AGE_GIRLS;
    const ageKey = this.getNearestAgeKey(ageInMonths, growthData);
    const percentiles = growthData[ageKey];

    const percentile = this.calculatePercentileFromValue(weight, percentiles);
    const zScore = this.calculateZScore(weight, percentiles);

    let interpretation: PercentileData['interpretation'] = 'normal';
    if (percentile < 3) interpretation = 'underweight';
    else if (percentile > 97) interpretation = 'overweight';
    else if (percentile > 95 && ageInMonths > 24) interpretation = 'obese';

    return {
      value: weight,
      percentile,
      zScore,
      interpretation,
      ageInMonths,
    };
  }

  /**
   * Calculate height percentile
   */
  private calculateHeightPercentile(
    height: number,
    ageInMonths: number,
    gender: 'male' | 'female',
  ): PercentileData {
    const growthData = gender === 'male' ? WHO_HEIGHT_FOR_AGE_BOYS : WHO_HEIGHT_FOR_AGE_GIRLS;
    const ageKey = this.getNearestAgeKey(ageInMonths, growthData);
    const percentiles = growthData[ageKey];

    const percentile = this.calculatePercentileFromValue(height, percentiles);
    const zScore = this.calculateZScore(height, percentiles);

    let interpretation: PercentileData['interpretation'] = 'normal';
    if (percentile < 3) interpretation = 'short';
    else if (percentile > 97) interpretation = 'tall';

    return {
      value: height,
      percentile,
      zScore,
      interpretation,
      ageInMonths,
    };
  }

  /**
   * Calculate BMI percentile
   */
  private calculateBMIPercentile(
    weight: number,
    height: number,
    ageInMonths: number,
    gender: 'male' | 'female',
  ): PercentileData {
    const bmi = weight / Math.pow(height / 100, 2);

    // Simplified BMI percentile calculation
    // In production, use CDC BMI-for-age charts
    let percentile = 50;
    let interpretation: PercentileData['interpretation'] = 'normal';

    if (ageInMonths >= 24) {
      if (bmi < 14) {
        percentile = 5;
        interpretation = 'underweight';
      } else if (bmi < 16) {
        percentile = 25;
        interpretation = 'normal';
      } else if (bmi < 18) {
        percentile = 50;
        interpretation = 'normal';
      } else if (bmi < 20) {
        percentile = 75;
        interpretation = 'normal';
      } else if (bmi < 23) {
        percentile = 90;
        interpretation = 'overweight';
      } else {
        percentile = 97;
        interpretation = 'obese';
      }
    }

    return {
      value: bmi,
      percentile,
      zScore: this.percentileToZScore(percentile),
      interpretation,
      ageInMonths,
    };
  }

  /**
   * Calculate weight-for-length percentile (for infants < 24 months)
   */
  private calculateWeightForLength(
    weight: number,
    length: number,
    gender: 'male' | 'female',
  ): PercentileData {
    // Simplified calculation - in production, use WHO weight-for-length tables
    const ratio = weight / (length / 100);
    let percentile = 50;
    let interpretation: PercentileData['interpretation'] = 'normal';

    if (ratio < 10) {
      percentile = 5;
      interpretation = 'underweight';
    } else if (ratio > 18) {
      percentile = 95;
      interpretation = 'overweight';
    }

    return {
      value: ratio,
      percentile,
      zScore: this.percentileToZScore(percentile),
      interpretation,
      ageInMonths: 0, // Not age-dependent
    };
  }

  /**
   * Calculate head circumference percentile
   */
  private calculateHeadCircumferencePercentile(
    circumference: number,
    ageInMonths: number,
    gender: 'male' | 'female',
  ): PercentileData {
    // Simplified - in production, use WHO head circumference charts
    const expectedCircumference = 35 + ageInMonths * 0.5; // Very simplified
    const deviation = circumference - expectedCircumference;
    const percentile = this.normalCDF(deviation / 2) * 100;

    let interpretation: PercentileData['interpretation'] = 'normal';
    if (percentile < 3 || percentile > 97) {
      interpretation = percentile < 3 ? 'short' : 'tall'; // Using as proxy
    }

    return {
      value: circumference,
      percentile,
      zScore: this.percentileToZScore(percentile),
      interpretation,
      ageInMonths,
    };
  }

  /**
   * Calculate growth velocity
   */
  private calculateGrowthVelocity(
    measurements: GrowthMeasurement[],
    child: Child,
  ): GrowthVelocity {
    if (measurements.length < 2) {
      return {
        interpretation: 'Insufficient data for velocity calculation',
        concerns: [],
      };
    }

    const recent = measurements.slice(-3); // Last 3 measurements
    const velocity: GrowthVelocity = {
      interpretation: '',
      concerns: [],
    };

    // Calculate weight velocity
    const weightMeasurements = recent.filter((m) => m.weight !== undefined);
    if (weightMeasurements.length >= 2) {
      const first = weightMeasurements[0];
      const last = weightMeasurements[weightMeasurements.length - 1];
      const monthsDiff = this.monthsDifference(first.date, last.date);

      if (monthsDiff > 0) {
        const weightGain = (last.weight! - first.weight!) / monthsDiff;
        const expectedGain = this.getExpectedWeightGain(child, first.date);

        velocity.weightVelocity = {
          value: weightGain,
          percentile: (weightGain / expectedGain) * 50, // Simplified
          isNormal: Math.abs(weightGain - expectedGain) / expectedGain < 0.3,
          trend:
            weightGain > expectedGain * 1.2 ? 'accelerating' :
            weightGain < expectedGain * 0.8 ? 'decelerating' :
            'steady',
        };

        if (!velocity.weightVelocity.isNormal) {
          velocity.concerns.push(
            weightGain < expectedGain * 0.5
              ? 'Poor weight gain detected'
              : weightGain > expectedGain * 1.5
              ? 'Rapid weight gain detected'
              : 'Weight gain outside normal range'
          );
        }
      }
    }

    // Calculate height velocity
    const heightMeasurements = recent.filter((m) => m.height !== undefined);
    if (heightMeasurements.length >= 2) {
      const first = heightMeasurements[0];
      const last = heightMeasurements[heightMeasurements.length - 1];
      const monthsDiff = this.monthsDifference(first.date, last.date);

      if (monthsDiff > 0) {
        const heightGain = (last.height! - first.height!) / monthsDiff;
        const expectedGain = this.getExpectedHeightGain(child, first.date);

        velocity.heightVelocity = {
          value: heightGain,
          percentile: (heightGain / expectedGain) * 50, // Simplified
          isNormal: Math.abs(heightGain - expectedGain) / expectedGain < 0.3,
          trend:
            heightGain > expectedGain * 1.2 ? 'accelerating' :
            heightGain < expectedGain * 0.8 ? 'decelerating' :
            'steady',
        };

        if (!velocity.heightVelocity.isNormal) {
          velocity.concerns.push(
            heightGain < expectedGain * 0.5
              ? 'Poor linear growth detected'
              : 'Height velocity outside normal range'
          );
        }
      }
    }

    // Set interpretation
    if (velocity.weightVelocity && velocity.heightVelocity) {
      if (velocity.weightVelocity.isNormal && velocity.heightVelocity.isNormal) {
        velocity.interpretation = 'Growth velocity is within normal range';
      } else {
        velocity.interpretation = 'Growth velocity shows concerning patterns';
      }
    } else {
      velocity.interpretation = 'Partial growth velocity data available';
    }

    return velocity;
  }

  /**
   * Build growth curve data for visualization
   */
  private buildGrowthCurve(
    measurements: GrowthMeasurement[],
    child: Child,
  ): GrowthCurveData {
    const gender = child.gender as 'male' | 'female';
    const weightData = gender === 'male' ? WHO_WEIGHT_FOR_AGE_BOYS : WHO_WEIGHT_FOR_AGE_GIRLS;

    // Extract percentile curves
    const ages = Object.keys(weightData).map(Number).sort((a, b) => a - b);
    const percentileCurves = {
      p3: ages.map(age => weightData[age][0]),
      p15: ages.map(age => weightData[age][1]),
      p50: ages.map(age => weightData[age][2]),
      p85: ages.map(age => weightData[age][3]),
      p97: ages.map(age => weightData[age][4]),
    };

    // Build child's curve
    const childCurve = measurements
      .filter(m => m.weight !== undefined)
      .map(m => m.weight!);

    return {
      measurements,
      percentileCurves,
      childCurve,
    };
  }

  /**
   * Generate growth projections
   */
  private generateGrowthProjections(
    measurements: GrowthMeasurement[],
    child: Child,
    velocity: GrowthVelocity,
  ): GrowthProjection {
    const lastMeasurement = measurements[measurements.length - 1];
    const projectionDate = new Date();
    projectionDate.setMonth(projectionDate.getMonth() + 3); // 3 months ahead

    // Project weight
    const projectedWeight = velocity.weightVelocity
      ? {
          value: lastMeasurement.weight! + velocity.weightVelocity.value * 3,
          date: projectionDate,
          confidence: velocity.weightVelocity.isNormal ? 0.8 : 0.5,
        }
      : {
          value: lastMeasurement.weight || 0,
          date: projectionDate,
          confidence: 0.3,
        };

    // Project height
    const projectedHeight = velocity.heightVelocity
      ? {
          value: lastMeasurement.height! + velocity.heightVelocity.value * 3,
          date: projectionDate,
          confidence: velocity.heightVelocity.isNormal ? 0.8 : 0.5,
        }
      : {
          value: lastMeasurement.height || 0,
          date: projectionDate,
          confidence: 0.3,
        };

    return {
      projectedWeight,
      projectedHeight,
    };
  }

  /**
   * Detect growth alerts
   */
  private detectGrowthAlerts(
    percentiles: GrowthPercentile,
    velocity: GrowthVelocity,
    measurements: GrowthMeasurement[],
    child: Child,
  ): GrowthAlert[] {
    const alerts: GrowthAlert[] = [];

    // Check for crossing percentiles
    if (measurements.length >= 3) {
      const percentileHistory = measurements.slice(-3).map((m) => {
        if (m.weight) {
          const ageInMonths = this.calculateAgeInMonths(child.birthDate, m.date);
          return this.calculateWeightPercentile(
            m.weight,
            ageInMonths,
            child.gender as 'male' | 'female',
          ).percentile;
        }
        return null;
      }).filter((p) => p !== null) as number[];

      if (percentileHistory.length >= 2) {
        const change = Math.abs(percentileHistory[percentileHistory.length - 1] - percentileHistory[0]);
        if (change > 25) {
          alerts.push({
            type: 'crossing_percentiles',
            severity: change > 50 ? 'critical' : 'warning',
            message: `Child has crossed ${Math.round(change)} percentile lines`,
            recommendation: 'Consult pediatrician about growth pattern changes',
          });
        }
      }
    }

    // Check for stunting (low height-for-age)
    if (percentiles.height && percentiles.height.percentile < 3) {
      alerts.push({
        type: 'stunting',
        severity: percentiles.height.percentile < 1 ? 'critical' : 'warning',
        message: 'Height is below the 3rd percentile for age',
        recommendation: 'Evaluate nutrition and consider pediatric endocrinology referral',
      });
    }

    // Check for wasting (low weight-for-height)
    if (percentiles.weightForLength && percentiles.weightForLength.percentile < 3) {
      alerts.push({
        type: 'wasting',
        severity: 'critical',
        message: 'Weight-for-length is below the 3rd percentile',
        recommendation: 'Urgent nutritional assessment needed',
      });
    }

    // Check for obesity risk
    if (percentiles.bmi && percentiles.bmi.percentile > 95) {
      alerts.push({
        type: 'obesity_risk',
        severity: percentiles.bmi.percentile > 99 ? 'warning' : 'info',
        message: 'BMI is above the 95th percentile for age',
        recommendation: 'Consider lifestyle modifications and dietary counseling',
      });
    }

    // Check for failure to thrive
    if (velocity.concerns.includes('Poor weight gain detected')) {
      alerts.push({
        type: 'failure_to_thrive',
        severity: 'critical',
        message: 'Poor weight gain velocity detected',
        recommendation: 'Comprehensive evaluation for failure to thrive indicated',
      });
    }

    return alerts;
  }

  /**
   * Generate growth recommendations
   */
  private generateGrowthRecommendations(
    percentiles: GrowthPercentile,
    velocity: GrowthVelocity,
    alerts: GrowthAlert[],
  ): string[] {
    const recommendations: string[] = [];

    // General growth tracking
    recommendations.push('Continue regular growth monitoring at pediatric visits');

    // Weight-specific recommendations
    if (percentiles.weight) {
      if (percentiles.weight.percentile < 10) {
        recommendations.push('Ensure adequate caloric intake for age');
        recommendations.push('Consider nutritional supplementation if indicated');
      } else if (percentiles.weight.percentile > 90) {
        recommendations.push('Focus on healthy eating habits and portion control');
        recommendations.push('Encourage age-appropriate physical activity');
      }
    }

    // Velocity-specific recommendations
    if (velocity.weightVelocity && !velocity.weightVelocity.isNormal) {
      if (velocity.weightVelocity.trend === 'decelerating') {
        recommendations.push('Monitor feeding patterns and appetite closely');
        recommendations.push('Rule out underlying medical conditions affecting growth');
      } else if (velocity.weightVelocity.trend === 'accelerating') {
        recommendations.push('Evaluate for overfeeding or excessive weight gain');
      }
    }

    // Alert-specific recommendations
    if (alerts.some(a => a.severity === 'critical')) {
      recommendations.unshift('URGENT: Schedule pediatric evaluation as soon as possible');
    }

    // Add age-specific recommendations
    const ageInMonths = this.calculateAgeInMonths(
      new Date(), // Assuming current age
      new Date(),
    );

    if (ageInMonths < 6) {
      recommendations.push('Ensure appropriate feeding frequency (8-12 times/day for breastfed infants)');
    } else if (ageInMonths < 12) {
      recommendations.push('Begin introducing appropriate solid foods if not already started');
    } else if (ageInMonths < 24) {
      recommendations.push('Transition to family foods with appropriate texture modifications');
    }

    return recommendations;
  }

  /**
   * Get growth measurements from activities
   */
  private async getGrowthMeasurements(childId: string): Promise<GrowthMeasurement[]> {
    const growthActivities = await this.activityRepository.find({
      where: {
        childId,
        type: ActivityType.GROWTH,
      },
      order: { startedAt: 'ASC' },
    });

    return growthActivities
      .filter((a) => a.metadata)
      .map((a) => ({
        date: a.startedAt,
        weight: a.metadata.weight,
        height: a.metadata.height,
        headCircumference: a.metadata.headCircumference,
      }));
  }

  // Utility methods

  private calculateAgeInMonths(birthDate: Date, currentDate: Date = new Date()): number {
    const months =
      (currentDate.getFullYear() - birthDate.getFullYear()) * 12 +
      (currentDate.getMonth() - birthDate.getMonth());
    return Math.max(0, months);
  }

  private monthsDifference(date1: Date, date2: Date): number {
    return Math.abs(
      (date2.getFullYear() - date1.getFullYear()) * 12 +
      (date2.getMonth() - date1.getMonth())
    );
  }

  private getNearestAgeKey(ageInMonths: number, data: any): number {
    const ages = Object.keys(data).map(Number);
    return ages.reduce((prev, curr) =>
      Math.abs(curr - ageInMonths) < Math.abs(prev - ageInMonths) ? curr : prev
    );
  }

  private calculatePercentileFromValue(value: number, percentiles: number[]): number {
    // percentiles = [P3, P15, P50, P85, P97]
    if (value <= percentiles[0]) return 3;
    if (value <= percentiles[1]) return this.interpolate(value, percentiles[0], percentiles[1], 3, 15);
    if (value <= percentiles[2]) return this.interpolate(value, percentiles[1], percentiles[2], 15, 50);
    if (value <= percentiles[3]) return this.interpolate(value, percentiles[2], percentiles[3], 50, 85);
    if (value <= percentiles[4]) return this.interpolate(value, percentiles[3], percentiles[4], 85, 97);
    return 97;
  }

  private calculateZScore(value: number, percentiles: number[]): number {
    const median = percentiles[2]; // P50
    const sd = (percentiles[3] - percentiles[1]) / 2; // Approximate SD
    return (value - median) / sd;
  }

  private percentileToZScore(percentile: number): number {
    // Convert percentile to z-score using inverse normal distribution
    // Simplified approximation
    if (percentile === 50) return 0;
    if (percentile === 84) return 1;
    if (percentile === 16) return -1;
    if (percentile === 97.7) return 2;
    if (percentile === 2.3) return -2;

    // General approximation
    return (percentile - 50) / 35;
  }

  private interpolate(
    value: number,
    x1: number,
    x2: number,
    y1: number,
    y2: number,
  ): number {
    return y1 + ((value - x1) / (x2 - x1)) * (y2 - y1);
  }

  private normalCDF(x: number): number {
    // Approximation of the cumulative distribution function for standard normal
    const t = 1 / (1 + 0.2316419 * Math.abs(x));
    const d = 0.3989423 * Math.exp((-x * x) / 2);
    const probability =
      d *
      t *
      (0.3193815 +
        t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))));
    return x > 0 ? 1 - probability : probability;
  }

  private getExpectedWeightGain(child: Child, date: Date): number {
    const ageInMonths = this.calculateAgeInMonths(child.birthDate, date);

    // Expected monthly weight gain by age (kg/month)
    if (ageInMonths < 3) return 0.7; // 700g/month for 0-3 months
    if (ageInMonths < 6) return 0.5; // 500g/month for 3-6 months
    if (ageInMonths < 12) return 0.3; // 300g/month for 6-12 months
    if (ageInMonths < 24) return 0.2; // 200g/month for 12-24 months
    return 0.15; // 150g/month for 2+ years
  }

  private getExpectedHeightGain(child: Child, date: Date): number {
    const ageInMonths = this.calculateAgeInMonths(child.birthDate, date);

    // Expected monthly height gain by age (cm/month)
    if (ageInMonths < 3) return 3.5; // 3.5cm/month for 0-3 months
    if (ageInMonths < 6) return 2.0; // 2cm/month for 3-6 months
    if (ageInMonths < 12) return 1.2; // 1.2cm/month for 6-12 months
    if (ageInMonths < 24) return 0.9; // 0.9cm/month for 12-24 months
    return 0.6; // 0.6cm/month for 2+ years
  }
}