// ABOUTME: Utility functions for unit conversions and data validation in the health tracker
// ABOUTME: Handles weight unit conversions, blood pressure validation, and date/time formatting
import 'package:albi/utils/constants.dart';
import 'package:intl/intl.dart';

/// Utility class for unit conversions and validation
class UnitConverter {
  // Private constructor to prevent instantiation
  UnitConverter._();

  // Weight Conversions
  
  /// Convert weight from one unit to another
  /// 
  /// [weight] - The weight value to convert
  /// [fromUnit] - The source unit ('kg', 'lbs', 'st')
  /// [toUnit] - The target unit ('kg', 'lbs', 'st')
  /// 
  /// Returns the converted weight value
  static double convertWeight(double weight, String fromUnit, String toUnit) {
    if (fromUnit == toUnit) return weight;

    // First convert to kg (our canonical unit)
    double weightInKg;
    switch (fromUnit) {
      case UnitConstants.kgUnit:
        weightInKg = weight;
        break;
      case UnitConstants.poundsUnit:
        weightInKg = weight * UnitConstants.poundsToKg;
        break;
      case UnitConstants.stonesUnit:
        weightInKg = weight * UnitConstants.stonesToKg;
        break;
      default:
        throw ArgumentError('Invalid source weight unit: $fromUnit');
    }

    // Then convert from kg to target unit
    switch (toUnit) {
      case UnitConstants.kgUnit:
        return weightInKg;
      case UnitConstants.poundsUnit:
        return weightInKg * UnitConstants.kgToPounds;
      case UnitConstants.stonesUnit:
        return weightInKg * UnitConstants.kgToStones;
      default:
        throw ArgumentError('Invalid target weight unit: $toUnit');
    }
  }

  /// Convert weight to kilograms (canonical storage unit)
  static double convertToKg(double weight, String fromUnit) {
    return convertWeight(weight, fromUnit, UnitConstants.kgUnit);
  }

  /// Convert weight from kilograms to specified unit
  static double convertFromKg(double weightKg, String toUnit) {
    return convertWeight(weightKg, UnitConstants.kgUnit, toUnit);
  }

  /// Get appropriate decimal places for weight unit
  static int getWeightDecimalPlaces(String unit) {
    switch (unit) {
      case UnitConstants.stonesUnit:
        return 2; // More precision needed for stones
      case UnitConstants.kgUnit:
      case UnitConstants.poundsUnit:
      default:
        return 1;
    }
  }

  /// Format weight value with appropriate decimal places
  static String formatWeight(double weight, String unit) {
    final decimalPlaces = getWeightDecimalPlaces(unit);
    return '${weight.toStringAsFixed(decimalPlaces)} $unit';
  }

  // Weight Validation

  /// Validate weight value is within reasonable range
  static bool isValidWeight(double weight, String unit) {
    final weightInKg = convertToKg(weight, unit);
    return weightInKg >= HealthDefaults.minWeight && 
           weightInKg <= HealthDefaults.maxWeight;
  }

  /// Get weight validation error message
  static String? getWeightValidationError(double weight, String unit) {
    if (!isValidWeight(weight, unit)) {
      final minInUnit = convertFromKg(HealthDefaults.minWeight, unit);
      final maxInUnit = convertFromKg(HealthDefaults.maxWeight, unit);
      return 'Weight must be between ${formatWeight(minInUnit, unit)} and ${formatWeight(maxInUnit, unit)}';
    }
    return null;
  }

  // Blood Pressure Validation

  /// Validate systolic blood pressure value
  static bool isValidSystolic(int systolic) {
    return systolic >= HealthDefaults.minBloodPressure && 
           systolic <= HealthDefaults.maxSystolic;
  }

  /// Validate diastolic blood pressure value
  static bool isValidDiastolic(int diastolic) {
    return diastolic >= HealthDefaults.minBloodPressure && 
           diastolic <= HealthDefaults.maxDiastolic;
  }

  /// Validate blood pressure reading (both systolic and diastolic)
  static bool isValidBloodPressure(int systolic, int diastolic) {
    return isValidSystolic(systolic) && 
           isValidDiastolic(diastolic) && 
           systolic > diastolic; // Systolic must be higher than diastolic
  }

  /// Get blood pressure validation error message
  static String? getBloodPressureValidationError(int systolic, int diastolic) {
    if (!isValidSystolic(systolic)) {
      return 'Systolic pressure must be between ${HealthDefaults.minBloodPressure} and ${HealthDefaults.maxSystolic} mmHg';
    }
    
    if (!isValidDiastolic(diastolic)) {
      return 'Diastolic pressure must be between ${HealthDefaults.minBloodPressure} and ${HealthDefaults.maxDiastolic} mmHg';
    }
    
    if (systolic <= diastolic) {
      return 'Systolic pressure must be higher than diastolic pressure';
    }
    
    return null;
  }

  // Date/Time Formatting

  /// Format DateTime for display
  static String formatDate(DateTime date) {
    return DateFormat(HealthDefaults.dateFormat).format(date);
  }

  /// Format DateTime for time display
  static String formatTime(DateTime time) {
    return DateFormat(HealthDefaults.timeFormat).format(time);
  }

  /// Format DateTime for full date and time display
  static String formatDateTime(DateTime dateTime) {
    return DateFormat(HealthDefaults.dateTimeFormat).format(dateTime);
  }

  /// Format DateTime for relative display (e.g., "2 hours ago")
  static String formatRelativeTime(DateTime dateTime) {
    final now = DateTime.now();
    final difference = now.difference(dateTime);

    if (difference.inDays > 0) {
      return '${difference.inDays} day${difference.inDays == 1 ? '' : 's'} ago';
    } else if (difference.inHours > 0) {
      return '${difference.inHours} hour${difference.inHours == 1 ? '' : 's'} ago';
    } else if (difference.inMinutes > 0) {
      return '${difference.inMinutes} minute${difference.inMinutes == 1 ? '' : 's'} ago';
    } else {
      return 'Just now';
    }
  }

  /// Check if date is today
  static bool isToday(DateTime date) {
    final now = DateTime.now();
    return date.year == now.year && 
           date.month == now.month && 
           date.day == now.day;
  }

  /// Check if date is yesterday
  static bool isYesterday(DateTime date) {
    final yesterday = DateTime.now().subtract(const Duration(days: 1));
    return date.year == yesterday.year && 
           date.month == yesterday.month && 
           date.day == yesterday.day;
  }

  /// Get smart date format (Today, Yesterday, or date)
  static String formatSmartDate(DateTime date) {
    if (isToday(date)) {
      return 'Today';
    } else if (isYesterday(date)) {
      return 'Yesterday';
    } else {
      return formatDate(date);
    }
  }

  // Date Validation

  /// Validate that date is not in the future
  static bool isValidDate(DateTime date) {
    return !date.isAfter(DateTime.now());
  }

  /// Get date validation error message
  static String? getDateValidationError(DateTime date) {
    if (!isValidDate(date)) {
      return ValidationConstants.futureDateError;
    }
    return null;
  }

  // General Utilities

  /// Get list of supported weight units
  static List<String> getSupportedWeightUnits() {
    return [
      UnitConstants.kgUnit,
      UnitConstants.poundsUnit,
      UnitConstants.stonesUnit,
    ];
  }

  /// Get display name for weight unit
  static String getWeightUnitDisplayName(String unit) {
    switch (unit) {
      case UnitConstants.kgUnit:
        return 'Kilograms';
      case UnitConstants.poundsUnit:
        return 'Pounds';
      case UnitConstants.stonesUnit:
        return 'Stones';
      default:
        return unit;
    }
  }

  /// Round weight to appropriate precision for unit
  static double roundWeightForUnit(double weight, String unit) {
    final decimalPlaces = getWeightDecimalPlaces(unit);
    final factor = _pow10(decimalPlaces);
    return (weight * factor).round() / factor;
  }

  /// Helper function to calculate power of 10
  static double _pow10(int exponent) {
    double result = 1.0;
    for (int i = 0; i < exponent; i++) {
      result *= 10.0;
    }
    return result;
  }
}
