import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/vitals_data_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
import 'package:permission_handler/permission_handler.dart';

import 'health_utils.dart';
import 'package:async/async.dart';

class HealthService {
  static final HealthService _instance = HealthService._internal();

  factory HealthService() => _instance;

  HealthService._internal();

  final Health health = Health();

  WatchHelper? watchHelper;

  final List<HealthDataType> _healthMetrics = [
    HealthDataType.HEART_RATE,
    // HealthDataType.STEPS,
    // HealthDataType.BLOOD_OXYGEN,
    // HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
    // HealthDataType.BLOOD_PRESSURE_DIASTOLIC,
    // HealthDataType.BODY_TEMPERATURE,
    // HealthDataType.DISTANCE_WALKING_RUNNING,
    // HealthDataType.ACTIVE_ENERGY_BURNED,
  ];

  final List<HealthDataType> _healthMetricsAndroid = [
    HealthDataType.HEART_RATE,
    HealthDataType.STEPS,
    HealthDataType.BLOOD_OXYGEN,
    // HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
    // HealthDataType.BLOOD_PRESSURE_DIASTOLIC,
    // HealthDataType.BODY_TEMPERATURE,
    HealthDataType.DISTANCE_DELTA,
    HealthDataType.ACTIVE_ENERGY_BURNED,
    HealthDataType.BASAL_ENERGY_BURNED,
    HealthDataType.TOTAL_CALORIES_BURNED
  ];

  final List<HealthDataType> _healthPermissions = [
    HealthDataType.HEART_RATE,
    HealthDataType.STEPS,
    HealthDataType.BLOOD_OXYGEN,
    // HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
    // HealthDataType.BLOOD_PRESSURE_DIASTOLIC,
    // HealthDataType.BODY_TEMPERATURE,
    Platform.isAndroid ? HealthDataType.DISTANCE_DELTA : HealthDataType.DISTANCE_WALKING_RUNNING,
    HealthDataType.ACTIVE_ENERGY_BURNED,
    HealthDataType.BASAL_ENERGY_BURNED,
    HealthDataType.TOTAL_CALORIES_BURNED
  ];

  final List<HealthDataType> _healthMetricsCumulative = [
    HealthDataType.BLOOD_OXYGEN,
    HealthDataType.STEPS,
    Platform.isAndroid ? HealthDataType.DISTANCE_DELTA : HealthDataType.DISTANCE_WALKING_RUNNING,
    HealthDataType.ACTIVE_ENERGY_BURNED,
    HealthDataType.BASAL_ENERGY_BURNED,
    HealthDataType.TOTAL_CALORIES_BURNED
  ];

  Future<bool> requestAuthorization() async {
    try {
      final types = (Platform.isAndroid) ? dataTypesAndroid : _healthPermissions;
      final granted = await health.requestAuthorization(types);
      await Permission.activityRecognition.request();
      await Permission.location.request();
      // request access to read historic data
      await Health().requestHealthDataHistoryAuthorization();
      // await authorize();
      return granted;
    } catch (e) {
      print('Authorization error: $e');
      return false;
    }
  }

  Future<Map<HealthDataType, List<HealthDataPoint>>> getAllHealthData(
    DateTime startTime,
    DateTime endTime,
  ) async {
    final Map<HealthDataType, List<HealthDataPoint>> allData = {};

    try {
      for (var type in _healthMetricsCumulative) {
        try {
          final data = await health.getHealthIntervalDataFromTypes(
            startDate: startTime,
            endDate: endTime,
            types: [type],
            interval: 86400,
          );

          if (type == HealthDataType.BLOOD_OXYGEN) {
            for (var point in data) {
              if (point.value is NumericHealthValue) {
                final numericValue = (point.value as NumericHealthValue).numericValue;
                point.value = NumericHealthValue(
                  numericValue: numericValue * 100,
                );
              }
            }
          }

          if (type == HealthDataType.DISTANCE_WALKING_RUNNING) {
            for (var point in data) {
              if (point.value is NumericHealthValue) {
                final numericValue = (point.value as NumericHealthValue).numericValue;
                point.value = NumericHealthValue(
                  numericValue: numericValue / 1000,
                );
              }
            }
          }

          allData[type] = data;
        } catch (e) {
          print('Error fetching $type: $e');
          allData[type] = [];
        }
      }

      for (var type in Platform.isIOS ? _healthMetrics : _healthMetricsAndroid) {
        try {
          final data = await health.getHealthDataFromTypes(
            startTime: startTime,
            endTime: endTime,
            types: [type],
            // includeManualEntry: false
            // interval: 86400,
          );

          allData[type] = data;
        } catch (e) {
          print('Error fetching $type: $e');
          allData[type] = [];
        }
      }
    } catch (e) {
      print('Error fetching health data: $e');
    }

    return allData;
  }

  Future<List<HealthDataPoint>> getSpecificHealthData(
    HealthDataType type,
    DateTime startTime,
    DateTime endTime,
  ) async {
    try {
      final data = await health.getHealthDataFromTypes(
        startTime: startTime,
        endTime: endTime,
        types: [type],
      );
      return data;
    } catch (e) {
      print('Error fetching $type: $e');
      return [];
    }
  }

  void addWatchHelper(SmartWatchTypes watchType) {
    watchHelper = CreateWatchHelper.getWatchName(watchType);
  }

  Future<Result<bool>> initDevice() async {
    if (watchHelper == null) {
      return Result.error('No watch helper found');
    }
    return await watchHelper!.initDevice();
  }

  Future<VitalsWRTType?> getVitals() async {
    if (watchHelper == null) {
      print('No watch helper found');
      return null;
    }
    try {
      await watchHelper!.getHeartRate();
      await watchHelper!.getSleep();
      await watchHelper!.getSteps();
      await watchHelper!.getActivity();
      await watchHelper!.getBodyTemperature();
      await watchHelper!.getDistance();
      await watchHelper!.getBloodOxygen();
      Result<dynamic> data = await watchHelper!.retrieveData();
      if (data.isError) {
        print('Unable to get the data');
      }
      var response = jsonDecode(data.asValue?.value?.toString()?.trim().replaceAll("\n", "") ?? "");
      VitalsWRTType vitals = VitalsWRTType.fromMap(response);
      // log("the data is ${vitals}");
      return vitals;
    } catch (e) {
      print('Error getting heart rate: $e');
    }
    return null;
  }
}
