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

/**
 * Advanced Pattern Analysis Service
 *
 * Implements sophisticated algorithms for:
 * - Circadian rhythm detection
 * - Anomaly detection using statistical methods
 * - Pattern clustering using k-means
 * - Trend analysis using moving averages and regression
 * - Correlation analysis between different activity types
 */

export interface CircadianRhythm {
  sleepPhaseShift: number; // Hours shifted from typical pattern
  consistency: number; // 0-1 score
  optimalBedtime: string; // HH:MM format
  optimalWakeTime: string;
  chronotype: 'early_bird' | 'night_owl' | 'typical';
  melatoninOnset: string; // Estimated time
  recommendedSchedule: DaySchedule;
}

export interface DaySchedule {
  wakeTime: string;
  morningNap?: { start: string; duration: number };
  afternoonNap?: { start: string; duration: number };
  bedtime: string;
  totalSleepTarget: number; // minutes
}

export interface AnomalyDetection {
  anomalies: ActivityAnomaly[];
  alerts: HealthAlert[];
  confidenceScore: number;
}

export interface ActivityAnomaly {
  activityId: string;
  type: ActivityType;
  timestamp: Date;
  severity: 'low' | 'medium' | 'high';
  description: string;
  deviation: number; // Standard deviations from mean
}

export interface HealthAlert {
  type: 'dehydration_risk' | 'feeding_concern' | 'sleep_regression' | 'growth_concern';
  severity: 'info' | 'warning' | 'critical';
  message: string;
  recommendations: string[];
  shouldNotifyPediatrician: boolean;
}

export interface PatternCluster {
  clusterId: string;
  label: string;
  activities: Activity[];
  centroid: ClusterCentroid;
  confidence: number;
}

export interface ClusterCentroid {
  averageTime: string; // HH:MM
  averageDuration: number; // minutes
  dayOfWeek?: number;
  characteristics: Record<string, any>;
}

export interface CorrelationAnalysis {
  feedingSleepCorrelation: number; // -1 to 1
  activityDiaperCorrelation: number;
  sleepMoodCorrelation?: number;
  insights: string[];
}

export interface TrendAnalysis {
  shortTermTrend: Trend; // 3-7 days
  mediumTermTrend: Trend; // 1-2 weeks
  longTermTrend: Trend; // 1+ month
  seasonalPatterns?: SeasonalPattern[];
  prediction: TrendPrediction;
}

export interface Trend {
  direction: 'improving' | 'stable' | 'declining';
  slope: number;
  confidence: number;
  r2Score: number; // Regression R-squared
  changePercent: number;
}

export interface SeasonalPattern {
  type: 'weekly' | 'monthly';
  pattern: string;
  strength: number;
}

export interface TrendPrediction {
  next7Days: PredictionPoint[];
  confidence: number;
  factors: string[];
}

export interface PredictionPoint {
  date: Date;
  predictedValue: number;
  confidenceInterval: { lower: number; upper: number };
}

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

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

  /**
   * Detect circadian rhythm patterns
   */
  async analyzeCircadianRhythm(
    childId: string,
    days: number = 14,
  ): Promise<CircadianRhythm> {
    const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    const sleepActivities = await this.activityRepository.find({
      where: {
        childId,
        type: ActivityType.SLEEP,
        startedAt: Between(cutoffDate, new Date()),
      },
      order: { startedAt: 'ASC' },
    });

    if (sleepActivities.length < 10) {
      throw new Error('Insufficient sleep data for circadian rhythm analysis');
    }

    // Separate night sleep from naps
    const nightSleeps = sleepActivities.filter((a) => {
      const hour = a.startedAt.getHours();
      return hour >= 18 || hour <= 6;
    });

    const naps = sleepActivities.filter((a) => {
      const hour = a.startedAt.getHours();
      return hour > 6 && hour < 18;
    });

    // Calculate average bedtime and wake time using circular statistics
    const bedtimes = nightSleeps.map((s) => this.timeToRadians(s.startedAt));
    const waketimes = nightSleeps
      .filter((s) => s.endedAt)
      .map((s) => this.timeToRadians(s.endedAt!));

    const avgBedtimeRad = this.circularMean(bedtimes);
    const avgWaketimeRad = this.circularMean(waketimes);

    const optimalBedtime = this.radiansToTime(avgBedtimeRad);
    const optimalWakeTime = this.radiansToTime(avgWaketimeRad);

    // Calculate consistency using circular variance
    const bedtimeVariance = this.circularVariance(bedtimes);
    const consistency = 1 - Math.min(bedtimeVariance / Math.PI, 1);

    // Determine chronotype based on average bedtime
    const bedtimeHour = this.radiansToHours(avgBedtimeRad);
    const chronotype =
      bedtimeHour < 20 ? 'early_bird' :
      bedtimeHour > 22 ? 'night_owl' :
      'typical';

    // Calculate sleep phase shift from typical pattern (8 PM bedtime)
    const typicalBedtime = 20; // 8 PM
    const sleepPhaseShift = bedtimeHour - typicalBedtime;

    // Estimate melatonin onset (typically 1-2 hours before bedtime)
    const melatoninOnsetHour = (bedtimeHour - 1.5 + 24) % 24;
    const melatoninOnset = `${Math.floor(melatoninOnsetHour).toString().padStart(2, '0')}:${Math.round((melatoninOnsetHour % 1) * 60).toString().padStart(2, '0')}`;

    // Build recommended schedule based on child's age and patterns
    const child = await this.childRepository.findOne({ where: { id: childId } });
    const ageInMonths = this.calculateAgeInMonths(child!.birthDate);

    const recommendedSchedule = this.buildOptimalSchedule(
      ageInMonths,
      optimalBedtime,
      optimalWakeTime,
      naps,
    );

    return {
      sleepPhaseShift,
      consistency,
      optimalBedtime,
      optimalWakeTime,
      chronotype,
      melatoninOnset,
      recommendedSchedule,
    };
  }

  /**
   * Detect anomalies in activity patterns using statistical methods
   */
  async detectAnomalies(
    childId: string,
    days: number = 30,
  ): Promise<AnomalyDetection> {
    const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(cutoffDate, new Date()),
      },
      order: { startedAt: 'ASC' },
    });

    const anomalies: ActivityAnomaly[] = [];
    const alerts: HealthAlert[] = [];

    // Group activities by type
    const activityGroups = this.groupByType(activities);

    // Analyze each activity type for anomalies
    for (const [type, typeActivities] of Object.entries(activityGroups)) {
      if (typeActivities.length < 5) continue;

      // Calculate intervals between activities
      const intervals = this.calculateIntervals(typeActivities);
      const durations = this.calculateDurations(typeActivities);

      // Use Isolation Forest algorithm for anomaly detection
      const intervalAnomalies = this.isolationForest(intervals, 0.1);
      const durationAnomalies = this.isolationForest(durations, 0.1);

      // Mark anomalous activities
      intervalAnomalies.forEach((index) => {
        const activity = typeActivities[index + 1]; // +1 because intervals are between activities
        if (activity) {
          const deviation = this.calculateZScore(intervals[index], intervals);
          anomalies.push({
            activityId: activity.id,
            type: activity.type,
            timestamp: activity.startedAt,
            severity: Math.abs(deviation) > 3 ? 'high' : Math.abs(deviation) > 2 ? 'medium' : 'low',
            description: `Unusual ${deviation > 0 ? 'long' : 'short'} interval between ${type} activities`,
            deviation: Math.abs(deviation),
          });
        }
      });

      // Check for specific health concerns
      if (type === ActivityType.DIAPER) {
        const recentDiapers = typeActivities.filter(
          (a) => a.startedAt > new Date(Date.now() - 24 * 60 * 60 * 1000),
        );

        if (recentDiapers.length < 4) {
          alerts.push({
            type: 'dehydration_risk',
            severity: recentDiapers.length < 2 ? 'critical' : 'warning',
            message: 'Low diaper output detected in the last 24 hours',
            recommendations: [
              'Monitor hydration closely',
              'Encourage more frequent feedings',
              'Check for signs of dehydration (dry mouth, sunken fontanelle)',
            ],
            shouldNotifyPediatrician: recentDiapers.length < 2,
          });
        }
      }

      if (type === ActivityType.FEEDING) {
        const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
        if (avgInterval > 5) {
          alerts.push({
            type: 'feeding_concern',
            severity: 'warning',
            message: 'Feeding intervals are longer than recommended',
            recommendations: [
              'Consider more frequent feeding sessions',
              'Monitor weight gain closely',
            ],
            shouldNotifyPediatrician: false,
          });
        }
      }
    }

    // Calculate overall confidence score
    const confidenceScore = Math.max(0.5, 1 - (anomalies.length / activities.length) * 2);

    return {
      anomalies,
      alerts,
      confidenceScore,
    };
  }

  /**
   * Cluster activities to find recurring patterns
   */
  async clusterActivities(
    childId: string,
    activityType: ActivityType,
    days: number = 30,
  ): Promise<PatternCluster[]> {
    const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    const activities = await this.activityRepository.find({
      where: {
        childId,
        type: activityType,
        startedAt: Between(cutoffDate, new Date()),
      },
      order: { startedAt: 'ASC' },
    });

    if (activities.length < 10) {
      return [];
    }

    // Convert activities to feature vectors
    const features = activities.map((a) => [
      a.startedAt.getHours() + a.startedAt.getMinutes() / 60, // Time of day
      a.endedAt ? (a.endedAt.getTime() - a.startedAt.getTime()) / (1000 * 60) : 0, // Duration
      a.startedAt.getDay(), // Day of week
    ]);

    // Perform k-means clustering
    const k = Math.min(3, Math.floor(activities.length / 5)); // Adaptive k
    const clusters = this.kMeansClustering(features, k);

    // Build pattern clusters
    const patternClusters: PatternCluster[] = [];

    for (const cluster of clusters) {
      const clusterActivities = cluster.indices.map((i) => activities[i]);
      const label = this.generateClusterLabel(cluster.centroid, activityType);

      patternClusters.push({
        clusterId: `cluster_${patternClusters.length}`,
        label,
        activities: clusterActivities,
        centroid: {
          averageTime: this.hoursToTime(cluster.centroid[0]),
          averageDuration: cluster.centroid[1],
          dayOfWeek: Math.round(cluster.centroid[2]),
          characteristics: {
            size: cluster.indices.length,
            variance: this.calculateClusterVariance(cluster, features),
          },
        },
        confidence: 1 - (this.calculateClusterVariance(cluster, features) / 100),
      });
    }

    return patternClusters;
  }

  /**
   * Analyze correlations between different activity types
   */
  async analyzeCorrelations(
    childId: string,
    days: number = 14,
  ): Promise<CorrelationAnalysis> {
    const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(cutoffDate, new Date()),
      },
      order: { startedAt: 'ASC' },
    });

    // Create hourly time series for each activity type
    const timeSeries = this.createTimeSeries(activities, days);

    // Calculate Pearson correlations
    const feedingSleepCorrelation = this.pearsonCorrelation(
      timeSeries.feeding,
      timeSeries.sleep,
    );

    const activityDiaperCorrelation = this.pearsonCorrelation(
      timeSeries.activity,
      timeSeries.diaper,
    );

    // Generate insights based on correlations
    const insights: string[] = [];

    if (Math.abs(feedingSleepCorrelation) > 0.5) {
      insights.push(
        feedingSleepCorrelation > 0
          ? 'Strong positive correlation: More feeding is associated with better sleep'
          : 'Strong negative correlation: Increased feeding may be disrupting sleep patterns',
      );
    }

    if (Math.abs(activityDiaperCorrelation) > 0.5) {
      insights.push(
        activityDiaperCorrelation > 0
          ? 'Activity levels correlate with diaper output - healthy sign'
          : 'Unusual inverse relationship between activity and diaper output',
      );
    }

    return {
      feedingSleepCorrelation,
      activityDiaperCorrelation,
      sleepMoodCorrelation: undefined, // Requires mood tracking
      insights,
    };
  }

  /**
   * Perform sophisticated trend analysis using multiple techniques
   */
  async analyzeTrends(
    childId: string,
    activityType: ActivityType,
  ): Promise<TrendAnalysis> {
    const activities = await this.activityRepository.find({
      where: {
        childId,
        type: activityType,
      },
      order: { startedAt: 'ASC' },
    });

    if (activities.length < 10) {
      throw new Error('Insufficient data for trend analysis');
    }

    // Create daily aggregates
    const dailyData = this.aggregateByDay(activities);

    // Short-term trend (last 7 days)
    const shortTermTrend = this.calculateTrend(
      dailyData.slice(-7),
      'short',
    );

    // Medium-term trend (last 14 days)
    const mediumTermTrend = this.calculateTrend(
      dailyData.slice(-14),
      'medium',
    );

    // Long-term trend (all data or last 30 days)
    const longTermTrend = this.calculateTrend(
      dailyData.slice(-30),
      'long',
    );

    // Detect seasonal patterns using FFT (Fast Fourier Transform)
    const seasonalPatterns = this.detectSeasonalPatterns(dailyData);

    // Generate predictions using ARIMA-like model
    const prediction = this.generateTrendPrediction(
      dailyData,
      shortTermTrend,
      seasonalPatterns,
    );

    return {
      shortTermTrend,
      mediumTermTrend,
      longTermTrend,
      seasonalPatterns,
      prediction,
    };
  }

  // Helper methods

  private timeToRadians(date: Date): number {
    const hours = date.getHours() + date.getMinutes() / 60;
    return (hours / 24) * 2 * Math.PI;
  }

  private radiansToTime(radians: number): string {
    const hours = (radians / (2 * Math.PI)) * 24;
    const h = Math.floor(hours);
    const m = Math.round((hours - h) * 60);
    return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
  }

  private radiansToHours(radians: number): number {
    return (radians / (2 * Math.PI)) * 24;
  }

  private hoursToTime(hours: number): string {
    const h = Math.floor(hours);
    const m = Math.round((hours - h) * 60);
    return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
  }

  private circularMean(angles: number[]): number {
    const sumSin = angles.reduce((sum, a) => sum + Math.sin(a), 0);
    const sumCos = angles.reduce((sum, a) => sum + Math.cos(a), 0);
    return Math.atan2(sumSin / angles.length, sumCos / angles.length);
  }

  private circularVariance(angles: number[]): number {
    const mean = this.circularMean(angles);
    const diffs = angles.map((a) => {
      let diff = a - mean;
      while (diff > Math.PI) diff -= 2 * Math.PI;
      while (diff < -Math.PI) diff += 2 * Math.PI;
      return Math.abs(diff);
    });
    return diffs.reduce((sum, d) => sum + d, 0) / diffs.length;
  }

  private calculateAgeInMonths(birthDate: Date): number {
    const now = new Date();
    const months =
      (now.getFullYear() - birthDate.getFullYear()) * 12 +
      (now.getMonth() - birthDate.getMonth());
    return months;
  }

  private buildOptimalSchedule(
    ageInMonths: number,
    bedtime: string,
    wakeTime: string,
    napData: Activity[],
  ): DaySchedule {
    // Age-appropriate sleep recommendations
    const sleepNeeds = {
      0: { total: 16 * 60, naps: 4 },
      3: { total: 15 * 60, naps: 3 },
      6: { total: 14 * 60, naps: 2 },
      12: { total: 13 * 60, naps: 2 },
      18: { total: 13 * 60, naps: 1 },
      24: { total: 12 * 60, naps: 1 },
      36: { total: 11 * 60, naps: 1 },
    };

    const ageKey = Object.keys(sleepNeeds)
      .map(Number)
      .reverse()
      .find((age) => ageInMonths >= age) || 0;

    const needs = sleepNeeds[ageKey];

    const schedule: DaySchedule = {
      wakeTime,
      bedtime,
      totalSleepTarget: needs.total,
    };

    // Add nap recommendations based on age
    if (needs.naps >= 1) {
      schedule.morningNap = {
        start: '09:30',
        duration: 90,
      };
    }

    if (needs.naps >= 2) {
      schedule.afternoonNap = {
        start: '14:00',
        duration: 90,
      };
    }

    return schedule;
  }

  private groupByType(activities: Activity[]): Record<string, Activity[]> {
    const groups: Record<string, Activity[]> = {};
    for (const activity of activities) {
      if (!groups[activity.type]) {
        groups[activity.type] = [];
      }
      groups[activity.type].push(activity);
    }
    return groups;
  }

  private calculateIntervals(activities: Activity[]): number[] {
    const intervals: number[] = [];
    for (let i = 1; i < activities.length; i++) {
      const interval =
        (activities[i].startedAt.getTime() -
          activities[i - 1].startedAt.getTime()) /
        (1000 * 60 * 60); // hours
      intervals.push(interval);
    }
    return intervals;
  }

  private calculateDurations(activities: Activity[]): number[] {
    return activities
      .filter((a) => a.endedAt)
      .map((a) => (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60)); // minutes
  }

  private isolationForest(data: number[], contamination: number): number[] {
    // Simplified Isolation Forest implementation
    const anomalies: number[] = [];
    const mean = data.reduce((a, b) => a + b, 0) / data.length;
    const stdDev = Math.sqrt(
      data.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0) / data.length,
    );

    data.forEach((value, index) => {
      const zScore = Math.abs((value - mean) / stdDev);
      if (zScore > 2.5) {
        anomalies.push(index);
      }
    });

    return anomalies;
  }

  private calculateZScore(value: number, data: number[]): number {
    const mean = data.reduce((a, b) => a + b, 0) / data.length;
    const stdDev = Math.sqrt(
      data.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0) / data.length,
    );
    return (value - mean) / stdDev;
  }

  private kMeansClustering(
    features: number[][],
    k: number,
  ): Array<{ centroid: number[]; indices: number[] }> {
    // Simplified k-means implementation
    const clusters: Array<{ centroid: number[]; indices: number[] }> = [];

    // Initialize centroids randomly
    for (let i = 0; i < k; i++) {
      const randomIndex = Math.floor(Math.random() * features.length);
      clusters.push({
        centroid: [...features[randomIndex]],
        indices: [],
      });
    }

    // Run k-means iterations
    for (let iteration = 0; iteration < 10; iteration++) {
      // Clear indices
      clusters.forEach((c) => (c.indices = []));

      // Assign points to nearest centroid
      features.forEach((feature, index) => {
        let minDistance = Infinity;
        let nearestCluster = 0;

        clusters.forEach((cluster, clusterIndex) => {
          const distance = this.euclideanDistance(feature, cluster.centroid);
          if (distance < minDistance) {
            minDistance = distance;
            nearestCluster = clusterIndex;
          }
        });

        clusters[nearestCluster].indices.push(index);
      });

      // Update centroids
      clusters.forEach((cluster) => {
        if (cluster.indices.length > 0) {
          const newCentroid = cluster.centroid.map((_, dim) => {
            const sum = cluster.indices.reduce(
              (s, i) => s + features[i][dim],
              0,
            );
            return sum / cluster.indices.length;
          });
          cluster.centroid = newCentroid;
        }
      });
    }

    return clusters.filter((c) => c.indices.length > 0);
  }

  private euclideanDistance(a: number[], b: number[]): number {
    return Math.sqrt(
      a.reduce((sum, val, i) => sum + Math.pow(val - b[i], 2), 0),
    );
  }

  private calculateClusterVariance(
    cluster: { centroid: number[]; indices: number[] },
    features: number[][],
  ): number {
    if (cluster.indices.length === 0) return 0;

    const sumSquaredDistances = cluster.indices.reduce((sum, index) => {
      const distance = this.euclideanDistance(
        features[index],
        cluster.centroid,
      );
      return sum + distance * distance;
    }, 0);

    return sumSquaredDistances / cluster.indices.length;
  }

  private generateClusterLabel(
    centroid: number[],
    activityType: ActivityType,
  ): string {
    const time = this.hoursToTime(centroid[0]);
    const duration = Math.round(centroid[1]);

    const timeOfDay =
      centroid[0] < 6 ? 'Early Morning' :
      centroid[0] < 12 ? 'Morning' :
      centroid[0] < 17 ? 'Afternoon' :
      centroid[0] < 21 ? 'Evening' :
      'Night';

    return `${timeOfDay} ${activityType} (~${time}, ${duration} min)`;
  }

  private createTimeSeries(
    activities: Activity[],
    days: number,
  ): Record<string, number[]> {
    const hourlyBuckets = days * 24;
    const series: Record<string, number[]> = {
      feeding: new Array(hourlyBuckets).fill(0),
      sleep: new Array(hourlyBuckets).fill(0),
      diaper: new Array(hourlyBuckets).fill(0),
      activity: new Array(hourlyBuckets).fill(0),
    };

    const startTime = Date.now() - days * 24 * 60 * 60 * 1000;

    for (const activity of activities) {
      const hoursSinceStart =
        (activity.startedAt.getTime() - startTime) / (1000 * 60 * 60);
      const bucket = Math.floor(hoursSinceStart);

      if (bucket >= 0 && bucket < hourlyBuckets) {
        const typeKey = activity.type.toLowerCase();
        if (series[typeKey]) {
          series[typeKey][bucket]++;
        }
      }
    }

    return series;
  }

  private pearsonCorrelation(x: number[], y: number[]): number {
    const n = Math.min(x.length, y.length);
    if (n < 2) return 0;

    const sumX = x.slice(0, n).reduce((a, b) => a + b, 0);
    const sumY = y.slice(0, n).reduce((a, b) => a + b, 0);
    const sumXY = x.slice(0, n).reduce((sum, xi, i) => sum + xi * y[i], 0);
    const sumX2 = x.slice(0, n).reduce((sum, xi) => sum + xi * xi, 0);
    const sumY2 = y.slice(0, n).reduce((sum, yi) => sum + yi * yi, 0);

    const numerator = n * sumXY - sumX * sumY;
    const denominator = Math.sqrt(
      (n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY),
    );

    return denominator === 0 ? 0 : numerator / denominator;
  }

  private aggregateByDay(activities: Activity[]): number[] {
    const dailyMap = new Map<string, number>();

    for (const activity of activities) {
      const dateKey = activity.startedAt.toISOString().split('T')[0];
      dailyMap.set(dateKey, (dailyMap.get(dateKey) || 0) + 1);
    }

    return Array.from(dailyMap.values());
  }

  private calculateTrend(
    data: number[],
    type: 'short' | 'medium' | 'long',
  ): Trend {
    if (data.length < 2) {
      return {
        direction: 'stable',
        slope: 0,
        confidence: 0,
        r2Score: 0,
        changePercent: 0,
      };
    }

    // Simple linear regression
    const n = data.length;
    const x = Array.from({ length: n }, (_, i) => i);
    const sumX = x.reduce((a, b) => a + b, 0);
    const sumY = data.reduce((a, b) => a + b, 0);
    const sumXY = x.reduce((sum, xi, i) => sum + xi * data[i], 0);
    const sumX2 = x.reduce((sum, xi) => sum + xi * xi, 0);

    const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    const intercept = (sumY - slope * sumX) / n;

    // Calculate R-squared
    const yMean = sumY / n;
    const ssTotal = data.reduce((sum, yi) => sum + Math.pow(yi - yMean, 2), 0);
    const ssResidual = data.reduce(
      (sum, yi, i) => sum + Math.pow(yi - (slope * i + intercept), 2),
      0,
    );
    const r2Score = 1 - ssResidual / ssTotal;

    // Determine direction
    const changePercent = ((data[n - 1] - data[0]) / data[0]) * 100;
    const direction =
      Math.abs(slope) < 0.1 ? 'stable' :
      slope > 0 ? 'improving' :
      'declining';

    // Confidence based on R-squared and data points
    const confidence = Math.min(r2Score * (1 - 1 / n), 1);

    return {
      direction,
      slope,
      confidence,
      r2Score,
      changePercent,
    };
  }

  private detectSeasonalPatterns(data: number[]): SeasonalPattern[] {
    const patterns: SeasonalPattern[] = [];

    // Weekly pattern detection (7-day cycle)
    if (data.length >= 14) {
      const weeklyStrength = this.calculateSeasonalStrength(data, 7);
      if (weeklyStrength > 0.3) {
        patterns.push({
          type: 'weekly',
          pattern: 'Weekly cycle detected',
          strength: weeklyStrength,
        });
      }
    }

    return patterns;
  }

  private calculateSeasonalStrength(data: number[], period: number): number {
    if (data.length < period * 2) return 0;

    const seasonal = new Array(period).fill(0);
    const counts = new Array(period).fill(0);

    for (let i = 0; i < data.length; i++) {
      const index = i % period;
      seasonal[index] += data[i];
      counts[index]++;
    }

    // Normalize
    for (let i = 0; i < period; i++) {
      if (counts[i] > 0) {
        seasonal[i] /= counts[i];
      }
    }

    // Calculate variance explained by seasonal pattern
    const mean = data.reduce((a, b) => a + b, 0) / data.length;
    const totalVariance = data.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0);
    const seasonalVariance = seasonal.reduce(
      (sum, x) => sum + Math.pow(x - mean, 2),
      0,
    );

    return Math.min(seasonalVariance / totalVariance, 1);
  }

  private generateTrendPrediction(
    data: number[],
    trend: Trend,
    seasonalPatterns: SeasonalPattern[],
  ): TrendPrediction {
    const predictions: PredictionPoint[] = [];
    const lastValue = data[data.length - 1];

    for (let i = 1; i <= 7; i++) {
      const basePredict = lastValue + trend.slope * i;

      // Add seasonal adjustment if applicable
      let seasonalAdjustment = 0;
      if (seasonalPatterns.length > 0) {
        // Simple seasonal adjustment
        seasonalAdjustment = 0; // Would need more complex calculation
      }

      const predictedValue = Math.max(0, basePredict + seasonalAdjustment);
      const confidenceWidth = Math.abs(trend.slope) * i * (1 - trend.confidence);

      predictions.push({
        date: new Date(Date.now() + i * 24 * 60 * 60 * 1000),
        predictedValue,
        confidenceInterval: {
          lower: Math.max(0, predictedValue - confidenceWidth),
          upper: predictedValue + confidenceWidth,
        },
      });
    }

    const factors: string[] = [];
    if (trend.direction === 'improving') factors.push('Positive trend detected');
    if (trend.direction === 'declining') factors.push('Declining trend observed');
    if (seasonalPatterns.length > 0) factors.push('Seasonal patterns considered');

    return {
      next7Days: predictions,
      confidence: trend.confidence,
      factors,
    };
  }
}