import 'dart:developer';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/water_monitor/models/insert_user_activity_request_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/models/undo_user_activity_request_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/models/update_user_detail_request_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/models/user_progress_models.dart';
import 'package:hmg_patient_app_new/features/water_monitor/models/water_cup_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';

class WaterMonitorViewModel extends ChangeNotifier {
  WaterMonitorRepo waterMonitorRepo;
  ErrorHandlerService errorHandlerService;

  WaterMonitorViewModel({
    required this.waterMonitorRepo,
    required this.errorHandlerService,
  });

  // Controllers
  final TextEditingController nameController = TextEditingController();
  final TextEditingController heightController = TextEditingController();
  final TextEditingController weightController = TextEditingController();
  final TextEditingController ageController = TextEditingController();

  // Units
  final List<String> heightUnits = ['cm', 'm', 'ft', 'in'];
  final List<String> heightUnitsEn = ['cm', 'm', 'ft', 'in'];
  final List<String> heightUnitsAr = ['سم', 'م', 'قدم', 'بوصة'];

  final List<String> weightUnits = ['kg', 'lb'];
  final List<String> weightUnitsEn = ['kg', 'lb'];
  final List<String> weightUnitsAr = ['كجم', 'رطل'];

  List<String> getHeightUnitsList() {
    return getIt.get<AppState>().isArabic() ? heightUnitsAr : heightUnitsEn;
  }

  List<String> getWeightUnitsList() {
    return getIt.get<AppState>().isArabic() ? weightUnitsAr : weightUnitsEn;
  }

  // Selected values
  String _selectedHeightUnit = 'cm';
  String _selectedWeightUnit = 'kg';
  String _selectedActivityLevel = 'Lightly active';
  String _selectedNumberOfReminders = '3 Time';
  String _selectedGender = "Male";

  // ConsumptionScreen

  String _selectedDuration = "Daily";
  bool _isGraphView = true;

  // Validation error message
  String? _validationError;

  // Getters
  String? get validationError => _validationError;

  String get selectedHeightUnit => _selectedHeightUnit;

  // Get the localized display value for the selected height unit
  String get selectedHeightUnitDisplay {
    final index = heightUnitsEn.indexOf(_selectedHeightUnit);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? heightUnitsAr[index] : heightUnitsEn[index];
    }
    return _selectedHeightUnit;
  }

  String get selectedWeightUnit => _selectedWeightUnit;

  // Get the localized display value for the selected weight unit
  String get selectedWeightUnitDisplay {
    final index = weightUnitsEn.indexOf(_selectedWeightUnit);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? weightUnitsAr[index] : weightUnitsEn[index];
    }
    return _selectedWeightUnit;
  }

  String get selectedActivityLevel => _selectedActivityLevel;

  String get selectedNumberOfReminders => _selectedNumberOfReminders;

  String get selectedGender => _selectedGender;

  String get selectedDurationFilter => _selectedDuration;

  bool get isGraphView => _isGraphView;

  // Activity level options
  final List<String> activityLevelsEn = [
    "Almost Inactive (no exercise)",
    "Lightly active",
    "Lightly active (1-3) days per week",
    "Super active (very hard exercise)"
  ];

  final List<String> activityLevelsAr = [
    "غير نشط تقريباً (بدون تمرين)",
    "نشط قليلاً",
    "نشط قليلاً (1-3) أيام في الأسبوع",
    "نشط جداً (تمرين شاق جداً)"
  ];

  List<String> get activityLevels => getIt.get<AppState>().isArabic() ? activityLevelsAr : activityLevelsEn;

  // Get the localized display value for the selected activity level
  String get selectedActivityLevelDisplay {
    final index = activityLevelsEn.indexOf(_selectedActivityLevel);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? activityLevelsAr[index] : activityLevelsEn[index];
    }
    return _selectedActivityLevel;
  }

  // Reminder options
  final List<String> reminderOptionsEn = ["1 Time", "2 Time", "3 Time", "4 Time", "5 Time", "6 Time"];
  final List<String> reminderOptionsAr = ["مرة واحدة", "مرتين", "3 مرات", "4 مرات", "5 مرات", "6 مرات"];

  List<String> get reminderOptions => getIt.get<AppState>().isArabic() ? reminderOptionsAr : reminderOptionsEn;

  // Get the localized display value for the selected number of reminders
  String get selectedNumberOfRemindersDisplay {
    final index = reminderOptionsEn.indexOf(_selectedNumberOfReminders);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? reminderOptionsAr[index] : reminderOptionsEn[index];
    }
    return _selectedNumberOfReminders;
  }

  // Gender options
  final List<String> genderOptionsEn = ["Male", "Female"];
  final List<String> genderOptionsAr = ["ذكر", "أنثى"];

  List<String> get genderOptions => getIt.get<AppState>().isArabic() ? genderOptionsAr : genderOptionsEn;

  // Get the localized display value for the selected gender
  String get selectedGenderDisplay {
    final index = genderOptionsEn.indexOf(_selectedGender);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? genderOptionsAr[index] : genderOptionsEn[index];
    }
    return _selectedGender;
  }

  // Duration Options
  final List<String> durationFiltersEn = ["Daily", "Weekly", "Monthly"];
  final List<String> durationFiltersAr = ["يومي", "أسبوعي", "شهري"];

  List<String> get durationFilters => getIt.get<AppState>().isArabic() ? durationFiltersAr : durationFiltersEn;

  // Get the localized display value for the selected duration
  String get selectedDurationFilterDisplay {
    final index = durationFiltersEn.indexOf(_selectedDuration);
    if (index >= 0) {
      return getIt.get<AppState>().isArabic() ? durationFiltersAr[index] : durationFiltersEn[index];
    }
    return _selectedDuration;
  }

  // Network/data
  final AppState _appState = GetIt.instance<AppState>();
  final NavigationService _navigationService = GetIt.instance<NavigationService>();
  final CacheService _cacheService = GetIt.instance<CacheService>();

  bool _isLoading = false;
  dynamic _userDetailData;
  bool _isWaterReminderEnabled = false;

  // Progress data lists
  List<UserProgressForTodayModel> _todayProgressList = [];
  List<UserProgressForWeekModel> _weekProgressList = [];
  List<UserProgressForMonthModel> _monthProgressList = [];
  List<UserProgressHistoryModel> _historyList = [];

  bool get isLoading => _isLoading;

  dynamic get userDetailData => _userDetailData;

  bool get isWaterReminderEnabled => _isWaterReminderEnabled;

  // Getters for progress data
  List<UserProgressForTodayModel> get todayProgressList => _todayProgressList;

  List<UserProgressForWeekModel> get weekProgressList => _weekProgressList;

  List<UserProgressForMonthModel> get monthProgressList => _monthProgressList;

  List<UserProgressHistoryModel> get historyList => _historyList;

  // Get current progress list based on selected duration
  dynamic get currentProgressData {
    switch (_selectedDuration) {
      case 'Daily':
        return _todayProgressList;
      case 'Weekly':
        return _weekProgressList;
      case 'Monthly':
        return _monthProgressList;
      default:
        return _todayProgressList;
    }
  }

  // Initialize method to be called when needed
  Future<void> initialize() async {
    _initializeDefaultCups();
    _loadReminderEnabledState();
    await fetchUserDetailsForMonitoring();
    // Fetch daily progress to get consumed amount and daily goal
    await fetchUserProgressForMonitoring();
  }

  /// Load reminder enabled state from cache
  void _loadReminderEnabledState() {
    _isWaterReminderEnabled = _cacheService.getBool(key: CacheConst.waterReminderEnabled) ?? false;
    log('Water reminder enabled state loaded: $_isWaterReminderEnabled');
  }

  /// Map selected duration to ProgressType enum
  ProgressType _getProgressTypeFromDuration() {
    switch (_selectedDuration) {
      case 'Daily':
        return ProgressType.today;
      case 'Weekly':
        return ProgressType.week;
      case 'Monthly':
        return ProgressType.month;
      default:
        return ProgressType.today;
    }
  }

  /// Map selected duration to ProgressType enum
  int _getProgressIdFromDuration() {
    switch (_selectedDuration) {
      case 'Daily':
        return 1;
      case 'Weekly':
        return 2;
      case 'Monthly':
        return 3;
      default:
        return 1;
    }
  }

  /// Fetch user progress data based on selected duration
  Future<void> fetchUserProgressForMonitoring() async {
    try {
      _isLoading = true;
      notifyListeners();

      final authenticated = _appState.getAuthenticatedUser();
      if (authenticated == null) {
        _isLoading = false;
        notifyListeners();
        return;
      }

      final mobile = (authenticated.mobileNumber ?? '').replaceAll('+', '').replaceFirst(RegExp(r'^0'), '');
      final identification = authenticated.patientIdentificationNo ?? '';
      final progressType = _getProgressTypeFromDuration();

      final result = await waterMonitorRepo.getUserProgressForWaterMonitoring(
        progressType: progressType,
        mobileNumber: mobile,
        identificationNo: identification,
      );

      result.fold(
        (failure) => errorHandlerService.handleError(failure: failure),
        (apiModel) {
        log("User Progress Data ($_selectedDuration): ${apiModel.data.toString()}");

        // Parse the response based on progress type
        try {
          // Extract progressData and historyData from combined response
          dynamic progressData;
          dynamic historyData;

          if (apiModel.data is Map && apiModel.data.containsKey('progressData')) {
            progressData = apiModel.data['progressData'];
            historyData = apiModel.data['historyData'];
          } else {
            // Fallback to old structure
            progressData = apiModel.data;
          }

          // Parse history data (available for all progress types, especially for daily)
          if (historyData != null && historyData is List) {
            _historyList.clear();
            for (var item in historyData) {
              if (item is Map) {
                _historyList.add(UserProgressHistoryModel.fromJson(item as Map<String, dynamic>));
              }
            }
            log('History data parsed: ${_historyList.length} items');
          }

          if (progressData != null && progressData is List) {
            switch (progressType) {
              case ProgressType.today:
                _todayProgressList.clear();
                for (var item in progressData) {
                  if (item is Map) {
                    _todayProgressList.add(UserProgressForTodayModel.fromJson(item as Map<String, dynamic>));
                  }
                }

                // Update consumed amount and daily goal from API response
                if (_todayProgressList.isNotEmpty) {
                  final todayData = _todayProgressList.first;
                  if (todayData.quantityConsumed != null) {
                    _totalConsumedMl = todayData.quantityConsumed!.toInt();
                    log('Updated consumed from API: $_totalConsumedMl ml');
                  }
                  if (todayData.quantityLimit != null) {
                    _dailyGoalMl = todayData.quantityLimit!.toInt();
                    log('Updated daily goal from API: $_dailyGoalMl ml');
                  }
                }

                break;

              case ProgressType.week:
                _weekProgressList.clear();
                for (var item in progressData) {
                  if (item is Map) {
                    _weekProgressList.add(UserProgressForWeekModel.fromJson(item as Map<String, dynamic>));
                  }
                }
                log('Week Progress: ${_weekProgressList.length} items');
                break;

              case ProgressType.month:
                _monthProgressList.clear();
                for (var item in progressData) {
                  if (item is Map) {
                    _monthProgressList.add(UserProgressForMonthModel.fromJson(item as Map<String, dynamic>));
                  }
                }
                log('Month Progress: ${_monthProgressList.length} items');
                break;
            }
          }
        } catch (e) {
          log('Error parsing progress data: $e');
        }
      });
    } catch (e) {
      log('Exception in fetchUserProgressForMonitoring: $e');
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  Future<void> fetchUserDetailsForMonitoring({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    try {
      _isLoading = true;

      notifyListeners();

      final authenticated = _appState.getAuthenticatedUser();
      if (authenticated == null) {
        _isLoading = false;
        notifyListeners();
        if (onError != null) onError('User not authenticated');
        return;
      }
      final mobile = (authenticated.mobileNumber ?? '').replaceAll('+', '');
      final identification = authenticated.patientIdentificationNo ?? '';

      final result = await waterMonitorRepo.getUserDetailsForWaterMonitoring(
        progress: 1,
        mobileNumber: mobile,
        identificationNo: identification,
      );

      result.fold(
        (failure) {
          errorHandlerService.handleError(failure: failure);
          _userDetailData = null;
          if (onError != null) onError(failure.message);
        },
        (apiModel) {
        _userDetailData = apiModel.data;

        // Populate form fields from the fetched data
        if (_userDetailData != null) {
          _populateFormFields(_userDetailData);
        }

        if (onSuccess != null) onSuccess(_userDetailData);
      });
    } catch (e) {
      _userDetailData = null;
      if (onError != null) onError(e.toString());
    } finally {
      _isLoading = false;
      notifyListeners();

      if (_userDetailData == null) {
        try {
          _navigationService.pushAndReplace(AppRoutes.waterMonitorSettingsPage);
        } catch (navErr) {
          log('Navigation to water monitor settings failed: $navErr');
        }
      }
    }
  }

  /// Populates form fields from the API response data
  void _populateFormFields(dynamic data) {
    if (data == null) return;

    try {
      // Parse the response and populate fields
      if (data is Map) {
        // Name
        if (data['FirstName'] != null) {
          nameController.text = data['FirstName'].toString();
        }

        // Gender
        if (data['Gender'] != null) {
          final gender = data['Gender'].toString();
          if (gender == 'M' || gender == 'Male') {
            _selectedGender = 'Male';
          } else if (gender == 'F' || gender == 'Female') {
            _selectedGender = 'Female';
          }
        }

        // Age - calculate from DOB if available
        if (data['DOB'] != null) {
          final dob = data['DOB'].toString();
          final age = _calculateAgeFromDOB(dob);
          if (age > 0) {
            ageController.text = age.toString();
          }
        }

        // Height
        if (data['Height'] != null) {
          heightController.text = data['Height'].toString();
          // Set height unit
          if (data['IsHeightInCM'] != null) {
            _selectedHeightUnit = data['IsHeightInCM'] == true ? 'cm' : 'in';
          }
        }

        // Weight
        if (data['Weight'] != null) {
          weightController.text = data['Weight'].toString();
          // Set weight unit
          if (data['IsWeightInKG'] != null) {
            _selectedWeightUnit = data['IsWeightInKG'] == true ? 'kg' : 'lb';
          }
        }

        // Activity Level - map ActivityID to activity level
        if (data['ActivityID'] != null) {
          final activityId = data['ActivityID'].toString();
          _selectedActivityLevel = _getActivityLevelFromID(activityId);
        }

        // Number of reminders (if available in response)
        // Note: This may not be in the response, keeping default if not present

        notifyListeners();
      }
    } catch (e) {
      log('Error populating form fields: $e');
    }
  }

  /// Calculate age from DOB string in format /Date(milliseconds+0300)/
  int _calculateAgeFromDOB(String dobString) {
    try {
      // Parse the /Date(milliseconds+0300)/ format
      final regex = RegExp(r'\/Date\((\d+)');
      final match = regex.firstMatch(dobString);
      if (match != null) {
        final milliseconds = int.tryParse(match.group(1)!);
        if (milliseconds != null) {
          final dob = DateTime.fromMillisecondsSinceEpoch(milliseconds);
          final now = DateTime.now();
          int age = now.year - dob.year;
          if (now.month < dob.month || (now.month == dob.month && now.day < dob.day)) {
            age--;
          }
          return age;
        }
      }
    } catch (e) {
      log('Error calculating age from DOB: $e');
    }
    return 0;
  }

  /// Map activity ID to activity level string
  String _getActivityLevelFromID(String activityId) {
    switch (activityId) {
      case '1':
        return "Almost Inactive (no exercise)";
      case '2':
        return "Lightly active";
      case '3':
        return "Lightly active (1-3) days per week";
      case '4':
        return "Super active (very hard exercise)";
      default:
        return "Lightly active";
    }
  }

  /// Populate form fields from authenticated user data (for new users)
  void populateFromAuthenticatedUser() {
    try {
      final authenticated = _appState.getAuthenticatedUser();
      if (authenticated == null) return;

      // Name - use firstName if available
      if (authenticated.firstName != null && authenticated.firstName!.isNotEmpty) {
        nameController.text = authenticated.firstName!;
      }

      // Gender - map from patient gender
      if (authenticated.gender != null) {
        final gender = (authenticated.gender == 1 ? 'male' : 'female').toLowerCase();
        if (gender.contains('m') || gender == 'male') {
          _selectedGender = 'Male';
        } else if (gender.contains('f') || gender == 'female') {
          _selectedGender = 'Female';
        }
      }

      // Age - calculate from DOB if available
      if (authenticated.dateofBirth != null && authenticated.dateofBirth!.isNotEmpty) {
        final age = _calculateAgeFromDOB(authenticated.dateofBirth!);
        if (age > 0) {
          ageController.text = age.toString();
        }
      }

      // Set default units and activity level
      _selectedHeightUnit = 'cm';
      _selectedWeightUnit = 'kg';
      _selectedActivityLevel = 'Lightly active';
      _selectedNumberOfReminders = '3 Time';

      log('Form fields populated from authenticated user: ${authenticated.firstName}');
      notifyListeners();
    } catch (e) {
      log('Error populating from authenticated user: $e');
    }
  }

  // Reset all fields to default
  void resetFields() {
    nameController.clear();
    heightController.clear();
    weightController.clear();
    ageController.clear();
    _selectedHeightUnit = 'cm';
    _selectedWeightUnit = 'kg';
    _selectedActivityLevel = 'Lightly active';
    _selectedNumberOfReminders = '3 Time';
    _selectedGender = "Male";
    notifyListeners();
  }

  // Setters with notification
  void setFilterDuration(String duration) async {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = durationFiltersAr.indexOf(duration);
      if (index >= 0 && index < durationFiltersEn.length) {
        _selectedDuration = durationFiltersEn[index];
      }
    } else {
      _selectedDuration = duration;
    }
    notifyListeners();
    // Fetch new progress data when duration filter changes
    await fetchUserProgressForMonitoring();
  }

  // Setters with notification
  void setGraphView(bool value) {
    _isGraphView = value;
    notifyListeners();
  }

  // Setters with notification
  void setGender(String gender) {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = genderOptionsAr.indexOf(gender);
      if (index >= 0 && index < genderOptionsEn.length) {
        _selectedGender = genderOptionsEn[index];
      }
    } else {
      _selectedGender = gender;
    }
    notifyListeners();
  }

  void setHeightUnit(String unit) {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = heightUnitsAr.indexOf(unit);
      if (index >= 0 && index < heightUnitsEn.length) {
        _selectedHeightUnit = heightUnitsEn[index];
      }
    } else {
      _selectedHeightUnit = unit;
    }
    notifyListeners();
  }

  void setWeightUnit(String unit) {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = weightUnitsAr.indexOf(unit);
      if (index >= 0 && index < weightUnitsEn.length) {
        _selectedWeightUnit = weightUnitsEn[index];
      }
    } else {
      _selectedWeightUnit = unit;
    }
    notifyListeners();
  }

  void setActivityLevel(String level) {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = activityLevelsAr.indexOf(level);
      if (index >= 0 && index < activityLevelsEn.length) {
        _selectedActivityLevel = activityLevelsEn[index];
      }
    } else {
      _selectedActivityLevel = level;
    }
    notifyListeners();
  }

  void setNumberOfReminders(String number) {
    // Map Arabic values to English keys
    if (getIt.get<AppState>().isArabic()) {
      final index = reminderOptionsAr.indexOf(number);
      if (index >= 0 && index < reminderOptionsEn.length) {
        _selectedNumberOfReminders = reminderOptionsEn[index];
      }
    } else {
      _selectedNumberOfReminders = number;
    }
    notifyListeners();
  }

  // Validation methods
  bool get isValid {
    _validationError = null; // Clear previous error

    if (nameController.text.trim().isEmpty) {
      _validationError = 'Name is required';
      return false;
    }

    if (ageController.text.trim().isEmpty) {
      _validationError = 'Age is required';
      return false;
    }

    if (!_isAgeValid()) {
      _validationError = validateAge();
      return false;
    }

    if (heightController.text.trim().isEmpty) {
      _validationError = 'Height is required';
      return false;
    }

    if (!_isHeightValid()) {
      _validationError = validateHeight();
      return false;
    }

    if (weightController.text.trim().isEmpty) {
      _validationError = 'Weight is required';
      return false;
    }

    if (!_isWeightValid()) {
      _validationError = validateWeight();
      return false;
    }

    return true;
  }

  bool _isAgeValid() {
    final age = int.tryParse(ageController.text.trim());
    return age != null && age >= 11 && age <= 120;
  }

  bool _isHeightValid() {
    final height = double.tryParse(heightController.text.trim());
    return height != null && height > 0;
  }

  bool _isWeightValid() {
    final weight = double.tryParse(weightController.text.trim());
    return weight != null && weight > 0;
  }

  String? validateAge() {
    if (ageController.text.trim().isEmpty) {
      return LocaleKeys.ageIsRequired.tr();
    }
    final age = int.tryParse(ageController.text.trim());
    if (age == null) {
      return LocaleKeys.invalidAge.tr();
    }
    if (age < 11 || age > 120) {
      return LocaleKeys.ageMustBeBetween11And120.tr();
    }
    return null;
  }

  String? validateHeight() {
    if (heightController.text.trim().isEmpty) {
      return LocaleKeys.heightIsRequired.tr();
    }
    final height = double.tryParse(heightController.text.trim());
    if (height == null || height <= 0) {
      return LocaleKeys.invalidHeight.tr();
    }
    return null;
  }

  String? validateWeight() {
    if (weightController.text.trim().isEmpty) {
      return LocaleKeys.weightIsRequired.tr();
    }
    final weight = double.tryParse(weightController.text.trim());
    if (weight == null || weight <= 0) {
      return LocaleKeys.invalidWeight.tr();
    }
    return null;
  }

  String _getApiCompatibleGender() {
    if (_selectedGender == "Female") {
      return "F";
    }
    return "M";
  }

  // Save settings
  Future<bool> saveSettings() async {
    if (!isValid) {
      notifyListeners(); // Notify so error can be read
      return false;
    }

    try {
      _isLoading = true;
      _validationError = null;
      notifyListeners();

      // Determine if this is an update or insert
      final isUpdate = _userDetailData != null;

      // Get authenticated user for mobile number and identification number
      final authenticated = _appState.getAuthenticatedUser();
      final mobile = (authenticated?.mobileNumber?.replaceAll('+', '') ?? '').replaceFirst("0", "");
      final identification = authenticated?.patientIdentificationNo ?? '';
      final zipCode = authenticated?.zipCode ?? '966';
      final email = authenticated?.emailAddress ?? '';

      // Get activity ID based on selected activity level
      String activityID = _getActivityID();

      // Create request model with user detail fields only
      final requestModel = UpdateUserDetailRequestModel(
        firstName: nameController.text.trim(),
        lastName: nameController.text.trim(),
        firstNameN: nameController.text.trim(),
        middleName: '',
        middleNameN: '',
        lastNameN: nameController.text.trim(),
        gender: _getApiCompatibleGender(),
        dOB: _calculateDOBFromAge(),
        height: double.tryParse(heightController.text.trim()),
        isHeightInCM: _selectedHeightUnit == 'cm',
        weight: double.tryParse(weightController.text.trim()),
        isWeightInKG: _selectedWeightUnit == 'kg',
        activityID: activityID,
        isNotificationOn: true,
        mobileNumber: mobile,
        identificationNo: identification,
        email: email,
        zipCode: zipCode,
      );

      // Call the API
      final result = await waterMonitorRepo.updateOrInsertUserDetailForWaterMonitoring(
        requestModel: requestModel,
        isUpdate: isUpdate,
      );

      return result.fold(
        (failure) {
          errorHandlerService.handleError(failure: failure);
          _validationError = failure.message;
          _isLoading = false;
          notifyListeners();
          return false;
        },
        (apiModel) async {
          // Update local data with response
          _userDetailData = apiModel.data;
          // Fetch daily progress to get the updated goal and consumed data from API
          await fetchUserProgressForMonitoring();

          _isLoading = false;
          notifyListeners();
          return true;
        },
      );
    } catch (e) {
      _validationError = e.toString();
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }

  // Helper method to get activity ID based on activity level
  String _getActivityID() {
    switch (_selectedActivityLevel) {
      case "Almost Inactive (no exercise)":
        return "1";
      case "Lightly active":
        return "2";
      case "Lightly active (1-3) days per week":
        return "3";
      case "Super active (very hard exercise)":
        return "4";
      default:
        return "2";
    }
  }

  // Helper method to calculate DOB from age
  String _calculateDOBFromAge() {
    final age = int.tryParse(ageController.text.trim()) ?? 0;
    if (age > 0) {
      final currentYear = DateTime.now().year;
      final birthYear = currentYear - age;
      // Create a DateTime for January 1st of the birth year
      final birthDate = DateTime(birthYear, 1, 1);
      // Convert to API format: /Date(milliseconds+0300)/ using DateUtil
      return DateUtil.convertDateToString(birthDate);
    }
    return "";
  }

  @override
  void dispose() {
    nameController.dispose();
    heightController.dispose();
    weightController.dispose();
    ageController.dispose();
    super.dispose();
  }

  List<WaterCupModel> _cups = [];
  String? _selectedCupId;
  int _totalConsumedMl = 0; // Loaded from API
  int _dailyGoalMl = 0; // Loaded from API

  // Calibration: portion of the bottle SVG height that is fillable (0.0 - 1.0)
  double _fillableHeightPercent = 0.7;

  List<WaterCupModel> get cups => _cups;

  WaterCupModel? get selectedCup {
    if (_cups.isEmpty) return null;
    return _cups.firstWhere((c) => c.id == _selectedCupId, orElse: () => _cups.first);
  }

  int get totalConsumedMl => _totalConsumedMl;

  int get dailyGoalMl => _dailyGoalMl;

  // Portion of bottle drawable height that is fillable.
  double get fillableHeightPercent => _fillableHeightPercent;

  void setFillableHeightPercent(double v) {
    _fillableHeightPercent = v.clamp(0.0, 1.0);
    notifyListeners();
  }

  // Normalized progress in 0.0 - 1.0 (ensure double)
  double get progress {
    if (_dailyGoalMl == 0) return 0.0;
    final p = _totalConsumedMl / _dailyGoalMl;
    return p.clamp(0.0, 1.0).toDouble();
  }

  // Convenience percent (0 - 100)
  double get progressPercent => (progress * 100.0).clamp(0.0, 100.0).toDouble();

  // Calculate hydration status based on progress
  String get hydrationStatus {
    final percent = progressPercent;

    if (percent >= 90) {
      return "Well Hydrated";
    } else if (percent >= 70) {
      return "Hydrated";
    } else if (percent >= 50) {
      return "Moderately Hydrated";
    } else if (percent >= 30) {
      return "Slightly Dehydrated";
    } else {
      return "Dehydrated";
    }
  }

  // Get hydration status color
  Color get hydrationStatusColor {
    final percent = progressPercent;

    if (percent >= 90) {
      return AppColors.successColor; // Dark Green
    } else if (percent >= 70) {
      return AppColors.successColor; // Green
    } else if (percent >= 50) {
      return AppColors.warningColorYellow; //orange
    } else if (percent >= 30) {
      return AppColors.warningColorYellow; // Orange
    } else {
      return AppColors.errorColor; // Red
    }
  }

  String get nextDrinkTime {
    if (progressPercent >= 100) {
      return "Goal Achieved!";
    }

    // Get number of reminders from selected string (e.g., "3 Time" -> 3)
    final remindersPerDay = int.tryParse(_selectedNumberOfReminders.replaceAll(' Time', '').trim()) ?? 3;

    // Define waking hours (e.g., 6 AM to 10 PM = 16 hours)
    const wakingHoursStart = 6; // 6 AM
    const wakingHoursEnd = 22; // 10 PM
    const totalWakingHours = wakingHoursEnd - wakingHoursStart;

    // Calculate interval between drinks in hours
    final intervalHours = totalWakingHours / remindersPerDay;

    // Get current time
    final now = DateTime.now();
    final currentHour = now.hour + (now.minute / 60.0);

    // If before waking hours, next drink is at start time
    if (currentHour < wakingHoursStart) {
      return "${wakingHoursStart.toString().padLeft(2, '0')}:00 AM";
    }

    if (currentHour >= wakingHoursEnd) {
      return "Tomorrow ${wakingHoursStart.toString().padLeft(2, '0')}:00 AM";
    }

    // Calculate which interval we're in
    final hoursSinceWakeup = currentHour - wakingHoursStart;
    final currentInterval = (hoursSinceWakeup / intervalHours).floor();

    // Calculate next drink time
    final nextDrinkHour = wakingHoursStart + ((currentInterval + 1) * intervalHours);

    // If next drink time is past bedtime, show tomorrow
    if (nextDrinkHour >= wakingHoursEnd) {
      return "Tomorrow ${wakingHoursStart.toString().padLeft(2, '0')}:00 AM";
    }

    // Format the time
    final hour = nextDrinkHour.floor();
    final minute = ((nextDrinkHour - hour) * 60).round();

    // Convert to 12-hour format
    final hour12 = hour > 12 ? hour - 12 : (hour == 0 ? 12 : hour);
    final period = hour >= 12 ? 'PM' : 'AM';

    return "${hour12.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')} $period";
  }

  /// Get time until next drink in minutes
  int get minutesUntilNextDrink {
    if (progressPercent >= 100) return 0;

    final remindersPerDay = int.tryParse(_selectedNumberOfReminders.replaceAll(' Time', '').trim()) ?? 3;
    const wakingHoursStart = 6;
    const wakingHoursEnd = 22;
    const totalWakingHours = wakingHoursEnd - wakingHoursStart;
    final intervalHours = totalWakingHours / remindersPerDay;

    final now = DateTime.now();
    final currentHour = now.hour + (now.minute / 60.0);

    if (currentHour < wakingHoursStart) {
      final minutesUntil = ((wakingHoursStart - currentHour) * 60).round();
      return minutesUntil;
    }

    if (currentHour >= wakingHoursEnd) {
      final hoursUntilTomorrow = 24 - currentHour + wakingHoursStart;
      return (hoursUntilTomorrow * 60).round();
    }

    final hoursSinceWakeup = currentHour - wakingHoursStart;
    final currentInterval = (hoursSinceWakeup / intervalHours).floor();
    final nextDrinkHour = wakingHoursStart + ((currentInterval + 1) * intervalHours);

    if (nextDrinkHour >= wakingHoursEnd) {
      final hoursUntilTomorrow = 24 - currentHour + wakingHoursStart;
      return (hoursUntilTomorrow * 60).round();
    }

    final minutesUntil = ((nextDrinkHour - currentHour) * 60).round();
    return minutesUntil.clamp(0, 1440);
  }

  // Allow updating consumed and goal through the vm so UI doesn't manipulate internal fields directly
  void setTotalConsumedMl(int ml) {
    _totalConsumedMl = ml;
    notifyListeners();
  }

  void addConsumedMl(int ml) {
    _totalConsumedMl = (_totalConsumedMl + ml).clamp(0, 1000000);
    notifyListeners();
  }

  void subtractConsumedMl(int ml) {
    _totalConsumedMl = (_totalConsumedMl - ml).clamp(0, 1000000);
    notifyListeners();
  }

  void setDailyGoal(int ml) {
    _dailyGoalMl = ml;
    notifyListeners();
  }

  void _initializeDefaultCups() {
    _cups = [
      WaterCupModel(
        id: 'default_125',
        name: '125ml',
        capacityMl: 125,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
      WaterCupModel(
        id: 'default_150',
        name: '150ml',
        capacityMl: 150,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
      WaterCupModel(
        id: 'default_175',
        name: '175ml',
        capacityMl: 175,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
      WaterCupModel(
        id: 'default_200',
        name: '200ml',
        capacityMl: 200,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
      WaterCupModel(
        id: 'default_250',
        name: '250ml',
        capacityMl: 250,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
      WaterCupModel(
        id: 'default_300',
        name: '300ml',
        capacityMl: 300,
        iconPath: AppAssets.cupEmpty,
        isDefault: true,
      ),
    ];
    _selectedCupId = _cups.first.id;
    notifyListeners();
  }

  void selectCup(String cupId) {
    _selectedCupId = cupId;
    notifyListeners();
  }

  void addCup(WaterCupModel cup) {
    _cups.add(cup);
    notifyListeners();
  }

  void updateCup(WaterCupModel updatedCup) {
    final index = _cups.indexWhere((c) => c.id == updatedCup.id);
    if (index != -1) {
      _cups[index] = updatedCup;
      notifyListeners();
    }
  }

  // Public alias for deleting/removing a cup. Keeps API intention clear in UI code.
  void removeCup(String cupId) {
    deleteCup(cupId);
  }

  void deleteCup(String cupId) {
    final cup = _cups.firstWhere((c) => c.id == cupId);
    if (cup.isDefault) return; // can't delete default cups

    _cups.removeWhere((c) => c.id == cupId);
    if (_selectedCupId == cupId) {
      _selectedCupId = _cups.first.id;
    }
    notifyListeners();
  }

  // Returns the currently selected cup capacity in ml (0 if none)
  int get selectedCupCapacityMl => selectedCup?.capacityMl ?? 0;

  /// Increment the consumed amount by the currently selected cup capacity.
  /// This centralizes business logic here so UI just calls this method.
  void incrementBySelectedCup() {
    if (selectedCup != null) {
      addConsumedMl(selectedCup!.capacityMl);
    }
  }

  /// Decrement the consumed amount by the currently selected cup capacity.
  /// Ensures value is clamped inside the VM (subtractConsumedMl already clamps).
  void decrementBySelectedCup() {
    if (selectedCup != null) {
      subtractConsumedMl(selectedCup!.capacityMl);
    }
  }

  /// Insert user activity (record water intake)
  Future<bool> insertUserActivity({required int quantityIntake}) async {
    try {
      _isLoading = true;
      notifyListeners();

      // Get authenticated user info
      final authenticated = _appState.getAuthenticatedUser();
      if (authenticated == null) {
        _isLoading = false;
        notifyListeners();
        return false;
      }

      final mobile = (authenticated.mobileNumber ?? '').replaceAll('+', '').replaceFirst("0", "");
      final identification = authenticated.patientIdentificationNo ?? '';

      // Create request model
      final requestModel = InsertUserActivityRequestModel(
        identificationNo: identification,
        mobileNumber: mobile,
        quantityIntake: quantityIntake,
      );

      // Call the API
      final result = await waterMonitorRepo.insertUserActivity(requestModel: requestModel);

      return result.fold(
        (failure) {
          errorHandlerService.handleError(failure: failure);
          log('Error inserting user activity: ${failure.message}');
          _isLoading = false;
          notifyListeners();
          return false;
        },
        (apiModel) async {
          log("Insert user activity success: ${apiModel.data.toString()}");
          if (apiModel.data != null && apiModel.data is List && (apiModel.data as List).isNotEmpty) {
            final progressData = (apiModel.data as List).first;
            if (progressData is Map) {
              // Update consumed amount
              if (progressData.containsKey('QuantityConsumed')) {
                final consumed = progressData['QuantityConsumed'];
                if (consumed != null) {
                  _totalConsumedMl = (consumed is num) ? consumed.toInt() : int.tryParse(consumed.toString()) ?? _totalConsumedMl;
                  log('Updated consumed after insert: $_totalConsumedMl ml');
                }
              }
              // Update daily goal
              if (progressData.containsKey('QuantityLimit')) {
                final limit = progressData['QuantityLimit'];
                if (limit != null) {
                  _dailyGoalMl = (limit is num) ? limit.toInt() : int.tryParse(limit.toString()) ?? _dailyGoalMl;
                  log('Updated daily goal after insert: $_dailyGoalMl ml');
                }
              }
            }

            // Refresh progress data to ensure consistency
            await fetchUserProgressForMonitoring();
          }

          _isLoading = false;
          notifyListeners();
          return true;
        },
      );
    } catch (e) {
      log('Exception in insertUserActivity: $e');
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }

  /// Undo last user activity
  Future<bool> undoUserActivity() async {
    try {
      _isLoading = true;
      notifyListeners();

      // Get authenticated user info
      final authenticated = _appState.getAuthenticatedUser();
      if (authenticated == null) {
        _isLoading = false;
        notifyListeners();
        return false;
      }

      final mobile = (authenticated.mobileNumber ?? '').replaceAll('+', '').replaceFirst("0", "");
      final identification = authenticated.patientIdentificationNo ?? '';

      // Create request model
      final requestModel = UndoUserActivityRequestModel(
        progress: _getProgressIdFromDuration(),
        mobileNumber: mobile,
        identificationNo: identification,
      );

      // Call the API
      final result = await waterMonitorRepo.undoUserActivity(requestModel: requestModel);

      return result.fold(
        (failure) {
          errorHandlerService.handleError(failure: failure);
          log('Error undoing user activity: ${failure.message}');
          _isLoading = false;
          notifyListeners();
          return false;
        },
        (apiModel) async {
          log("Undo user activity success: ${apiModel.data.toString()}");

          // Update consumed amount and goal from the response
          if (apiModel.data != null && apiModel.data is List && (apiModel.data as List).isNotEmpty) {
            final progressData = (apiModel.data as List).first;
            if (progressData is Map) {
              // Update consumed amount
              if (progressData.containsKey('QuantityConsumed')) {
                final consumed = progressData['QuantityConsumed'];
                if (consumed != null) {
                  _totalConsumedMl = (consumed is num) ? consumed.toInt() : int.tryParse(consumed.toString()) ?? _totalConsumedMl;
                  log('Updated consumed after undo: $_totalConsumedMl ml');
                }
              }
              // Update daily goal
              if (progressData.containsKey('QuantityLimit')) {
                final limit = progressData['QuantityLimit'];
                if (limit != null) {
                  _dailyGoalMl = (limit is num) ? limit.toInt() : int.tryParse(limit.toString()) ?? _dailyGoalMl;
                  log('Updated daily goal after undo: $_dailyGoalMl ml');
                }
              }
            }
            await fetchUserProgressForMonitoring();
          }
          _isLoading = false;
          notifyListeners();
          return true;
        },
      );
    } catch (e) {
      log('Exception in undoUserActivity: $e');
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }

  /// Schedule water reminders based on user's reminder settings
  Future<bool> scheduleWaterReminders() async {
    try {
      final notificationService = getIt.get<NotificationService>();

      // Request permission first
      final hasPermission = await notificationService.requestPermissions();
      if (!hasPermission) {
        log('Notification permission denied');
        return false;
      }

      // Calculate reminder times based on _selectedNumberOfReminders
      final reminderTimes = _calculateReminderTimes();

      if (reminderTimes.isEmpty) {
        log('No reminder times calculated');
        return false;
      }

      // Schedule water reminders
      await notificationService.scheduleWaterReminders(
        reminderTimes: reminderTimes,
        title: LocaleKeys.timeToDrinkWater.tr(),
        body: LocaleKeys.stayHydratedDrinkWater.tr(namedArgs: {'amount': selectedCupCapacityMl.toString()}),
      );

      // Save reminder enabled state to cache
      _isWaterReminderEnabled = true;
      await _cacheService.saveBool(key: CacheConst.waterReminderEnabled, value: true);

      log('Scheduled ${reminderTimes.length} water reminders successfully');
      notifyListeners();
      return true;
    } catch (e) {
      log('Exception in scheduleWaterReminders: $e');
      return false;
    }
  }

  /// Calculate reminder times based on selected number of reminders
  List<DateTime> _calculateReminderTimes() {
    try {
      final remindersPerDay = int.tryParse(_selectedNumberOfReminders.replaceAll(' Time', '').trim()) ?? 3;

      const wakingHoursStart = 6; // 6 AM
      const wakingHoursEnd = 22; // 10 PM
      const totalWakingHours = wakingHoursEnd - wakingHoursStart;

      final intervalHours = totalWakingHours / remindersPerDay;

      List<DateTime> times = [];
      final now = DateTime.now();

      for (int i = 0; i < remindersPerDay; i++) {
        final hourDecimal = wakingHoursStart + (i * intervalHours);
        final hour = hourDecimal.floor();
        final minute = ((hourDecimal - hour) * 60).round();

        final reminderTime = DateTime(
          now.year,
          now.month,
          now.day,
          hour,
          minute,
        );

        times.add(reminderTime);
      }

      return times;
    } catch (e) {
      log('Error calculating reminder times: $e');
      return [];
    }
  }

  /// Cancel all water reminders
  Future<bool> cancelWaterReminders() async {
    try {
      final notificationService = GetIt.instance<NotificationService>();

      // Get pending notifications and cancel water reminders (IDs 5000-5999)
      final pendingNotifications = await notificationService.getPendingNotifications();
      for (final notification in pendingNotifications) {
        if (notification.id >= 5000 && notification.id < 6000) {
          await notificationService.cancelNotification(notification.id);
        }
      }

      // Save reminder disabled state to cache
      _isWaterReminderEnabled = false;
      await _cacheService.saveBool(key: CacheConst.waterReminderEnabled, value: false);

      log('Cancelled all water reminders');
      notifyListeners();
      return true;
    } catch (e) {
      log('Exception in cancelWaterReminders: $e');
      return false;
    }
  }

  /// Get list of scheduled water reminder times
  Future<List<DateTime>> getScheduledReminderTimes() async {
    try {
      final notificationService = GetIt.instance<NotificationService>();
      final pendingNotifications = await notificationService.getPendingNotifications();

      List<DateTime> times = [];
      for (final notification in pendingNotifications) {
        if (notification.id >= 5000 && notification.id < 6000) {
          // Note: PendingNotificationRequest doesn't contain scheduled time
          // We can only return the calculated times based on current settings
          times = _calculateReminderTimes();
          break;
        }
      }

      return times;
    } catch (e) {
      log('Exception in getScheduledReminderTimes: $e');
      return [];
    }
  }

  /// Schedule a test notification after 5 seconds
  /// Useful for testing notification functionality
  Future<bool> scheduleTestNotification() async {
    try {
      final notificationService = GetIt.instance<NotificationService>();

      // Request permission first
      final hasPermission = await notificationService.requestPermissions();
      if (!hasPermission) {
        log('Notification permission denied for test notification');
        return false;
      }

      // Schedule notification 5 seconds from now
      final scheduledTime = DateTime.now().add(const Duration(seconds: 5));

      await notificationService.scheduleNotification(
        id: 9999,
        // Use a unique ID for test notifications
        title: LocaleKeys.timeToDrinkWater.tr(),
        body: LocaleKeys.stayHydratedDrinkWater.tr(namedArgs: {'amount': selectedCupCapacityMl.toString()}),
        scheduledDate: scheduledTime,
        payload: 'test_notification',
      );

      log('Test notification scheduled for 5 seconds from now');
      return true;
    } catch (e) {
      log('Exception in scheduleTestNotification: $e');
      return false;
    }
  }
}
