package com.atridad.ascently.data.health

import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import androidx.activity.result.contract.ActivityResultContract
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.PermissionController
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.TotalCaloriesBurnedRecord

import androidx.health.connect.client.units.Energy
import com.atridad.ascently.data.model.ClimbSession
import com.atridad.ascently.data.model.SessionStatus
import com.atridad.ascently.utils.AppLogger
import com.atridad.ascently.utils.DateFormatUtils
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flow
import java.time.Duration
import java.time.Instant
import java.time.ZoneOffset
import androidx.core.content.edit

/**
 * Health Connect manager for Ascently that syncs climbing sessions to Samsung Health, Google Fit,
 * and other health apps.
 */
@SuppressLint("RestrictedApi")
class HealthConnectManager(private val context: Context) {

    private val preferences: SharedPreferences =
        context.getSharedPreferences("health_connect_prefs", Context.MODE_PRIVATE)

    private val _isEnabled = MutableStateFlow(preferences.getBoolean("enabled", false))
    private val _hasPermissions = MutableStateFlow(preferences.getBoolean("permissions", false))
    private val _autoSync = MutableStateFlow(preferences.getBoolean("auto_sync", true))
    private val _isCompatible = MutableStateFlow(true)

    val isEnabled: Flow<Boolean> = _isEnabled.asStateFlow()
    val hasPermissions: Flow<Boolean> = _hasPermissions.asStateFlow()
    val isCompatible: Flow<Boolean> = _isCompatible.asStateFlow()

    companion object {
        private const val TAG = "HealthConnectManager"

        val REQUIRED_PERMISSIONS =
            setOf(
                HealthPermission.getReadPermission(ExerciseSessionRecord::class),
                HealthPermission.getWritePermission(ExerciseSessionRecord::class),
                HealthPermission.getReadPermission(HeartRateRecord::class),
                HealthPermission.getWritePermission(HeartRateRecord::class),
                HealthPermission.getReadPermission(TotalCaloriesBurnedRecord::class),
                HealthPermission.getWritePermission(TotalCaloriesBurnedRecord::class),
            )
    }

    private val healthConnectClient by lazy {
        try {
            HealthConnectClient.getOrCreate(context)
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Failed to create Health Connect client" }
            _isCompatible.value = false
            null
        }
    }

    fun isHealthConnectAvailable(): Flow<Boolean> = flow {
        try {
            if (!_isCompatible.value) {
                emit(false)
                return@flow
            }
            val status = HealthConnectClient.getSdkStatus(context)
            emit(status == HealthConnectClient.SDK_AVAILABLE)
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error checking Health Connect availability" }
            _isCompatible.value = false
            emit(false)
        }
    }

    suspend fun setEnabled(enabled: Boolean) {
        preferences.edit().putBoolean("enabled", enabled).apply()
        _isEnabled.value = enabled

        if (enabled && _isCompatible.value) {
            // Automatically request permissions when enabling
            try {
                val alreadyHasPermissions = hasAllPermissions()
                if (!alreadyHasPermissions) {
                    AppLogger.d(TAG) { "Health Connect enabled - permissions will be requested by UI" }
                }
            } catch (e: Exception) {
                AppLogger.w(TAG, e) { "Error checking permissions when enabling Health Connect" }
            }
        } else if (!enabled) {
            setPermissionsGranted(false)
        }
    }

    fun setPermissionsGranted(granted: Boolean) {
        preferences.edit().putBoolean("permissions", granted).apply()
        _hasPermissions.value = granted
    }

    suspend fun hasAllPermissions(): Boolean {
        return try {
            if (!_isCompatible.value || healthConnectClient == null) {
                return false
            }
            val grantedPermissions =
                healthConnectClient!!.permissionController.getGrantedPermissions()
            val hasAll =
                REQUIRED_PERMISSIONS.all { permission ->
                    grantedPermissions.contains(permission)
                }
            setPermissionsGranted(hasAll)
            hasAll
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error checking permissions" }
            setPermissionsGranted(false)
            false
        }
    }

    suspend fun isReady(): Boolean {
        return try {
            if (!_isEnabled.value || !_isCompatible.value || healthConnectClient == null) {
                return false
            }

            val isAvailable =
                HealthConnectClient.getSdkStatus(context) == HealthConnectClient.SDK_AVAILABLE
            val hasPerms = if (isAvailable) hasAllPermissions() else false
            isAvailable && hasPerms
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error checking Health Connect readiness" }
            false
        }
    }

    fun getPermissionRequestContract(): ActivityResultContract<Set<String>, Set<String>> {
        return PermissionController.createRequestPermissionResultContract()
    }

    fun getRequiredPermissions(): Set<String> {
        return try {
            REQUIRED_PERMISSIONS.map { it }.toSet()
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error getting required permissions" }
            emptySet()
        }
    }

    @SuppressLint("RestrictedApi")
    suspend fun syncCompletedSession(
        session: ClimbSession,
        gymName: String,
        attemptCount: Int = 0,
    ): Result<Unit> {
        return try {
            if (!isReady() || !_autoSync.value) {
                return Result.failure(
                    IllegalStateException("Health Connect not ready or auto-sync disabled"),
                )
            }

            if (session.status != SessionStatus.COMPLETED) {
                return Result.failure(
                    IllegalArgumentException("Only completed sessions can be synced"),
                )
            }

            val startTime = session.startTime?.let { DateFormatUtils.parseISO8601(it) }
            val endTime = session.endTime?.let { DateFormatUtils.parseISO8601(it) }

            if (startTime == null || endTime == null) {
                return Result.failure(
                    IllegalArgumentException("Session must have valid start and end times"),
                )
            }

            AppLogger.d(TAG) { "Attempting to sync session '${session.id}' to Health Connect..." }

            val records = mutableListOf<androidx.health.connect.client.records.Record>()

            try {
                val exerciseSession =
                    ExerciseSessionRecord(
                        startTime = startTime,
                        startZoneOffset =
                        ZoneOffset.systemDefault().rules.getOffset(startTime),
                        endTime = endTime,
                        endZoneOffset = ZoneOffset.systemDefault().rules.getOffset(endTime),
                        exerciseType =
                        ExerciseSessionRecord.EXERCISE_TYPE_STRENGTH_TRAINING,
                        title = "Rock Climbing at $gymName",
                        metadata = androidx.health.connect.client.records.metadata.Metadata.manualEntry(),
                    )
                records.add(exerciseSession)
            } catch (e: Exception) {
                AppLogger.w(TAG, e) { "Failed to create exercise session record" }
            }

            try {
                val durationMinutes = Duration.between(startTime, endTime).toMinutes()
                val estimatedCalories = estimateCaloriesForClimbing(durationMinutes, attemptCount)

                if (estimatedCalories > 0) {
                    val caloriesRecord =
                        TotalCaloriesBurnedRecord(
                            startTime = startTime,
                            startZoneOffset =
                            ZoneOffset.systemDefault().rules.getOffset(startTime),
                            endTime = endTime,
                            endZoneOffset =
                            ZoneOffset.systemDefault().rules.getOffset(endTime),
                            energy = Energy.calories(estimatedCalories),
                            metadata = androidx.health.connect.client.records.metadata.Metadata.manualEntry(),
                        )
                    records.add(caloriesRecord)
                }
            } catch (e: Exception) {
                AppLogger.w(TAG, e) { "Failed to create calories record" }
            }

            try {
                val heartRateRecord = createHeartRateRecord(startTime, endTime, attemptCount)
                heartRateRecord?.let { records.add(it) }
            } catch (e: Exception) {
                AppLogger.w(TAG, e) { "Failed to create heart rate record" }
            }

            if (records.isNotEmpty() && healthConnectClient != null) {
                AppLogger.d(TAG) { "Writing ${records.size} records to Health Connect..." }
                healthConnectClient!!.insertRecords(records)
                AppLogger.i(TAG) {
                    "Successfully synced ${records.size} records for session '${session.id}' to Health Connect"
                }

                preferences
                    .edit {
                        putString("last_sync_success", DateFormatUtils.nowISO8601())
                    }
            } else {
                val reason =
                    when {
                        records.isEmpty() -> "No records created"
                        healthConnectClient == null -> "Health Connect client unavailable"
                        else -> "Unknown reason"
                    }
                AppLogger.w(TAG) { "Sync failed for session '${session.id}': $reason" }
                return Result.failure(Exception("Sync failed: $reason"))
            }

            Result.success(Unit)
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error syncing climbing session to Health Connect" }
            Result.failure(e)
        }
    }

    suspend fun autoSyncCompletedSession(
        session: ClimbSession,
        gymName: String,
        attemptCount: Int = 0,
    ): Result<Unit> {
        return if (_autoSync.value && isReady() && session.status == SessionStatus.COMPLETED) {
            AppLogger.d(TAG) { "Auto-syncing completed session '${session.id}' to Health Connect..." }
            syncCompletedSession(session, gymName, attemptCount)
        } else {
            val reason =
                when {
                    session.status != SessionStatus.COMPLETED -> "session not completed"
                    !_autoSync.value -> "auto-sync disabled"
                    !isReady() -> "Health Connect not ready"
                    else -> "unknown reason"
                }
            AppLogger.d(TAG) { "Auto-sync skipped for session '${session.id}': $reason" }
            Result.success(Unit)
        }
    }

    private fun estimateCaloriesForClimbing(durationMinutes: Long, attemptCount: Int): Double {
        val baseCaloriesPerMinute = 8.0
        val intensityMultiplier =
            when {
                attemptCount >= 20 -> 1.3
                attemptCount >= 10 -> 1.1
                else -> 0.9
            }
        return durationMinutes * baseCaloriesPerMinute * intensityMultiplier
    }

    @SuppressLint("RestrictedApi")
    private fun createHeartRateRecord(
        startTime: Instant,
        endTime: Instant,
        attemptCount: Int,
    ): HeartRateRecord? {
        return try {
            val samples = mutableListOf<HeartRateRecord.Sample>()
            val intervalMinutes = 5L

            val baseHeartRate =
                when {
                    attemptCount >= 20 -> 155L
                    attemptCount >= 10 -> 145L
                    else -> 135L
                }

            var currentTime = startTime
            while (currentTime.isBefore(endTime)) {
                val variation = (-15..15).random()
                val heartRate = (baseHeartRate + variation).coerceIn(110L, 180L)

                samples.add(HeartRateRecord.Sample(time = currentTime, beatsPerMinute = heartRate))
                currentTime = currentTime.plusSeconds(intervalMinutes * 60)
            }

            if (samples.isEmpty()) return null

            HeartRateRecord(
                startTime = startTime,
                startZoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime),
                endTime = endTime,
                endZoneOffset = ZoneOffset.systemDefault().rules.getOffset(endTime),
                samples = samples,
                metadata = androidx.health.connect.client.records.metadata.Metadata.manualEntry(),
            )
        } catch (e: Exception) {
            AppLogger.e(TAG, e) { "Error creating heart rate record" }
            null
        }
    }

    fun isReadySync(): Boolean {
        return _isEnabled.value && _hasPermissions.value
    }
}
