package com.cloudsolutions.HMGPatientApp.watch.samsung_watch



import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.cloudsolutions.HMGPatientApp.MainActivity
import com.cloudsolutions.HMGPatientApp.watch.samsung_watch.model.Vitals
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import com.samsung.android.sdk.health.data.HealthDataService
import com.samsung.android.sdk.health.data.HealthDataStore
import com.samsung.android.sdk.health.data.data.AggregatedData
import com.samsung.android.sdk.health.data.data.HealthDataPoint
import com.samsung.android.sdk.health.data.permission.AccessType
import com.samsung.android.sdk.health.data.permission.Permission
import com.samsung.android.sdk.health.data.request.DataType
import com.samsung.android.sdk.health.data.request.DataTypes
import com.samsung.android.sdk.health.data.request.LocalTimeFilter
import com.samsung.android.sdk.health.data.request.LocalTimeGroup
import com.samsung.android.sdk.health.data.request.LocalTimeGroupUnit
import com.samsung.android.sdk.health.data.request.Ordering
import com.samsung.android.sdk.health.data.response.DataResponse
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.time.LocalTime

class SamsungWatch(
    private var flutterEngine: FlutterEngine,
    private var mainActivity: MainActivity
) {

    private lateinit var channel: MethodChannel
    private lateinit var dataStore: HealthDataStore
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private  val TAG = "SamsungWatch"


    private lateinit var vitals: MutableMap<String, List<Vitals>>
    companion object {
        private const val CHANNEL = "samsung_watch"

    }
    init{
        create()
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun create() {
        Log.d(TAG, "create: is called")
//        openTok = OpenTok(mainActivity, flutterEngine)
        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
            when (call.method) {
                "init" -> {
                    Log.d(TAG, "onMethodCall:  init called")
                    dataStore = HealthDataService.getStore(mainActivity)
                    vitals = mutableMapOf()
                    result.success("initialized")
                }

                "getPermission"->{
                    if(!this::dataStore.isInitialized)
                        result.error("DataStoreNotInitialized", "Please call init before requesting permissions", null)
                    val permSet = setOf(
                        Permission.of(DataTypes.HEART_RATE, AccessType.READ),
                        Permission.of(DataTypes.STEPS, AccessType.READ),
                        Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ),
                        Permission.of(DataTypes.ACTIVITY_SUMMARY, AccessType.READ),
                        Permission.of(DataTypes.SLEEP, AccessType.READ),
                        Permission.of(DataTypes.BODY_TEMPERATURE, AccessType.READ),
                        Permission.of(DataTypes.EXERCISE, AccessType.READ),
//                        Permission.of(DataTypes.SKIN_TEMPERATURE, AccessType.READ),
//                        Permission.of(DataTypes.NUTRITION, AccessType.READ),

                    )
                    scope.launch {
                        try {
                            var granted = dataStore.getGrantedPermissions(permSet)

                            if (granted.containsAll(permSet)) {
                                result.success("Permission Granted")
                                return@launch
                            }

                            granted = dataStore.requestPermissions(permSet, mainActivity)

                            if (granted.containsAll(permSet)) {
                                result.success("Permission Granted") // adapt result as needed
                                return@launch
                            }
                            result.error("PermissionError", "Permission Not Granted", null) // adapt result as needed
                        } catch (e: Exception) {
                            Log.e(TAG, "create: getPermission failed", e)
                            result.error("PermissionError", e.message, null)
                        }
                    }
                }

                "getHeartRate"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val readRequest = DataTypes.HEART_RATE.readDataRequestBuilder
                        .setLocalTimeFilter(localTimeFilter)
                        .setOrdering(Ordering.DESC)
                        .build()

                    scope.launch {
                        val heartRateList = dataStore.readData(readRequest).dataList
                        processHeartVital(heartRateList)
                        Log.d("TAG"," the data is ${vitals}")
                        print("the data is ${vitals}")
                        result.success("Data is obtained")
                    }
                }


                "getSleepData" -> {
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val readRequest = DataTypes.SLEEP.readDataRequestBuilder
                        .setLocalTimeFilter(localTimeFilter)
                        .setOrdering(Ordering.ASC)
                        .build()
                    scope.launch {
                        val sleepData = dataStore.readData(readRequest).dataList
                        processSleepVital(sleepData)
                        print("the data is $vitals")
                        Log.d(TAG, "the data is $vitals")
                        result.success("Data is obtained")
                    }

                }

                "steps"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
                    val aggregateRequest = DataType.StepsType.TOTAL.requestBuilder
                        .setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
                        .setOrdering(Ordering.ASC)
                        .build()

                    scope.launch {
                        val steps = dataStore.aggregateData(aggregateRequest)
                        processStepsCount(steps)
                        print("the data is $vitals")
                        Log.d(TAG, "the data is $vitals")
                        result.success("Data is obtained")
                    }
                }

                "activitySummary"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
                    val readRequest = DataType.ActivitySummaryType.TOTAL_ACTIVE_CALORIES_BURNED
                        .requestBuilder
                        .setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
                        .setOrdering(Ordering.DESC)
                        .build()

                    scope.launch {
                        val activityResult = dataStore.aggregateData(readRequest).dataList
                        processActivity(activityResult)
                        Log.d("TAG"," the data is ${vitals}")
                        print("the data is ${vitals}")
                        result.success("Data is obtained")
                    }

//                    val readRequest = DataTypes.EXERCISE.readDataRequestBuilder
//                        .setLocalTimeFilter(localTimeFilter)
//                        .build()
//
//                        scope.launch{
//                            try {
//                                val readResult = dataStore.readData(readRequest)
//                                val dataPoints = readResult.dataList
//
//                               processActivity(dataPoints)
//
//
//                            } catch (e: Exception) {
//                                e.printStackTrace()
//                            }
//                            result.success("Data is obtained")
//                        }
                }

                "bloodOxygen"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val readRequest = DataTypes.BLOOD_OXYGEN.readDataRequestBuilder
                        .setLocalTimeFilter(localTimeFilter)
                        .setOrdering(Ordering.DESC)
                        .build()

                    scope.launch {
                        val bloodOxygenList = dataStore.readData(readRequest).dataList
                        processBloodOxygen(bloodOxygenList)
                        Log.d("TAG"," the data is ${vitals}")
                        print("the data is ${vitals["bloodOxygen"]}")
                        result.success("Data is obtained")
                    }
                }


            "bodyTemperature"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val readRequest = DataTypes.BODY_TEMPERATURE.readDataRequestBuilder
                        .setLocalTimeFilter(localTimeFilter)
                        .setOrdering(Ordering.DESC)
                        .build()

                    scope.launch {
                        val bodyTemperatureList = dataStore.readData(readRequest).dataList
                        processBodyTemperature(bodyTemperatureList)
                        Log.d("TAG"," the data is ${vitals}")
                        print("the data is ${vitals["bodyTemperature"]}")
                        result.success("Data is obtained")
                    }
                }

                "distance"->{
                    val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
                    val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
                    val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
                    val readRequest = DataType.ActivitySummaryType.TOTAL_DISTANCE.requestBuilder
                        .setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
                        .setOrdering(Ordering.DESC)
                        .build()

                    scope.launch {
                        val activityResult = dataStore.aggregateData(readRequest).dataList
                        processDistance(activityResult)
                        Log.d("TAG"," the data is ${vitals}")
                        print("the data is ${vitals}")
                        result.success("Data is obtained")
                    }
                }

                "retrieveData"->{
                    if(vitals.isEmpty()){
                        result.error("NoDataFound", "No Data was obtained", null)
                        return@setMethodCallHandler
                    }
                    result.success("""
                        {
                            "heartRate": ${vitals["heartRate"]},
                            "steps": ${vitals["steps"]},
                            "sleep": ${vitals["sleep"]},
                            "activity": ${vitals["activity"]},
                            "bloodOxygen": ${vitals["bloodOxygen"]},
                            "bodyTemperature": ${vitals["bodyTemperature"]},
                            "distance": ${vitals["distance"]}
                        }
                    """.trimIndent())
                }


                "closeCoroutineScope"->{
                    destroy()
                    result.success("Coroutine Scope Cancelled")
                }

                else -> {
                    result.notImplemented()
                }
            }
        }
    }

    private fun CoroutineScope.processDistance(activityResult: List<AggregatedData<Float>>) {
        vitals["distance"] = mutableListOf()
        activityResult.forEach { stepData ->
            val vitalData = Vitals().apply {

                value =  stepData.value.toString()
                timeStamp =  stepData.startTime.toString()
            }
            (vitals["distance"] as MutableList).add(vitalData)
        }
    }

    private fun CoroutineScope.processBodyTemperature( bodyTemperatureList :List<HealthDataPoint>) {
        vitals["bodyTemperature"] = mutableListOf()
        bodyTemperatureList.forEach { stepData ->
            val vitalData = Vitals().apply {
                value =  stepData.getValue(DataType.BodyTemperatureType.BODY_TEMPERATURE).toString()
                timeStamp =  stepData.endTime.toString()
            }
            (vitals["bodyTemperature"] as MutableList).add(vitalData)
        }
    }

    private fun CoroutineScope.processBloodOxygen( bloodOxygenList :List<HealthDataPoint>) {
        vitals["bloodOxygen"] = mutableListOf()
        bloodOxygenList.forEach { stepData ->
            val vitalData = Vitals().apply {
                value =  stepData.getValue(DataType.BloodOxygenType.OXYGEN_SATURATION).toString()
                timeStamp =  stepData.endTime.toString()
            }
            (vitals["bloodOxygen"] as MutableList).add(vitalData)
        }
    }


//    private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {
//
//        vitals["activity"] = mutableListOf()
//        activityResult.forEach { stepData ->
//            val vitalData = Vitals().apply {
//
//                value =  stepData.value.toString()
//                timeStamp =  stepData.startTime.toString()
//            }
//            (vitals["activity"] as MutableList).add(vitalData)
//        }
//    }
    private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {

        vitals["activity"] = mutableListOf()
        activityResult.forEach { stepData ->
            val vitalData = Vitals().apply {

                value =  stepData.value.toString()
                timeStamp =  stepData.startTime.toString()
            }
            (vitals["activity"] as MutableList).add(vitalData)
        }

//    dataPoints.forEach { dataPoint ->
//        val sessions = dataPoint.getValue(DataType.ExerciseType.SESSIONS)
//
//        sessions?.forEach { session ->
//
//            val exerciseSessionCalories = session.calories
//            val vitalData = Vitals().apply {
//                value =  exerciseSessionCalories.toString()
//                timeStamp =  session.startTime.toString()
//            }
//            (vitals["activity"] as MutableList).add(vitalData)
//        }
//    }
    }

    private fun CoroutineScope.processStepsCount(result: DataResponse<AggregatedData<Long>>) {
        val stepCount = ArrayList<AggregatedData<Long>>()
        var totalSteps: Long = 0
        vitals["steps"] = mutableListOf()
        result.dataList.forEach { stepData ->
            val vitalData = Vitals().apply {
                value = (stepData.value as Long).toString()
                timeStamp = stepData.startTime.toString()
            }
            (vitals["steps"] as MutableList).add(vitalData)
        }

    }

    private fun CoroutineScope.processSleepVital(sleepData: List<HealthDataPoint>) {
        vitals["sleep"] = mutableListOf()
        sleepData.forEach {
            (vitals["sleep"] as MutableList).add(
                Vitals().apply {
                    timeStamp = it.startTime.toString()
                    value = (it.getValue(DataType.SleepType.DURATION)?.toMillis().toString())
                }
            )
        }
    }

    private suspend fun CoroutineScope.processHeartVital(
        heartRateList: List<HealthDataPoint>,
    ) {
        vitals["heartRate"] = mutableListOf()
        heartRateList.forEach {
            (vitals["heartRate"] as MutableList).add(processHeartRateData(it))
        }
    }

    private fun processHeartRateData(heartRateData: HealthDataPoint) =
        Vitals().apply {
            heartRateData.getValue(DataType.HeartRateType.MAX_HEART_RATE)?.let {
                value = it.toString()
            }
            timeStamp = heartRateData.startTime.toString()
        }


    fun destroy() {
        scope.cancel()
    }

}
