package com.gametime.watch.bridge

import androidx.health.services.client.data.DataType
import androidx.health.services.client.data.ExerciseCapabilities
import androidx.health.services.client.data.ExerciseType
import androidx.health.services.client.data.ExerciseTypeCapabilities
import java.util.concurrent.ExecutionException
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class WatchExerciseMetricsRetryPolicyTest {
    @Test
    fun distanceSecurityFailureMatchesWrappedSecurityException() {
        val error = ExecutionException(
            SecurityException("Missing permissions: [android.permission.ACCESS_FINE_LOCATION]"),
        )

        assertTrue(
            WatchExerciseMetricsRetryPolicy.isDistanceSecurityFailure(
                setOf(DataType.DISTANCE, DataType.CALORIES, DataType.HEART_RATE_BPM),
                error,
            ),
        )
    }

    @Test
    fun nonDistanceSecurityFailureDoesNotScheduleDistanceRetry() {
        val error = ExecutionException(
            SecurityException("Missing permissions: [android.permission.ACCESS_FINE_LOCATION]"),
        )

        assertFalse(
            WatchExerciseMetricsRetryPolicy.isDistanceSecurityFailure(
                setOf(DataType.CALORIES, DataType.HEART_RATE_BPM),
                error,
            ),
        )
    }

    @Test
    fun distanceNonSecurityFailureDoesNotScheduleDistanceRetry() {
        val error = ExecutionException(IllegalStateException("Health Services busy"))

        assertFalse(
            WatchExerciseMetricsRetryPolicy.isDistanceSecurityFailure(
                setOf(DataType.DISTANCE, DataType.CALORIES, DataType.HEART_RATE_BPM),
                error,
            ),
        )
    }

    @Test
    fun distanceSecurityRetriesAreBounded() {
        assertFalse(WatchExerciseMetricsRetryPolicy.canRetryDistance(0))
        assertTrue(WatchExerciseMetricsRetryPolicy.canRetryDistance(1))
        assertTrue(WatchExerciseMetricsRetryPolicy.canRetryDistance(2))
        assertTrue(WatchExerciseMetricsRetryPolicy.canRetryDistance(3))
        assertFalse(WatchExerciseMetricsRetryPolicy.canRetryDistance(4))
    }

    @Test
    fun distanceConfigDoesNotRequireGpsOrCalories() {
        val configs = WatchExerciseMetricsConfigFactory.plansFromCapabilities(
            capabilities(
                ExerciseType.RUNNING to setOf(DataType.DISTANCE, DataType.HEART_RATE_BPM),
            ),
            requestedTypeNames = listOf("RUNNING"),
            canUseGps = false,
        )

        assertEquals(1, configs.size)
        assertEquals(ExerciseType.RUNNING, configs.single().exerciseType)
        assertFalse(configs.single().enableGps)
        assertEquals(
            setOf(DataType.DISTANCE, DataType.HEART_RATE_BPM),
            configs.single().dataTypes,
        )
    }

    @Test
    fun gpsDistanceConfigKeepsNonGpsFallback() {
        val configs = WatchExerciseMetricsConfigFactory.plansFromCapabilities(
            capabilities(
                ExerciseType.RUNNING to setOf(
                    DataType.DISTANCE,
                    DataType.CALORIES,
                    DataType.HEART_RATE_BPM,
                ),
            ),
            requestedTypeNames = listOf("RUNNING"),
            canUseGps = true,
        )

        assertEquals(2, configs.size)
        assertTrue(configs[0].enableGps)
        assertFalse(configs[1].enableGps)
        assertEquals(configs[0].dataTypes, configs[1].dataTypes)
        assertTrue(DataType.DISTANCE in configs[0].dataTypes)
        assertTrue(DataType.CALORIES in configs[0].dataTypes)
    }

    @Test
    fun runtimeSensorPermissionsDoNotRequireFineLocation() {
        val permissions = WatchSensorPermissionPolicy.requiredRuntimePermissions(sdkInt = 35)

        assertTrue(android.Manifest.permission.BODY_SENSORS in permissions)
        assertTrue(android.Manifest.permission.ACTIVITY_RECOGNITION in permissions)
        assertTrue(android.Manifest.permission.POST_NOTIFICATIONS in permissions)
        assertFalse(android.Manifest.permission.ACCESS_FINE_LOCATION in permissions)
    }

    private fun capabilities(
        vararg supportedTypes: Pair<
            ExerciseType,
            Set<androidx.health.services.client.data.DataType<*, *>>,
        >,
    ): ExerciseCapabilities {
        return ExerciseCapabilities(
            supportedTypes.associate { (exerciseType, dataTypes) ->
                exerciseType to ExerciseTypeCapabilities(
                    dataTypes,
                    emptyMap(),
                    emptyMap(),
                    false,
                )
            },
        )
    }
}
