// SPDX-FileCopyrightText: © 2025 Nøkken.io <nokken.io@proton.me>
// SPDX-License-Identifier: AGPL-3.0
//
// health_analytics_service.dart
//
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as path;
import 'dart:math' as math;
import 'package:nokken/src/features/bloodwork_tracker/models/bloodwork.dart';
import 'package:nokken/src/features/medication_tracker/models/medication.dart';
import 'package:nokken/src/features/mood_tracker/models/mood_entry.dart';

/// Advanced Health Analytics Service
///
/// This centralized service provides comprehensive statistical analysis capabilities
/// for health data. It leverages C++ for performance-critical calculations via FFI.
class HealthAnalyticsService {
  static const String _tag = "HealthAnalyticsService";

  // Singleton instance
  static final HealthAnalyticsService _instance =
      HealthAnalyticsService._internal();
  factory HealthAnalyticsService() => _instance;

  // Native library handle
  static DynamicLibrary? _nativeLib;
  bool _initialized = false;

  // Core C++ function bindings
  late _CalculateBasicStatsDart _calculateBasicStats;
  late _DetectTrendDart _detectTrend;
  late _FindStrongestCorrelationsDart _findStrongestCorrelations;
  late _FreeCorrelationResultsDart _freeCorrelationResults;
  late _FindMultivariateCorrelationsDart _findMultivariateCorrelations;
  late _FreeMultivariateCorrelationsDart _freeMultivariateCorrelations;
  late _PerformClusterAnalysisDart _performClusterAnalysis;
  late _FreeClusterResultsDart _freeClusterResults;
  late _PredictTimeSeriesDart _predictTimeSeries;
  late _DetectAnomaliesDart _detectAnomalies;
  late _FreeAnomalyResultsDart _freeAnomalyResults;
  late _RankFactorImpactsDart _rankFactorImpacts;
  late _FreeFactorImpactResultsDart _freeFactorImpactResults;
  late _AnalyzeDatePatternsDart _analyzeDatePatterns;
  late _FreeDatePatternResultsDart _freeDatePatternResults;
  late _AnalyzeCyclesDart _analyzeCycles;
  late _AnalyzeMedicationImpactDart _analyzeMedicationImpact;
  late _FreeMedicationImpactAnalysisDart _freeMedicationImpactAnalysis;
  late _AnalyzeHormoneImpactDart _analyzeHormoneImpact;
  late _FreeHormoneImpactAnalysisDart _freeHormoneImpactAnalysis;

  HealthAnalyticsService._internal();

  /// Initialize the service by loading the native library
  /// Returns true if successful, false otherwise
  Future<bool> initialize() async {
    if (_initialized) return true;

    try {
      // Load the appropriate library for the current platform
      if (Platform.isAndroid) {
        _nativeLib = DynamicLibrary.open('libhealth_analytics.so');
      } else if (Platform.isIOS) {
        _nativeLib = DynamicLibrary.process();
      } else if (Platform.isWindows) {
        try {
          _nativeLib = DynamicLibrary.open('health_analytics.dll');
        } catch (_) {
          final dir = path.dirname(Platform.resolvedExecutable);
          _nativeLib =
              DynamicLibrary.open(path.join(dir, 'health_analytics.dll'));
        }
      } else if (Platform.isLinux) {
        try {
          // First try the current directory
          _nativeLib = DynamicLibrary.open('./libhealth_analytics.so');
        } catch (_) {
          try {
            // Try project specific paths
            final projectDir = Directory.current.path;
            _nativeLib = DynamicLibrary.open(path.join(
                projectDir, 'build', 'linux', 'lib', 'libhealth_analytics.so'));
          } catch (_) {
            try {
              // Final the Flutter executable directory
              final dir = path.dirname(Platform.resolvedExecutable);
              _nativeLib =
                  DynamicLibrary.open(path.join(dir, 'libhealth_analytics.so'));
            } catch (e) {
              throw Exception(
                  'Could not find libhealth_analytics.so in any location');
            }
          }
        }
      } else if (Platform.isMacOS) {
        try {
          _nativeLib = DynamicLibrary.open('./libhealth_analytics.dylib');
        } catch (_) {
          final dir = path.dirname(Platform.resolvedExecutable);
          _nativeLib =
              DynamicLibrary.open(path.join(dir, 'libhealth_analytics.dylib'));
        }
      } else {
        throw UnsupportedError(
            'Unsupported platform: ${Platform.operatingSystem}');
      }

      _initFunctions();
      debugPrint('$_tag: Successfully loaded health analytics library');
      _initialized = true;
      return true;
    } catch (e) {
      debugPrint('$_tag: Failed to load native library: $e');
      return false;
    }
  }

  /// Initialize function pointers to the native C++ functions
  void _initFunctions() {
    if (_nativeLib == null) {
      throw StateError('Native library not loaded');
    }

    _calculateBasicStats = _nativeLib!
        .lookup<NativeFunction<_CalculateBasicStatsC>>('calculate_basic_stats')
        .asFunction();

    _detectTrend = _nativeLib!
        .lookup<NativeFunction<_DetectTrendC>>('detect_trend')
        .asFunction();

    _findStrongestCorrelations = _nativeLib!
        .lookup<NativeFunction<_FindStrongestCorrelationsC>>(
            'find_strongest_correlations')
        .asFunction();

    _freeCorrelationResults = _nativeLib!
        .lookup<NativeFunction<_FreeCorrelationResultsC>>(
            'free_correlation_results')
        .asFunction();

    _findMultivariateCorrelations = _nativeLib!
        .lookup<NativeFunction<_FindMultivariateCorrelationsC>>(
            'find_multivariate_correlations')
        .asFunction();

    _freeMultivariateCorrelations = _nativeLib!
        .lookup<NativeFunction<_FreeMultivariateCorrelationsC>>(
            'free_multivariate_correlations')
        .asFunction();

    _performClusterAnalysis = _nativeLib!
        .lookup<NativeFunction<_PerformClusterAnalysisC>>(
            'perform_cluster_analysis')
        .asFunction();

    _freeClusterResults = _nativeLib!
        .lookup<NativeFunction<_FreeClusterResultsC>>('free_cluster_results')
        .asFunction();

    _predictTimeSeries = _nativeLib!
        .lookup<NativeFunction<_PredictTimeSeriesC>>('predict_time_series')
        .asFunction();

    _detectAnomalies = _nativeLib!
        .lookup<NativeFunction<_DetectAnomaliesC>>('detect_anomalies')
        .asFunction();

    _freeAnomalyResults = _nativeLib!
        .lookup<NativeFunction<_FreeAnomalyResultsC>>('free_anomaly_results')
        .asFunction();

    _rankFactorImpacts = _nativeLib!
        .lookup<NativeFunction<_RankFactorImpactsC>>('rank_factor_impacts')
        .asFunction();

    _freeFactorImpactResults = _nativeLib!
        .lookup<NativeFunction<_FreeFactorImpactResultsC>>(
            'free_factor_impact_results')
        .asFunction();

    _analyzeDatePatterns = _nativeLib!
        .lookup<NativeFunction<_AnalyzeDatePatternsC>>('analyze_date_patterns')
        .asFunction();

    _freeDatePatternResults = _nativeLib!
        .lookup<NativeFunction<_FreeDatePatternResultsC>>(
            'free_date_pattern_results')
        .asFunction();

    _analyzeCycles = _nativeLib!
        .lookup<NativeFunction<_AnalyzeCyclesC>>('analyze_cycles')
        .asFunction();

    _analyzeMedicationImpact = _nativeLib!
        .lookup<NativeFunction<_AnalyzeMedicationImpactC>>(
            'analyze_medication_impact')
        .asFunction();

    _freeMedicationImpactAnalysis = _nativeLib!
        .lookup<NativeFunction<_FreeMedicationImpactAnalysisC>>(
            'free_medication_impact_analysis')
        .asFunction();

    _analyzeHormoneImpact = _nativeLib!
        .lookup<NativeFunction<_AnalyzeHormoneImpactC>>(
            'analyze_hormone_impact')
        .asFunction();

    _freeHormoneImpactAnalysis = _nativeLib!
        .lookup<NativeFunction<_FreeHormoneImpactAnalysisC>>(
            'free_hormone_impact_analysis')
        .asFunction();
  }

  /// Check if the service is initialized
  bool get isInitialized => _initialized;

  /// Calculate basic statistics for a variable
  HealthStatistics calculateStatistics(List<double> values) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (values.isEmpty) {
      throw ArgumentError('Cannot calculate statistics on empty data');
    }

    final valuesPtr = _allocateDoubleArray(values);

    try {
      final stats = _calculateBasicStats(valuesPtr, values.length);

      return HealthStatistics(
        mean: stats.mean,
        median: stats.median,
        min: stats.min,
        max: stats.max,
        stdDev: stats.stdDev,
        variance: stats.variance,
        skewness: stats.skewness,
        kurtosis: stats.kurtosis,
        q1: stats.q1,
        q3: stats.q3,
        iqr: stats.iqr,
      );
    } finally {
      calloc.free(valuesPtr);
    }
  }

  /// Detect trend in a time series
  TrendAnalysis detectTrend(List<double> values) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (values.length < 3) {
      throw ArgumentError('Need at least 3 data points for trend analysis');
    }

    final valuesPtr = _allocateDoubleArray(values);
    final strengthPtr = calloc<Double>();

    try {
      final trendTypeValue =
          _detectTrend(valuesPtr, values.length, strengthPtr);
      final strength = strengthPtr.value;

      // Convert the C enum to our Dart enum
      final trendType = TrendType.values.firstWhere(
        (type) => type.value == trendTypeValue,
        orElse: () => TrendType.none,
      );

      return TrendAnalysis(
        type: trendType,
        strength: strength,
        description: _getTrendDescription(trendType, strength),
      );
    } finally {
      calloc.free(valuesPtr);
      calloc.free(strengthPtr);
    }
  }

  /// Find correlations between a target variable and multiple factors
  List<FactorCorrelation> findCorrelations(
    List<double> target,
    Map<String, List<double>> factorData,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (factorData.isEmpty || target.isEmpty) {
      throw ArgumentError('Target and factor data cannot be empty');
    }

    // Extract factor names and data
    final factorNames = factorData.keys.toList();
    final factors = factorNames.map((name) => factorData[name]!).toList();

    // Check that all factors have the same length as the target
    for (final factor in factors) {
      if (factor.length != target.length) {
        throw ArgumentError(
            'All factors must have the same length as the target');
      }
    }

    // Allocate memory for target
    final targetPtr = _allocateDoubleArray(target);

    // Allocate memory for factors array
    final factorsPtr = calloc<Pointer<Double>>(factors.length);
    for (int i = 0; i < factors.length; i++) {
      factorsPtr[i] = _allocateDoubleArray(factors[i]);
    }

    // Create an array of char* pointers for the C++ function
    final charPtrsArray = calloc<Pointer<Char>>(factorNames.length);
    final utf8Pointers = <Pointer<Utf8>>[]; // Keep references to free later

    for (int i = 0; i < factorNames.length; i++) {
      final utf8Ptr = factorNames[i].toNativeUtf8();
      utf8Pointers.add(utf8Ptr); // Save reference
      charPtrsArray[i] = utf8Ptr.cast<Char>();
    }

    try {
      final resultsPtr = _findStrongestCorrelations(
        targetPtr,
        factorsPtr.cast(),
        charPtrsArray,
        target.length,
        factors.length,
      );

      final results = <FactorCorrelation>[];
      var i = 0;

      // Read results until we find a terminator (factorIndex = -1)
      while (resultsPtr[i].factorIndex >= 0) {
        results.add(FactorCorrelation(
          factorName: resultsPtr[i].factorName,
          correlation: resultsPtr[i].correlation,
          pValue: resultsPtr[i].pValue,
          confidence: resultsPtr[i].confidence,
        ));
        i++;
      }

      _freeCorrelationResults(resultsPtr);
      return results;
    } finally {
      // Free allocated memory
      calloc.free(targetPtr);

      for (int i = 0; i < factors.length; i++) {
        calloc.free(factorsPtr[i]);
      }
      calloc.free(factorsPtr);

      // Free the UTF-8 pointers
      for (final ptr in utf8Pointers) {
        calloc.free(ptr);
      }
      calloc.free(charPtrsArray);
    }
  }

  /// Find multivariate correlations between factors
  List<MultivariateRelationship> findMultivariateCorrelations(
    Map<String, List<double>> factorData,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (factorData.isEmpty) {
      throw ArgumentError('Factor data cannot be empty');
    }

    final factorNames = factorData.keys.toList();
    final factors = factorNames.map((name) => factorData[name]!).toList();

    // Check that all lists have the same length
    final dataLength = factors.first.length;
    for (final factor in factors) {
      if (factor.length != dataLength) {
        throw ArgumentError('All factors must have the same length');
      }
    }

    // Allocate memory for factors array
    final dataPtr = calloc<Pointer<Double>>(factors.length);
    for (int i = 0; i < factors.length; i++) {
      dataPtr[i] = _allocateDoubleArray(factors[i]);
    }

    // Create an array of char* pointers for the C++ function
    final charPtrsArray = calloc<Pointer<Char>>(factorNames.length);
    final utf8Pointers = <Pointer<Utf8>>[]; // Keep references to free later

    for (int i = 0; i < factorNames.length; i++) {
      final utf8Ptr = factorNames[i].toNativeUtf8();
      utf8Pointers.add(utf8Ptr); // Save reference
      charPtrsArray[i] = utf8Ptr.cast<Char>();
    }

    try {
      final resultsPtr = _findMultivariateCorrelations(
        dataPtr.cast(),
        charPtrsArray,
        factors.length,
        dataLength,
      );

      final results = <MultivariateRelationship>[];
      var i = 0;

      // Read results until we find a terminator (factorCount = -1)
      while (resultsPtr[i].factorCount > 0) {
        // Extract factor names and weights
        final factorList = <String>[];
        final weightList = <double>[];

        for (int j = 0; j < resultsPtr[i].factorCount; j++) {
          factorList.add(resultsPtr[i].getFactorName(j, resultsPtr + i));
          weightList.add(resultsPtr[i].getFactorWeight(j, resultsPtr + i));
        }

        results.add(MultivariateRelationship(
          factorNames: factorList,
          correlationStrength: resultsPtr[i].correlationStrength,
          description: resultsPtr[i].description,
          confidence: resultsPtr[i].confidence,
          factorWeights: weightList,
          relationshipType: RelationshipType.values.firstWhere(
            (type) => type.value == resultsPtr[i].relationshipType,
            orElse: () => RelationshipType.correlation,
          ),
        ));
        i++;
      }

      _freeMultivariateCorrelations(resultsPtr);
      return results;
    } finally {
      // Free allocated memory
      for (int i = 0; i < factors.length; i++) {
        calloc.free(dataPtr[i]);
      }
      calloc.free(dataPtr);

      // Free the UTF-8 pointers
      for (final ptr in utf8Pointers) {
        calloc.free(ptr);
      }
      calloc.free(charPtrsArray);
    }
  }

  /// Perform cluster analysis to identify patterns in data
  List<ClusterPattern> performClusterAnalysis(
    Map<String, List<double>> factorData,
    int maxClusters,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (factorData.isEmpty || maxClusters <= 0) {
      throw ArgumentError('Invalid input for cluster analysis');
    }

    final factorNames = factorData.keys.toList();
    final factors = factorNames.map((name) => factorData[name]!).toList();

    // Check that all lists have the same length
    final dataLength = factors.first.length;
    for (final factor in factors) {
      if (factor.length != dataLength) {
        throw ArgumentError('All factors must have the same length');
      }
    }

    // Allocate memory for factors array
    final dataPtr = calloc<Pointer<Double>>(factors.length);
    for (int i = 0; i < factors.length; i++) {
      dataPtr[i] = _allocateDoubleArray(factors[i]);
    }

    try {
      final resultsPtr = _performClusterAnalysis(
        dataPtr.cast(),
        factors.length,
        dataLength,
        maxClusters,
      );

      final results = <ClusterPattern>[];
      var i = 0;

      // Read results until we find a terminator (clusterId = -1)
      while (i < maxClusters && resultsPtr[i].clusterId >= 0) {
        // Extract key factors for this cluster
        final keyFactors = <String>[];
        final factorImportance = <double>[];

        for (int j = 0; j < factors.length; j++) {
          final factorIndex = resultsPtr[i].getFactorIndex(j, resultsPtr + i);

          if (factorIndex >= 0 && factorIndex < factorNames.length) {
            keyFactors.add(factorNames[factorIndex]);
            factorImportance
                .add(resultsPtr[i].getFactorWeight(j, resultsPtr + i));
          }
        }

        results.add(ClusterPattern(
          id: resultsPtr[i].clusterId,
          name: resultsPtr[i].clusterName,
          description: resultsPtr[i].description,
          pointCount: resultsPtr[i].dataPointCount,
          significance: resultsPtr[i].significance,
          keyFactors: keyFactors,
          factorImportance: factorImportance,
        ));
        i++;
      }

      _freeClusterResults(resultsPtr);
      return results;
    } finally {
      // Free allocated memory
      for (int i = 0; i < factors.length; i++) {
        calloc.free(dataPtr[i]);
      }
      calloc.free(dataPtr);
    }
  }

  /// Predict future values for a time series
  TimeSeriesPrediction predictTimeSeries(
    String factorName,
    List<double> timeSeriesData,
    int stepsAhead,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (timeSeriesData.isEmpty || stepsAhead <= 0) {
      throw ArgumentError('Invalid input for time series prediction');
    }

    // Allocate memory for time series data
    final dataPtr = _allocateDoubleArray(timeSeriesData);

    // Allocate memory for factor name
    final factorNamePtr = factorName.toNativeUtf8();

    try {
      final result = _predictTimeSeries(
        dataPtr,
        timeSeriesData.length,
        stepsAhead,
        factorNamePtr.cast<Char>(),
      );

      final resultPointer = calloc<_TimeSeriesForecast>();
      resultPointer.ref = result;

      // Extract predictions and confidence intervals
      final predictions = <double>[];
      final confidenceIntervals = <List<double>>[];

      for (int i = 0; i < stepsAhead && i < 30; i++) {
        predictions.add(result.getPrediction(i));
        confidenceIntervals.add(result.getConfidenceInterval(i, resultPointer));
      }

      calloc.free(resultPointer);

      return TimeSeriesPrediction(
        factorName: factorName,
        predictions: predictions,
        confidenceIntervals: confidenceIntervals,
        confidence: result.overallConfidence,
        seasonalityPeriod: result.seasonalityPeriod,
        timeUnit: TimeUnit.values[result.timeUnit],
      );
    } finally {
      calloc.free(dataPtr);
      calloc.free(factorNamePtr);
    }
  }

  /// Detect anomalies in a time series
  List<AnomalyResult> detectAnomalies(
    String factorName,
    List<double> timeSeriesData,
    List<DateTime> dates,
    double threshold,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (timeSeriesData.isEmpty || timeSeriesData.length != dates.length) {
      throw ArgumentError('Invalid input for anomaly detection');
    }

    // Allocate memory for time series data
    final dataPtr = _allocateDoubleArray(timeSeriesData);

    // Allocate memory for dates
    final datesPtr = calloc<_DateStruct>(dates.length);
    for (int i = 0; i < dates.length; i++) {
      datesPtr[i].year = dates[i].year;
      datesPtr[i].month = dates[i].month;
      datesPtr[i].day = dates[i].day;
    }

    // Allocate memory for factor name
    final factorNamePtr = factorName.toNativeUtf8();

    try {
      final resultsPtr = _detectAnomalies(
        dataPtr,
        timeSeriesData.length,
        threshold,
        datesPtr,
        factorNamePtr.cast<Char>(),
      );

      final results = <AnomalyResult>[];
      var i = 0;

      // Read results until we find a terminator (dataPointIndex = -1)
      while (resultsPtr[i].dataPointIndex >= 0) {
        final dateStruct = resultsPtr[i].date;
        DateTime? anomalyDate;

        if (dateStruct.year > 0) {
          anomalyDate =
              DateTime(dateStruct.year, dateStruct.month, dateStruct.day);
        } else if (resultsPtr[i].dataPointIndex < dates.length) {
          anomalyDate = dates[resultsPtr[i].dataPointIndex];
        }

        results.add(AnomalyResult(
          factorName: factorName,
          dataPointIndex: resultsPtr[i].dataPointIndex,
          anomalyScore: resultsPtr[i].anomalyScore,
          description: resultsPtr[i].description,
          originalValue: resultsPtr[i].originalValue,
          expectedValue: resultsPtr[i].expectedValue,
          date: anomalyDate,
          confidence: resultsPtr[i].confidence,
          anomalyType: AnomalyType.values[resultsPtr[i].anomalyType],
        ));
        i++;
      }

      _freeAnomalyResults(resultsPtr);
      return results;
    } finally {
      calloc.free(dataPtr);
      calloc.free(datesPtr);
      calloc.free(factorNamePtr);
    }
  }

  /// Rank factors by their impact on a target variable
  List<FactorImpact> rankFactorImpacts(
    Map<String, List<double>> factorData,
    List<double> targetData,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (factorData.isEmpty || targetData.isEmpty) {
      throw ArgumentError('Invalid input for factor impact analysis');
    }

    final factorNames = factorData.keys.toList();
    final factors = factorNames.map((name) => factorData[name]!).toList();

    // Check that all factors have the same length as the target
    for (final factor in factors) {
      if (factor.length != targetData.length) {
        throw ArgumentError(
            'All factors must have the same length as the target');
      }
    }

    // Allocate memory for target
    final targetPtr = _allocateDoubleArray(targetData);

    // Allocate memory for factors array
    final factorsPtr = calloc<Pointer<Double>>(factors.length);
    for (int i = 0; i < factors.length; i++) {
      factorsPtr[i] = _allocateDoubleArray(factors[i]);
    }

    // Create an array of char* pointers for the C++ function
    final charPtrsArray = calloc<Pointer<Char>>(factorNames.length);
    final utf8Pointers = <Pointer<Utf8>>[]; // Keep references to free later

    for (int i = 0; i < factorNames.length; i++) {
      final utf8Ptr = factorNames[i].toNativeUtf8();
      utf8Pointers.add(utf8Ptr); // Save reference
      charPtrsArray[i] = utf8Ptr.cast<Char>();
    }

    try {
      final resultsPtr = _rankFactorImpacts(
        factorsPtr.cast(),
        targetPtr,
        charPtrsArray,
        factors.length,
        targetData.length,
      );

      final results = <FactorImpact>[];
      var i = 0;

      // Read results until we find a terminator (factorIndex = -1)
      while (resultsPtr[i].factorIndex >= 0) {
        results.add(FactorImpact(
          factorName: resultsPtr[i].factorName,
          impactScore: resultsPtr[i].impactScore,
          directEffect: resultsPtr[i].directEffect,
          indirectEffect: resultsPtr[i].indirectEffect,
          confidence: resultsPtr[i].confidence,
          mechanism: resultsPtr[i].mechanism,
        ));
        i++;
      }

      _freeFactorImpactResults(resultsPtr);
      return results;
    } finally {
      // Free allocated memory
      calloc.free(targetPtr);

      for (int i = 0; i < factors.length; i++) {
        calloc.free(factorsPtr[i]);
      }
      calloc.free(factorsPtr);

      // Free the UTF-8 pointers
      for (final ptr in utf8Pointers) {
        calloc.free(ptr);
      }
      calloc.free(charPtrsArray);
    }
  }

  /// Analyze patterns related to dates (e.g., day of week effects)
  List<DatePattern> analyzeDatePatterns(
    String factorName,
    List<double> values,
    List<DateTime> dates,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (values.isEmpty || values.length != dates.length) {
      throw ArgumentError('Invalid input for date pattern analysis');
    }

    // Allocate memory for values
    final valuesPtr = _allocateDoubleArray(values);

    // Allocate memory for dates
    final datesPtr = calloc<_DateStruct>(dates.length);
    for (int i = 0; i < dates.length; i++) {
      datesPtr[i].year = dates[i].year;
      datesPtr[i].month = dates[i].month;
      datesPtr[i].day = dates[i].day;
    }

    // Allocate memory for factor name
    final factorNamePtr = factorName.toNativeUtf8();

    try {
      final resultsPtr = _analyzeDatePatterns(
        valuesPtr,
        datesPtr,
        values.length,
        factorNamePtr.cast<Char>(),
      );

      final results = <DatePattern>[];
      var i = 0;

      // Read results until we find a terminator (patternType = -1)
      while (resultsPtr[i].patternType >= 0) {
        // Get all peak values for weekly patterns
        final peakValues = resultsPtr[i].peakValues;

        results.add(DatePattern(
          type: PatternType.values.firstWhere(
            (type) => type.value == resultsPtr[i].patternType,
            orElse: () => PatternType.none,
          ),
          periodicity: resultsPtr[i].periodicity,
          strength: resultsPtr[i].strength,
          description: resultsPtr[i].description,
          peakValues: peakValues,
          peakDayOfWeek: resultsPtr[i].peakDayOfWeek,
          peakDayOfMonth: resultsPtr[i].peakDayOfMonth,
          peakMonth: resultsPtr[i].peakMonth,
        ));
        i++;
      }

      _freeDatePatternResults(resultsPtr);
      return results;
    } finally {
      calloc.free(valuesPtr);
      calloc.free(datesPtr);
      calloc.free(factorNamePtr);
    }
  }

  /// Analyze cyclical patterns (e.g., hormonal cycles)
  CycleAnalysis analyzeCycles(
    String factorName,
    List<double> values,
    List<DateTime> dates,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (values.isEmpty || values.length != dates.length) {
      throw ArgumentError('Invalid input for cycle analysis');
    }

    // Allocate memory for values
    final valuesPtr = _allocateDoubleArray(values);

    // Allocate memory for dates
    final datesPtr = calloc<_DateStruct>(dates.length);
    for (int i = 0; i < dates.length; i++) {
      datesPtr[i].year = dates[i].year;
      datesPtr[i].month = dates[i].month;
      datesPtr[i].day = dates[i].day;
    }

    // Allocate memory for factor name
    final factorNamePtr = factorName.toNativeUtf8();

    try {
      final result = _analyzeCycles(
        valuesPtr,
        datesPtr,
        values.length,
        factorNamePtr.cast<Char>(),
      );

      if (result.cycleLength <= 0) {
        return CycleAnalysis(
          cycleLength: 0,
          cycleLengthVariance: 0,
          amplitude: 0,
          phaseShift: 0,
          confidence: 0,
          description: 'No significant cycles detected',
        );
      }

      return CycleAnalysis(
        cycleLength: result.cycleLength,
        cycleLengthVariance: result.cycleLengthVariance,
        amplitude: result.amplitude,
        phaseShift: result.phaseShift,
        confidence: result.confidence,
        description: result.description,
      );
    } finally {
      calloc.free(valuesPtr);
      calloc.free(datesPtr);
      calloc.free(factorNamePtr);
    }
  }

  /// Analyze the impact of medication on health metrics
  MedicationImpactResult analyzeMedicationImpact(
    String medicationName,
    String factorName,
    List<double> beforeData,
    List<double> afterData,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (beforeData.isEmpty || afterData.isEmpty) {
      throw ArgumentError('Invalid input for medication impact analysis');
    }

    // Allocate memory for before and after data
    final beforePtr = _allocateDoubleArray(beforeData);
    final afterPtr = _allocateDoubleArray(afterData);

    // Allocate memory for names
    final medicationNamePtr = medicationName.toNativeUtf8();
    final factorNamePtr = factorName.toNativeUtf8();

    try {
      final resultPtr = _analyzeMedicationImpact(
        beforePtr,
        beforeData.length,
        afterPtr,
        afterData.length,
        medicationNamePtr.cast<Char>(),
        factorNamePtr.cast<Char>(),
      );

      final result = MedicationImpactResult(
        medicationName: medicationName,
        factorName: factorName,
        beforeMean: resultPtr.ref.beforeMean,
        afterMean: resultPtr.ref.afterMean,
        changeMagnitude: resultPtr.ref.changeMagnitude,
        changeSignificance: resultPtr.ref.changeSignificance,
        overallImpact: resultPtr.ref.overallImpact,
        daysToEffect: resultPtr.ref.daysToEffect,
        description: resultPtr.ref.description,
      );

      _freeMedicationImpactAnalysis(resultPtr);
      return result;
    } finally {
      calloc.free(beforePtr);
      calloc.free(afterPtr);
      calloc.free(medicationNamePtr);
      calloc.free(factorNamePtr);
    }
  }

  /// Analyze hormone levels and their impact on health
  HormoneImpactResult analyzeHormoneImpact(
    String hormoneName,
    List<double> hormoneLevels,
    Map<String, List<double>> factorData,
    double minOptimalLevel,
    double maxOptimalLevel,
  ) {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (hormoneLevels.isEmpty || factorData.isEmpty) {
      throw ArgumentError('Invalid input for hormone impact analysis');
    }

    final factorNames = factorData.keys.toList();
    final factors = factorNames.map((name) => factorData[name]!).toList();

    // Check that all factors have the same length as the hormone levels
    for (final factor in factors) {
      if (factor.length != hormoneLevels.length) {
        throw ArgumentError(
            'All factors must have the same length as hormone levels');
      }
    }

    // Allocate memory for hormone levels
    final levelsPtr = _allocateDoubleArray(hormoneLevels);

    // Allocate memory for factors array
    final factorsPtr = calloc<Pointer<Double>>(factors.length);
    for (int i = 0; i < factors.length; i++) {
      factorsPtr[i] = _allocateDoubleArray(factors[i]);
    }

    // Create an array of char* pointers for the C++ function
    final charPtrsArray = calloc<Pointer<Char>>(factorNames.length);
    final utf8Pointers = <Pointer<Utf8>>[]; // Keep references to free later

    for (int i = 0; i < factorNames.length; i++) {
      final utf8Ptr = factorNames[i].toNativeUtf8();
      utf8Pointers.add(utf8Ptr); // Save reference
      charPtrsArray[i] = utf8Ptr.cast<Char>();
    }

    // Allocate memory for hormone name
    final hormoneNamePtr = hormoneName.toNativeUtf8();

    try {
      final resultPtr = _analyzeHormoneImpact(
        levelsPtr,
        hormoneLevels.length,
        factorsPtr.cast(),
        charPtrsArray,
        factors.length,
        hormoneNamePtr.cast<Char>(),
        minOptimalLevel,
        maxOptimalLevel,
      );

      // Extract factor impacts
      final factorImpacts = <String, double>{};
      for (int i = 0; i < factors.length; i++) {
        final factorName = resultPtr.ref.getFactorName(i, resultPtr);

        if (factorName.isNotEmpty) {
          factorImpacts[factorName] =
              resultPtr.ref.getFactorImpact(i, resultPtr);
        }
      }

      final result = HormoneImpactResult(
        hormoneName: hormoneName,
        currentLevel: resultPtr.ref.currentLevel,
        optimalLevel: resultPtr.ref.optimalLevel,
        optimalRangeLower: resultPtr.ref.optimalRangeLower,
        optimalRangeUpper: resultPtr.ref.optimalRangeUpper,
        deviation: resultPtr.ref.deviation,
        impactOnMood: resultPtr.ref.impactOnMood,
        impactOnEnergy: resultPtr.ref.impactOnEnergy,
        factorImpacts: factorImpacts,
        description: resultPtr.ref.description,
      );

      _freeHormoneImpactAnalysis(resultPtr);
      return result;
    } finally {
      calloc.free(levelsPtr);

      for (int i = 0; i < factors.length; i++) {
        calloc.free(factorsPtr[i]);
      }
      calloc.free(factorsPtr);

      // Free the UTF-8 pointers
      for (final ptr in utf8Pointers) {
        calloc.free(ptr);
      }
      calloc.free(charPtrsArray);

      calloc.free(hormoneNamePtr);
    }
  }

  /// Integrated analysis of all health data
  Future<ComprehensiveHealthAnalysis> generateComprehensiveAnalysis({
    required List<MoodEntry> moodEntries,
    required List<Medication> medications,
    required Set<String> takenMedications,
    required List<Bloodwork> bloodworkEntries,
    required String timeframe,
  }) async {
    if (!_initialized) {
      throw StateError('Health analytics service not initialized');
    }

    if (moodEntries.isEmpty) {
      throw ArgumentError(
          'Mood entries cannot be empty for comprehensive analysis');
    }

    // Extract data from mood entries
    final factorData = await _extractFactorData(moodEntries);

    // Analyze mood trends
    final moodTrend = detectTrend(factorData['Mood'] ?? []);

    // Find correlations between factors
    final correlations = findMultivariateCorrelations(factorData);

    // Identify clusters
    final clusters = performClusterAnalysis(factorData, 4);

    // Detect anomalies
    final anomalies = <AnomalyResult>[];
    final dates = moodEntries.map((e) => e.date).toList();

    for (final factor in factorData.keys) {
      if (factorData[factor]!.length > 5) {
        anomalies.addAll(detectAnomalies(
          factor,
          factorData[factor]!,
          dates.sublist(0, factorData[factor]!.length),
          2.0, // 2 standard deviations threshold
        ));
      }
    }

    // Rank factors by impact on mood
    final moodFactors = Map<String, List<double>>.from(factorData);
    moodFactors.remove('Mood');

    final factorImpacts = rankFactorImpacts(
      moodFactors,
      factorData['Mood'] ?? [],
    );

    // Make predictions
    final predictions = <TimeSeriesPrediction>[];

    for (final factor in factorData.keys) {
      if (factorData[factor]!.length >= 10) {
        predictions.add(predictTimeSeries(
          factor,
          factorData[factor]!,
          7, // 7 days ahead
        ));
      }
    }

    // Analyze date patterns
    final datePatterns = <DatePattern>[];

    for (final factor in factorData.keys) {
      if (factorData[factor]!.length >= 14) {
        // Require at least 2 weeks of data
        datePatterns.addAll(analyzeDatePatterns(
          factor,
          factorData[factor]!,
          dates.sublist(0, factorData[factor]!.length),
        ));
      }
    }

    // Analyze cyclical patterns
    final cycleAnalyses = <CycleAnalysis>[];

    for (final factor in factorData.keys) {
      if (factorData[factor]!.length >= 20) {
        // Need enough data for cycles
        final analysis = analyzeCycles(
          factor,
          factorData[factor]!,
          dates.sublist(0, factorData[factor]!.length),
        );

        if (analysis.cycleLength > 0) {
          cycleAnalyses.add(analysis);
        }
      }
    }

    // Analyze bloodwork if available
    final hormoneImpacts = <HormoneImpactResult>[];

    if (bloodworkEntries.isNotEmpty) {
      // Extract hormone data
      final hormoneReadings = _extractHormoneReadings(bloodworkEntries);

      for (final hormone in hormoneReadings.keys) {
        final readings = hormoneReadings[hormone]!;
        if (readings.length >= 3) {
          final values = readings.map((r) => r.value).toList();

          // Get optimal range from the readings
          final minOptimal = readings.first.minValue ?? 0;
          final maxOptimal = readings.first.maxValue ?? 100;

          hormoneImpacts.add(analyzeHormoneImpact(
            hormone,
            values,
            factorData,
            minOptimal,
            maxOptimal,
          ));
        }
      }
    }

    // Analyze medication impact if available
    final medicationImpacts = <MedicationImpactResult>[];

    for (final medication in medications) {
      if (takenMedications.contains(medication.id)) {
        // Split mood entries into before and after
        final beforeEntries = moodEntries
            .where((e) => e.date.isBefore(medication.startDate))
            .toList();

        final afterEntries = moodEntries
            .where((e) => !e.date.isBefore(medication.startDate))
            .toList();

        if (beforeEntries.length >= 7 && afterEntries.length >= 7) {
          // Extract mood scores
          final beforeMood =
              beforeEntries.map((e) => 4 - e.mood.index.toDouble()).toList();

          final afterMood =
              afterEntries.map((e) => 4 - e.mood.index.toDouble()).toList();

          medicationImpacts.add(analyzeMedicationImpact(
            medication.name,
            'Mood',
            beforeMood,
            afterMood,
          ));

          // Analyze impact on other factors if available
          for (final factor in ['Sleep', 'Energy', 'Focus']) {
            if (factorData.containsKey(factor)) {
              // Extract before/after data for this factor
              final beforeFactor = <double>[];
              final afterFactor = <double>[];

              for (final entry in beforeEntries) {
                double? value;

                if (factor == 'Sleep' && entry.sleepQuality != null) {
                  value = 4 - entry.sleepQuality!.index.toDouble();
                } else if (factor == 'Energy' && entry.energyLevel != null) {
                  value = 4 - entry.energyLevel!.index.toDouble();
                } else if (factor == 'Focus' && entry.focusLevel != null) {
                  value = 4 - entry.focusLevel!.index.toDouble();
                }

                if (value != null) {
                  beforeFactor.add(value);
                }
              }

              for (final entry in afterEntries) {
                double? value;

                if (factor == 'Sleep' && entry.sleepQuality != null) {
                  value = 4 - entry.sleepQuality!.index.toDouble();
                } else if (factor == 'Energy' && entry.energyLevel != null) {
                  value = 4 - entry.energyLevel!.index.toDouble();
                } else if (factor == 'Focus' && entry.focusLevel != null) {
                  value = 4 - entry.focusLevel!.index.toDouble();
                }

                if (value != null) {
                  afterFactor.add(value);
                }
              }

              if (beforeFactor.length >= 5 && afterFactor.length >= 5) {
                medicationImpacts.add(analyzeMedicationImpact(
                  medication.name,
                  factor,
                  beforeFactor,
                  afterFactor,
                ));
              }
            }
          }
        }
      }
    }

    // Generate comprehensive insights
    return ComprehensiveHealthAnalysis(
      moodTrend: moodTrend,
      factorCorrelations: correlations,
      clusters: clusters,
      anomalies: anomalies,
      factorImpacts: factorImpacts,
      predictions: predictions,
      datePatterns: datePatterns,
      cycleAnalyses: cycleAnalyses,
      hormoneImpacts: hormoneImpacts,
      medicationImpacts: medicationImpacts,
      analysisDate: DateTime.now(),
      timeframe: timeframe,
    );
  }

  // Helper methods for data extraction and conversion

  Future<Map<String, List<double>>> _extractFactorData(
      List<MoodEntry> entries) async {
    final factorData = <String, List<double>>{};

    // Extract mood scores (0-4 scale, with 4 being great)
    factorData['Mood'] =
        entries.map((e) => 4 - e.mood.index.toDouble()).toList();

    // Process health metrics
    final entriesWithSleep =
        entries.where((e) => e.sleepQuality != null).toList();
    final entriesWithEnergy =
        entries.where((e) => e.energyLevel != null).toList();
    final entriesWithAppetite =
        entries.where((e) => e.appetiteLevel != null).toList();
    final entriesWithFocus =
        entries.where((e) => e.focusLevel != null).toList();
    final entriesWithDysphoria =
        entries.where((e) => e.dysphoriaLevel != null).toList();
    final entriesWithLibido =
        entries.where((e) => e.libidoLevel != null).toList();
    final entriesWithExercise =
        entries.where((e) => e.exerciseLevel != null).toList();

    // Add health metrics to factor data (if we have enough data points)
    if (entriesWithSleep.length >= 5) {
      factorData['Sleep'] = entriesWithSleep
          .map((e) => 4 - e.sleepQuality!.index.toDouble())
          .toList();
    }

    if (entriesWithEnergy.length >= 5) {
      factorData['Energy'] = entriesWithEnergy
          .map((e) => 4 - e.energyLevel!.index.toDouble())
          .toList();
    }

    if (entriesWithAppetite.length >= 5) {
      factorData['Appetite'] = entriesWithAppetite
          .map((e) => 4 - e.appetiteLevel!.index.toDouble())
          .toList();
    }

    if (entriesWithFocus.length >= 5) {
      factorData['Focus'] = entriesWithFocus
          .map((e) => 4 - e.focusLevel!.index.toDouble())
          .toList();
    }

    if (entriesWithDysphoria.length >= 5) {
      factorData['Dysphoria'] = entriesWithDysphoria
          .map((e) => 4 - e.dysphoriaLevel!.index.toDouble())
          .toList();
    }

    if (entriesWithLibido.length >= 5) {
      factorData['Libido'] = entriesWithLibido
          .map((e) => 4 - e.libidoLevel!.index.toDouble())
          .toList();
    }

    if (entriesWithExercise.length >= 5) {
      factorData['Exercise'] = entriesWithExercise
          .map((e) => 4 - e.exerciseLevel!.index.toDouble())
          .toList();
    }

    // Analyze emotions - add each one as a factor
    final emotionCounts = <String, int>{};

    // Count emotions
    for (final entry in entries) {
      for (final emotion in entry.emotions) {
        emotionCounts[emotion.name] = (emotionCounts[emotion.name] ?? 0) + 1;
      }
    }

    // Add emotions as factors (if we have enough instances)
    for (final emotionName in emotionCounts.keys) {
      if (emotionCounts[emotionName]! >= 5) {
        // Create binary array (1 if emotion present, 0 if not)
        final emotionPresence = entries.map((entry) {
          return entry.emotions.any((e) => e.name == emotionName) ? 1.0 : 0.0;
        }).toList();

        factorData['Emotion: $emotionName'] = emotionPresence;
      }
    }

    return factorData;
  }

  Map<String, List<HormoneReading>> _extractHormoneReadings(
      List<Bloodwork> entries) {
    final hormoneReadings = <String, List<HormoneReading>>{};

    for (final entry in entries) {
      for (final reading in entry.hormoneReadings) {
        if (!hormoneReadings.containsKey(reading.name)) {
          hormoneReadings[reading.name] = [];
        }

        // Create a new HormoneReading with date info added
        final readingWithDate = HormoneReading(
          name: reading.name,
          value: reading.value,
          unit: reading.unit,
          date: entry.date,
          minValue: reading.minValue,
          maxValue: reading.maxValue,
        );

        hormoneReadings[reading.name]!.add(readingWithDate);
      }
    }

    return hormoneReadings;
  }

  // Helper methods for FFI
  Pointer<Double> _allocateDoubleArray(List<double> values) {
    final ptr = calloc<Double>(values.length);
    for (int i = 0; i < values.length; i++) {
      ptr[i] = values[i];
    }
    return ptr;
  }

  // Helper for trend description
  String _getTrendDescription(TrendType type, double strength) {
    final strengthDesc =
        strength > 0.7 ? 'strong' : (strength > 0.5 ? 'moderate' : 'weak');

    switch (type) {
      case TrendType.increasing:
        return 'A $strengthDesc increasing trend is detected';
      case TrendType.decreasing:
        return 'A $strengthDesc decreasing trend is detected';
      case TrendType.cyclic:
        return 'A $strengthDesc cyclic pattern is detected';
      case TrendType.variable:
        return 'A variable pattern with $strengthDesc fluctuations is detected';
      case TrendType.none:
        return 'No significant trend detected';
    }
  }
}

// Define missing FFI structs and typedefs

// Basic date structure
base class _DateStruct extends Struct {
  @Int32()
  external int year;

  @Int32()
  external int month;

  @Int32()
  external int day;
}

// Basic statistics result
base class _BasicStats extends Struct {
  @Double()
  external double mean;

  @Double()
  external double median;

  @Double()
  external double min;

  @Double()
  external double max;

  @Double()
  external double stdDev;

  @Double()
  external double variance;

  @Double()
  external double skewness;

  @Double()
  external double kurtosis;

  @Double()
  external double q1;

  @Double()
  external double q3;

  @Double()
  external double iqr;
}

// Function typedefs for FFI calls

// Basic statistics
typedef _CalculateBasicStatsC = _BasicStats Function(
    Pointer<Double> values, Int32 length);
typedef _CalculateBasicStatsDart = _BasicStats Function(
    Pointer<Double> values, int length);

// Trend detection
typedef _DetectTrendC = Int32 Function(
    Pointer<Double> values, Int32 length, Pointer<Double> strength);
typedef _DetectTrendDart = int Function(
    Pointer<Double> values, int length, Pointer<Double> strength);

// Correlation analysis
typedef _FindStrongestCorrelationsC = Pointer<_CorrelationResult> Function(
    Pointer<Double> targetValues,
    Pointer<Pointer<Double>> factorValues,
    Pointer<Pointer<Char>> factorNames,
    Int32 dataLength,
    Int32 factorCount);
typedef _FindStrongestCorrelationsDart = Pointer<_CorrelationResult> Function(
    Pointer<Double> targetValues,
    Pointer<Pointer<Double>> factorValues,
    Pointer<Pointer<Char>> factorNames,
    int dataLength,
    int factorCount);

typedef _FreeCorrelationResultsC = Void Function(
    Pointer<_CorrelationResult> results);
typedef _FreeCorrelationResultsDart = void Function(
    Pointer<_CorrelationResult> results);

// Multivariate correlation analysis
typedef _FindMultivariateCorrelationsC
    = Pointer<_MultivariateCorrelation> Function(
        Pointer<Pointer<Double>> factorData,
        Pointer<Pointer<Char>> factorNames,
        Int32 factorCount,
        Int32 dataLength);
typedef _FindMultivariateCorrelationsDart
    = Pointer<_MultivariateCorrelation> Function(
        Pointer<Pointer<Double>> factorData,
        Pointer<Pointer<Char>> factorNames,
        int factorCount,
        int dataLength);

typedef _FreeMultivariateCorrelationsC = Void Function(
    Pointer<_MultivariateCorrelation> correlations);
typedef _FreeMultivariateCorrelationsDart = void Function(
    Pointer<_MultivariateCorrelation> correlations);

// Cluster analysis
typedef _PerformClusterAnalysisC = Pointer<_ClusterResult> Function(
    Pointer<Pointer<Double>> factorData,
    Int32 factorCount,
    Int32 dataLength,
    Int32 maxClusters);
typedef _PerformClusterAnalysisDart = Pointer<_ClusterResult> Function(
    Pointer<Pointer<Double>> factorData,
    int factorCount,
    int dataLength,
    int maxClusters);

typedef _FreeClusterResultsC = Void Function(Pointer<_ClusterResult> results);
typedef _FreeClusterResultsDart = void Function(
    Pointer<_ClusterResult> results);

// Time series forecasting
typedef _PredictTimeSeriesC = _TimeSeriesForecast Function(
    Pointer<Double> timeSeriesData,
    Int32 dataLength,
    Int32 stepsAhead,
    Pointer<Char> factorName);
typedef _PredictTimeSeriesDart = _TimeSeriesForecast Function(
    Pointer<Double> timeSeriesData,
    int dataLength,
    int stepsAhead,
    Pointer<Char> factorName);

// Anomaly detection
typedef _DetectAnomaliesC = Pointer<_AnomalyResult> Function(
    Pointer<Double> timeSeriesData,
    Int32 dataLength,
    Double threshold,
    Pointer<_DateStruct> dates,
    Pointer<Char> factorName);
typedef _DetectAnomaliesDart = Pointer<_AnomalyResult> Function(
    Pointer<Double> timeSeriesData,
    int dataLength,
    double threshold,
    Pointer<_DateStruct> dates,
    Pointer<Char> factorName);

typedef _FreeAnomalyResultsC = Void Function(Pointer<_AnomalyResult> results);
typedef _FreeAnomalyResultsDart = void Function(
    Pointer<_AnomalyResult> results);

// Factor impact ranking
typedef _RankFactorImpactsC = Pointer<_FactorImpactResult> Function(
    Pointer<Pointer<Double>> factorData,
    Pointer<Double> targetData,
    Pointer<Pointer<Char>> factorNames,
    Int32 factorCount,
    Int32 dataLength);
typedef _RankFactorImpactsDart = Pointer<_FactorImpactResult> Function(
    Pointer<Pointer<Double>> factorData,
    Pointer<Double> targetData,
    Pointer<Pointer<Char>> factorNames,
    int factorCount,
    int dataLength);

typedef _FreeFactorImpactResultsC = Void Function(
    Pointer<_FactorImpactResult> results);
typedef _FreeFactorImpactResultsDart = void Function(
    Pointer<_FactorImpactResult> results);

// Date pattern analysis
typedef _AnalyzeDatePatternsC = Pointer<_DatePatternResult> Function(
    Pointer<Double> values,
    Pointer<_DateStruct> dates,
    Int32 dataLength,
    Pointer<Char> factorName);
typedef _AnalyzeDatePatternsDart = Pointer<_DatePatternResult> Function(
    Pointer<Double> values,
    Pointer<_DateStruct> dates,
    int dataLength,
    Pointer<Char> factorName);

typedef _FreeDatePatternResultsC = Void Function(
    Pointer<_DatePatternResult> results);
typedef _FreeDatePatternResultsDart = void Function(
    Pointer<_DatePatternResult> results);

// Cycle analysis
typedef _AnalyzeCyclesC = _CycleAnalysisResult Function(Pointer<Double> values,
    Pointer<_DateStruct> dates, Int32 dataLength, Pointer<Char> factorName);
typedef _AnalyzeCyclesDart = _CycleAnalysisResult Function(
    Pointer<Double> values,
    Pointer<_DateStruct> dates,
    int dataLength,
    Pointer<Char> factorName);

// Medication impact analysis
typedef _AnalyzeMedicationImpactC = Pointer<_MedicationImpactAnalysis> Function(
    Pointer<Double> beforeData,
    Int32 beforeLength,
    Pointer<Double> afterData,
    Int32 afterLength,
    Pointer<Char> medicationName,
    Pointer<Char> factorName);
typedef _AnalyzeMedicationImpactDart
    = Pointer<_MedicationImpactAnalysis> Function(
        Pointer<Double> beforeData,
        int beforeLength,
        Pointer<Double> afterData,
        int afterLength,
        Pointer<Char> medicationName,
        Pointer<Char> factorName);

typedef _FreeMedicationImpactAnalysisC = Void Function(
    Pointer<_MedicationImpactAnalysis> analysis);
typedef _FreeMedicationImpactAnalysisDart = void Function(
    Pointer<_MedicationImpactAnalysis> analysis);

// Hormone impact analysis
typedef _AnalyzeHormoneImpactC = Pointer<_HormoneImpactAnalysis> Function(
    Pointer<Double> hormoneLevels,
    Int32 dataLength,
    Pointer<Pointer<Double>> factorData,
    Pointer<Pointer<Char>> factorNames,
    Int32 factorCount,
    Pointer<Char> hormoneName,
    Double minOptimalLevel,
    Double maxOptimalLevel);
typedef _AnalyzeHormoneImpactDart = Pointer<_HormoneImpactAnalysis> Function(
    Pointer<Double> hormoneLevels,
    int dataLength,
    Pointer<Pointer<Double>> factorData,
    Pointer<Pointer<Char>> factorNames,
    int factorCount,
    Pointer<Char> hormoneName,
    double minOptimalLevel,
    double maxOptimalLevel);

typedef _FreeHormoneImpactAnalysisC = Void Function(
    Pointer<_HormoneImpactAnalysis> analysis);
typedef _FreeHormoneImpactAnalysisDart = void Function(
    Pointer<_HormoneImpactAnalysis> analysis);

// Add struct definitions for FFI that match the C++ structs

base class _CorrelationResult extends Struct {
  @Int32()
  external int factorIndex;

  @Double()
  external double correlation;

  @Double()
  external double pValue;

  @Double()
  external double confidence;

  // Use Array<Char> for fixed-length string fields in FFI
  @Array(maxStringSize)
  external Array<Char> _factorName;

  // Getter to convert Char array to Dart string
  String get factorName => _factorName.toDartString();
}

// Define a constant for maximum string size
const int maxStringSize = 200;
const int maxFactors = 50;

base class _MultivariateCorrelation extends Struct {
  @Int32()
  external int factorCount;

  // We can't directly map 2D arrays in Dart FFI, so skip declaring them here
  // But adjust the other fields to account for their memory space

  @Double()
  external double correlationStrength;

  @Array(maxStringSize)
  external Array<Char> _description;

  @Double()
  external double confidence;

  @Int32()
  external int relationshipType;

  @Int32()
  external int primaryFactorIndex;

  @Int32()
  external int secondaryFactorIndex;

  @Int32()
  external int tertiaryFactorIndex;

  // Method to get factor name at index - fixed version
  String getFactorName(int index, Pointer<_MultivariateCorrelation> ptr) {
    if (index < 0 || index >= ptr.ref.factorCount) return '';

    // factorNames starts immediately after factorCount
    final baseAddress = ptr.address + sizeOf<Int32>();
    final nameOffset = index * maxStringSize;

    return Pointer<Char>.fromAddress(baseAddress + nameOffset)
        .cast<Utf8>()
        .toDartString();
  }

  double getFactorWeight(int index, Pointer<_MultivariateCorrelation> ptr) {
    if (index < 0 || index >= ptr.ref.factorCount) return 0.0;

    // factorWeights starts after the 2D factorNames array
    final baseAddress = ptr.address +
        sizeOf<Int32>() + // factorCount
        (maxStringSize * maxStringSize); // factorNames 2D array

    final weightOffset = index * sizeOf<Double>();
    return Pointer<Double>.fromAddress(baseAddress + weightOffset).value;
  }

  // Getter for description string
  String get description => _description.toDartString();
}

base class _ClusterResult extends Struct {
  @Int32()
  external int clusterId;

  @Array(maxStringSize)
  external Array<Char> _clusterName;

  @Array(maxStringSize)
  external Array<Char> _description;

  @Int32()
  external int dataPointCount;

  @Double()
  external double significance;

  // Methods to access arrays at proper memory locations

  // Get factor index at position
  int getFactorIndex(int index, Pointer<_ClusterResult> ptr) {
    if (index < 0 || index >= maxFactors) return -1;

    // Calculate offset to factorIndices within the struct
    final baseAddress = ptr.address +
        sizeOf<Int32>() + // clusterId
        (2 * maxStringSize) + // _clusterName + _description
        sizeOf<Int32>() + // dataPointCount
        sizeOf<Double>(); // significance

    final indexOffset = index * sizeOf<Int32>();
    return Pointer<Int32>.fromAddress(baseAddress + indexOffset).value;
  }

  double getFactorWeight(int index, Pointer<_ClusterResult> ptr) {
    if (index < 0 || index >= maxFactors) return 0.0;

    // factorWeights starts after factorIndices
    final baseAddress = ptr.address +
        sizeOf<Int32>() + // clusterId
        (2 * maxStringSize) + // _clusterName + _description
        sizeOf<Int32>() + // dataPointCount
        sizeOf<Double>() + // significance
        (maxFactors * sizeOf<Int32>()); // factorIndices array

    final weightOffset = index * sizeOf<Double>();
    return Pointer<Double>.fromAddress(baseAddress + weightOffset).value;
  }

  // Getters for string fields
  String get clusterName => _clusterName.toDartString();
  String get description => _description.toDartString();
}

base class _TimeSeriesForecast extends Struct {
  // Define array fields
  @Array(30)
  external Array<Double> _predictions;

  // Cannot directly represent 2D arrays in FFI, need custom accessor method

  @Double()
  external double overallConfidence;

  @Int32()
  external int seasonalityPeriod;

  @Int32()
  external int timeUnit;

  @Array(maxStringSize)
  external Array<Char> _factorName;

  // Accessor methods

  // Get prediction at index
  double getPrediction(int index) {
    if (index < 0 || index >= 30) return 0.0;
    return _predictions[index];
  }

  // Get confidence interval at index
  List<double> getConfidenceInterval(
      int index, Pointer<_TimeSeriesForecast> ptr) {
    if (index < 0 || index >= 30) return [0.0, 0.0];

    // confidenceIntervals follow the predictions array
    final baseAddress =
        ptr.address + (30 * sizeOf<Double>()); // After predictions array

    // Each confidence interval has 2 values (lower and upper)
    final intervalOffset = index * (2 * sizeOf<Double>());

    final lowerBound =
        Pointer<Double>.fromAddress(baseAddress + intervalOffset).value;
    final upperBound = Pointer<Double>.fromAddress(
            baseAddress + intervalOffset + sizeOf<Double>())
        .value;

    return [lowerBound, upperBound];
  }

  // Getter for string field
  String get factorName => _factorName.toDartString();
}

base class _AnomalyResult extends Struct {
  @Int32()
  external int dataPointIndex;

  @Double()
  external double anomalyScore;

  @Array(maxStringSize)
  external Array<Char> _description;

  @Double()
  external double originalValue;

  @Double()
  external double expectedValue;

  external _DateStruct date;

  @Double()
  external double confidence;

  @Int32()
  external int anomalyType;

  @Array(maxStringSize)
  external Array<Char> _factorName;

  // Getters for string fields
  String get description => _description.toDartString();
  String get factorName => _factorName.toDartString();
}

base class _FactorImpactResult extends Struct {
  @Int32()
  external int factorIndex;

  @Array(maxStringSize)
  external Array<Char> _factorName;

  @Double()
  external double impactScore;

  @Double()
  external double directEffect;

  @Double()
  external double indirectEffect;

  @Double()
  external double confidence;

  @Array(maxStringSize)
  external Array<Char> _mechanism;

  // Getters for string fields
  String get factorName => _factorName.toDartString();
  String get mechanism => _mechanism.toDartString();
}

base class _DatePatternResult extends Struct {
  @Int32()
  external int patternType;

  @Int32()
  external int periodicity;

  @Double()
  external double strength;

  @Array(maxStringSize)
  external Array<Char> _description;

  @Array(7)
  external Array<Double> _peakValues;

  @Int32()
  external int peakDayOfWeek;

  @Int32()
  external int peakDayOfMonth;

  @Int32()
  external int peakMonth;

  // Getters for string field and array
  String get description => _description.toDartString();

  // Get peak value at index
  double getPeakValue(int index) {
    if (index < 0 || index >= 7) return 0.0;
    return _peakValues[index];
  }

  // Get all peak values as a list
  List<double> get peakValues {
    final result = <double>[];
    for (int i = 0; i < 7; i++) {
      result.add(_peakValues[i]);
    }
    return result;
  }
}

base class _CycleAnalysisResult extends Struct {
  @Double()
  external double cycleLength;

  @Double()
  external double cycleLengthVariance;

  @Double()
  external double amplitude;

  @Double()
  external double phaseShift;

  @Double()
  external double confidence;

  @Array(maxStringSize)
  external Array<Char> _description;

  // Getter for string field
  String get description => _description.toDartString();
}

base class _MedicationImpactAnalysis extends Struct {
  @Array(maxStringSize)
  external Array<Char> _medicationName;

  @Array(maxStringSize)
  external Array<Char> _factorName;

  @Double()
  external double beforeMean;

  @Double()
  external double afterMean;

  @Double()
  external double changeMagnitude;

  @Double()
  external double changeSignificance;

  @Double()
  external double overallImpact;

  @Int32()
  external int daysToEffect;

  @Array(maxStringSize)
  external Array<Char> _description;

  // Getters for string fields
  String get medicationName => _medicationName.toDartString();
  String get factorName => _factorName.toDartString();
  String get description => _description.toDartString();
}

base class _HormoneImpactAnalysis extends Struct {
  @Array(maxStringSize)
  external Array<Char> _hormoneName;

  @Double()
  external double optimalLevel;

  @Double()
  external double optimalRangeLower;

  @Double()
  external double optimalRangeUpper;

  @Double()
  external double currentLevel;

  @Double()
  external double deviation;

  @Double()
  external double impactOnMood;

  @Double()
  external double impactOnEnergy;

  @Array(maxStringSize)
  external Array<Char> _description;

  // Methods to access factor impacts and names

  // Get factor impact at index
  double getFactorImpact(int index, Pointer<_HormoneImpactAnalysis> ptr) {
    if (index < 0 || index >= maxFactors) return 0.0;

    // impactOnOtherFactors array follows the basic fields
    final baseAddress = ptr.address +
        maxStringSize + // hormoneName
        (8 *
            sizeOf<
                Double>()) + // 8 double fields (from optimalLevel through impactOnEnergy)
        maxStringSize; // description

    final impactOffset = index * sizeOf<Double>();
    return Pointer<Double>.fromAddress(baseAddress + impactOffset).value;
  }

  String getFactorName(int index, Pointer<_HormoneImpactAnalysis> ptr) {
    if (index < 0 || index >= maxFactors) return '';

    // factorNames array follows after impactOnOtherFactors array
    final baseAddress = ptr.address +
        maxStringSize + // hormoneName
        (8 * sizeOf<Double>()) + // 8 double fields
        maxStringSize + // description
        (maxFactors * sizeOf<Double>()); // impacts array

    final nameOffset = index * maxStringSize;
    return Pointer<Char>.fromAddress(baseAddress + nameOffset)
        .cast<Utf8>()
        .toDartString();
  }

  // Getters for string fields
  String get hormoneName => _hormoneName.toDartString();
  String get description => _description.toDartString();
}

// Extension to convert Char arrays to Dart strings
extension CharArrayToDartString on Array<Char> {
  String toDartString() {
    final buffer = StringBuffer();
    for (int i = 0; i < maxStringSize; i++) {
      final char = this[i];
      if (char == 0) break; // Null terminator
      buffer.writeCharCode(char);
    }
    return buffer.toString();
  }
}

// Define model classes that represent the data structures in Dart

// Enums
enum TrendType {
  none(0),
  increasing(1),
  decreasing(2),
  cyclic(3),
  variable(4);

  final int value;
  const TrendType(this.value);
}

enum RelationshipType {
  correlation(0),
  causation(1),
  coincidence(2);

  final int value;
  const RelationshipType(this.value);
}

enum TimeUnit {
  days(0),
  weeks(1),
  months(2);

  final int value;
  const TimeUnit(this.value);
}

enum AnomalyType {
  outlier(0),
  trendChange(1),
  seasonalityChange(2);

  final int value;
  const AnomalyType(this.value);
}

enum PatternType {
  none(0),
  daily(1),
  weekly(2),
  monthly(3),
  custom(4);

  final int value;
  const PatternType(this.value);
}

// Data classes

class HealthStatistics {
  final double mean;
  final double median;
  final double min;
  final double max;
  final double stdDev;
  final double variance;
  final double skewness;
  final double kurtosis;
  final double q1;
  final double q3;
  final double iqr;

  HealthStatistics({
    required this.mean,
    required this.median,
    required this.min,
    required this.max,
    required this.stdDev,
    required this.variance,
    required this.skewness,
    required this.kurtosis,
    required this.q1,
    required this.q3,
    required this.iqr,
  });
}

class TrendAnalysis {
  final TrendType type;
  final double strength;
  final String description;

  TrendAnalysis({
    required this.type,
    required this.strength,
    required this.description,
  });
}

class FactorCorrelation {
  final String factorName;
  final double correlation;
  final double pValue;
  final double confidence;

  FactorCorrelation({
    required this.factorName,
    required this.correlation,
    required this.pValue,
    required this.confidence,
  });
}

class MultivariateRelationship {
  final List<String> factorNames;
  final double correlationStrength;
  final String description;
  final double confidence;
  final List<double> factorWeights;
  final RelationshipType relationshipType;

  MultivariateRelationship({
    required this.factorNames,
    required this.correlationStrength,
    required this.description,
    required this.confidence,
    required this.factorWeights,
    required this.relationshipType,
  });
}

class ClusterPattern {
  final int id;
  final String name;
  final String description;
  final int pointCount;
  final double significance;
  final List<String> keyFactors;
  final List<double> factorImportance;

  ClusterPattern({
    required this.id,
    required this.name,
    required this.description,
    required this.pointCount,
    required this.significance,
    required this.keyFactors,
    required this.factorImportance,
  });
}

class TimeSeriesPrediction {
  final String factorName;
  final List<double> predictions;
  final List<List<double>> confidenceIntervals;
  final double confidence;
  final int seasonalityPeriod;
  final TimeUnit timeUnit;

  TimeSeriesPrediction({
    required this.factorName,
    required this.predictions,
    required this.confidenceIntervals,
    required this.confidence,
    required this.seasonalityPeriod,
    required this.timeUnit,
  });
}

class AnomalyResult {
  final String factorName;
  final int dataPointIndex;
  final double anomalyScore;
  final String description;
  final double originalValue;
  final double expectedValue;
  final DateTime? date;
  final double confidence;
  final AnomalyType anomalyType;

  AnomalyResult({
    required this.factorName,
    required this.dataPointIndex,
    required this.anomalyScore,
    required this.description,
    required this.originalValue,
    required this.expectedValue,
    required this.date,
    required this.confidence,
    required this.anomalyType,
  });
}

class FactorImpact {
  final String factorName;
  final double impactScore;
  final double directEffect;
  final double indirectEffect;
  final double confidence;
  final String mechanism;

  FactorImpact({
    required this.factorName,
    required this.impactScore,
    required this.directEffect,
    required this.indirectEffect,
    required this.confidence,
    required this.mechanism,
  });
}

class DatePattern {
  final PatternType type;
  final int periodicity;
  final double strength;
  final String description;
  final List<double> peakValues;
  final int peakDayOfWeek;
  final int peakDayOfMonth;
  final int peakMonth;

  DatePattern({
    required this.type,
    required this.periodicity,
    required this.strength,
    required this.description,
    required this.peakValues,
    required this.peakDayOfWeek,
    required this.peakDayOfMonth,
    required this.peakMonth,
  });
}

class CycleAnalysis {
  final double cycleLength;
  final double cycleLengthVariance;
  final double amplitude;
  final double phaseShift;
  final double confidence;
  final String description;

  CycleAnalysis({
    required this.cycleLength,
    required this.cycleLengthVariance,
    required this.amplitude,
    required this.phaseShift,
    required this.confidence,
    required this.description,
  });
}

class MedicationImpactResult {
  final String medicationName;
  final String factorName;
  final double beforeMean;
  final double afterMean;
  final double changeMagnitude;
  final double changeSignificance;
  final double overallImpact;
  final int daysToEffect;
  final String description;

  MedicationImpactResult({
    required this.medicationName,
    required this.factorName,
    required this.beforeMean,
    required this.afterMean,
    required this.changeMagnitude,
    required this.changeSignificance,
    required this.overallImpact,
    required this.daysToEffect,
    required this.description,
  });
}

class HormoneImpactResult {
  final String hormoneName;
  final double currentLevel;
  final double optimalLevel;
  final double optimalRangeLower;
  final double optimalRangeUpper;
  final double deviation;
  final double impactOnMood;
  final double impactOnEnergy;
  final Map<String, double> factorImpacts;
  final String description;

  HormoneImpactResult({
    required this.hormoneName,
    required this.currentLevel,
    required this.optimalLevel,
    required this.optimalRangeLower,
    required this.optimalRangeUpper,
    required this.deviation,
    required this.impactOnMood,
    required this.impactOnEnergy,
    required this.factorImpacts,
    required this.description,
  });
}

class ComprehensiveHealthAnalysis {
  final TrendAnalysis moodTrend;
  final List<MultivariateRelationship> factorCorrelations;
  final List<ClusterPattern> clusters;
  final List<AnomalyResult> anomalies;
  final List<FactorImpact> factorImpacts;
  final List<TimeSeriesPrediction> predictions;
  final List<DatePattern> datePatterns;
  final List<CycleAnalysis> cycleAnalyses;
  final List<HormoneImpactResult> hormoneImpacts;
  final List<MedicationImpactResult> medicationImpacts;
  final DateTime analysisDate;
  final String timeframe;

  ComprehensiveHealthAnalysis({
    required this.moodTrend,
    required this.factorCorrelations,
    required this.clusters,
    required this.anomalies,
    required this.factorImpacts,
    required this.predictions,
    required this.datePatterns,
    required this.cycleAnalyses,
    required this.hormoneImpacts,
    required this.medicationImpacts,
    required this.analysisDate,
    required this.timeframe,
  });
}
