//
//  HealthService.swift
//  Reflect
//
//  Manages Apple Health integration for mood correlation insights.
//

import Foundation
import HealthKit
import SwiftUI

@MainActor
class HealthService: ObservableObject {

    static let shared = HealthService()

    // MARK: - Published State

    @Published var isAuthorized: Bool = false
    @Published var isAvailable: Bool = false

    // MARK: - App Storage

    @AppStorage(UserDefaultsStore.Keys.healthKitEnabled.rawValue, store: GroupUserDefaults.groupDefaults)
    var isEnabled: Bool = false

    // MARK: - HealthKit Store

    private let healthStore = HKHealthStore()

    // MARK: - Data Types

    // Core activity metrics
    private let stepCountType = HKQuantityType(.stepCount)
    private let exerciseTimeType = HKQuantityType(.appleExerciseTime)
    private let activeEnergyType = HKQuantityType(.activeEnergyBurned)
    private let distanceType = HKQuantityType(.distanceWalkingRunning)

    // Heart & stress indicators
    private let heartRateType = HKQuantityType(.heartRate)
    private let restingHeartRateType = HKQuantityType(.restingHeartRate)
    private let hrvType = HKQuantityType(.heartRateVariabilitySDNN)

    // Sleep & recovery
    private let sleepAnalysisType = HKCategoryType(.sleepAnalysis)

    // Mindfulness
    private let mindfulSessionType = HKCategoryType(.mindfulSession)

    // State of Mind
    private let stateOfMindType = HKSampleType.stateOfMindType()

    // Workouts
    private let workoutType = HKWorkoutType.workoutType()

    private var readTypes: Set<HKObjectType> {
        [
            stepCountType, exerciseTimeType, activeEnergyType, distanceType,
            heartRateType, restingHeartRateType, hrvType,
            sleepAnalysisType, mindfulSessionType, stateOfMindType, workoutType
        ]
    }

    // MARK: - Initialization

    init() {
        isAvailable = HKHealthStore.isHealthDataAvailable()
    }

    // MARK: - Authorization

    func requestAuthorization() async -> Bool {
        guard isAvailable else {
            print("HealthService: HealthKit not available on this device")
            return false
        }

        do {
            try await healthStore.requestAuthorization(toShare: [], read: readTypes)
            isAuthorized = true
            isEnabled = true
            AnalyticsManager.shared.track(.healthKitAuthorized)
            return true
        } catch {
            print("HealthService: Authorization failed: \(error.localizedDescription)")
            AnalyticsManager.shared.track(.healthKitAuthFailed(error: error.localizedDescription))
            return false
        }
    }

    // MARK: - Fetch Health Data for Date

    struct DailyHealthData {
        let date: Date

        // Activity metrics
        let steps: Int?
        let exerciseMinutes: Int?
        let activeCalories: Int?
        let distanceKm: Double?

        // Heart metrics
        let averageHeartRate: Double?
        let restingHeartRate: Double?
        let hrv: Double?  // Heart Rate Variability in ms

        // Recovery metrics
        let sleepHours: Double?
        let mindfulMinutes: Int?

        var hasData: Bool {
            steps != nil || exerciseMinutes != nil || averageHeartRate != nil ||
            sleepHours != nil || hrv != nil || mindfulMinutes != nil
        }
    }

    func fetchHealthData(for date: Date) async -> DailyHealthData {
        let calendar = Calendar.current
        let startOfDay = calendar.startOfDay(for: date)
        guard let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay) else {
            return DailyHealthData(date: date, steps: nil, exerciseMinutes: nil, activeCalories: nil, distanceKm: nil, averageHeartRate: nil, restingHeartRate: nil, hrv: nil, sleepHours: nil, mindfulMinutes: nil)
        }

        // Fetch all metrics concurrently
        async let steps = fetchSteps(start: startOfDay, end: endOfDay)
        async let exercise = fetchExerciseMinutes(start: startOfDay, end: endOfDay)
        async let activeCalories = fetchActiveCalories(start: startOfDay, end: endOfDay)
        async let distance = fetchDistance(start: startOfDay, end: endOfDay)
        async let heartRate = fetchAverageHeartRate(start: startOfDay, end: endOfDay)
        async let restingHR = fetchRestingHeartRate(start: startOfDay, end: endOfDay)
        async let hrv = fetchHRV(start: startOfDay, end: endOfDay)
        async let sleep = fetchSleepHours(for: date)
        async let mindful = fetchMindfulMinutes(start: startOfDay, end: endOfDay)

        return await DailyHealthData(
            date: date,
            steps: steps,
            exerciseMinutes: exercise,
            activeCalories: activeCalories,
            distanceKm: distance,
            averageHeartRate: heartRate,
            restingHeartRate: restingHR,
            hrv: hrv,
            sleepHours: sleep,
            mindfulMinutes: mindful
        )
    }

    // MARK: - Steps

    private func fetchSteps(start: Date, end: Date) async -> Int? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: stepCountType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum
            ) { _, result, error in
                guard error == nil,
                      let sum = result?.sumQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let steps = Int(sum.doubleValue(for: .count()))
                continuation.resume(returning: steps)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Exercise Minutes

    private func fetchExerciseMinutes(start: Date, end: Date) async -> Int? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: exerciseTimeType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum
            ) { _, result, error in
                guard error == nil,
                      let sum = result?.sumQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let minutes = Int(sum.doubleValue(for: .minute()))
                continuation.resume(returning: minutes)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Heart Rate

    private func fetchAverageHeartRate(start: Date, end: Date) async -> Double? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: heartRateType,
                quantitySamplePredicate: predicate,
                options: .discreteAverage
            ) { _, result, error in
                guard error == nil,
                      let avg = result?.averageQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let bpm = avg.doubleValue(for: HKUnit.count().unitDivided(by: .minute()))
                continuation.resume(returning: bpm)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Sleep

    private func fetchSleepHours(for date: Date) async -> Double? {
        guard isAuthorized else { return nil }

        // Sleep data is typically recorded for the night before
        // So for mood on date X, we look at sleep from evening of X-1 to morning of X
        let calendar = Calendar.current
        guard let endOfSleep = calendar.date(bySettingHour: 12, minute: 0, second: 0, of: date),
              let startOfSleep = calendar.date(byAdding: .hour, value: -18, to: endOfSleep) else {
            return nil
        }

        let predicate = HKQuery.predicateForSamples(withStart: startOfSleep, end: endOfSleep, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: sleepAnalysisType,
                predicate: predicate,
                limit: HKObjectQueryNoLimit,
                sortDescriptors: nil
            ) { _, samples, error in
                guard error == nil,
                      let sleepSamples = samples as? [HKCategorySample] else {
                    continuation.resume(returning: nil)
                    return
                }

                // Sum up asleep time (not in bed time)
                var totalSleepSeconds: TimeInterval = 0
                for sample in sleepSamples {
                    // Filter for actual sleep states (not in bed)
                    if sample.value == HKCategoryValueSleepAnalysis.asleepCore.rawValue ||
                       sample.value == HKCategoryValueSleepAnalysis.asleepDeep.rawValue ||
                       sample.value == HKCategoryValueSleepAnalysis.asleepREM.rawValue ||
                       sample.value == HKCategoryValueSleepAnalysis.asleepUnspecified.rawValue {
                        totalSleepSeconds += sample.endDate.timeIntervalSince(sample.startDate)
                    }
                }

                if totalSleepSeconds > 0 {
                    let hours = totalSleepSeconds / 3600
                    continuation.resume(returning: hours)
                } else {
                    continuation.resume(returning: nil)
                }
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Active Calories

    private func fetchActiveCalories(start: Date, end: Date) async -> Int? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: activeEnergyType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum
            ) { _, result, error in
                guard error == nil,
                      let sum = result?.sumQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let calories = Int(sum.doubleValue(for: .kilocalorie()))
                continuation.resume(returning: calories)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Distance

    private func fetchDistance(start: Date, end: Date) async -> Double? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: distanceType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum
            ) { _, result, error in
                guard error == nil,
                      let sum = result?.sumQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let km = sum.doubleValue(for: .meterUnit(with: .kilo))
                continuation.resume(returning: km)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Resting Heart Rate

    private func fetchRestingHeartRate(start: Date, end: Date) async -> Double? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: restingHeartRateType,
                quantitySamplePredicate: predicate,
                options: .discreteAverage
            ) { _, result, error in
                guard error == nil,
                      let avg = result?.averageQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                let bpm = avg.doubleValue(for: HKUnit.count().unitDivided(by: .minute()))
                continuation.resume(returning: bpm)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Heart Rate Variability (HRV)

    private func fetchHRV(start: Date, end: Date) async -> Double? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKStatisticsQuery(
                quantityType: hrvType,
                quantitySamplePredicate: predicate,
                options: .discreteAverage
            ) { _, result, error in
                guard error == nil,
                      let avg = result?.averageQuantity() else {
                    continuation.resume(returning: nil)
                    return
                }

                // HRV is measured in milliseconds
                let ms = avg.doubleValue(for: .secondUnit(with: .milli))
                continuation.resume(returning: ms)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Mindful Minutes

    private func fetchMindfulMinutes(start: Date, end: Date) async -> Int? {
        guard isAuthorized else { return nil }

        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)

        return await withCheckedContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: mindfulSessionType,
                predicate: predicate,
                limit: HKObjectQueryNoLimit,
                sortDescriptors: nil
            ) { _, samples, error in
                guard error == nil,
                      let mindfulSamples = samples as? [HKCategorySample] else {
                    continuation.resume(returning: nil)
                    return
                }

                // Sum up all mindful session durations
                var totalSeconds: TimeInterval = 0
                for sample in mindfulSamples {
                    totalSeconds += sample.endDate.timeIntervalSince(sample.startDate)
                }

                if totalSeconds > 0 {
                    let minutes = Int(totalSeconds / 60)
                    continuation.resume(returning: minutes)
                } else {
                    continuation.resume(returning: nil)
                }
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Batch Fetch for Insights

    func fetchHealthData(for entries: [MoodEntryModel]) async -> [Date: DailyHealthData] {
        guard isEnabled && isAuthorized else { return [:] }

        var results: [Date: DailyHealthData] = [:]
        let calendar = Calendar.current

        // Get unique dates
        let dates = Set(entries.map { calendar.startOfDay(for: $0.forDate) })

        // Fetch health data for each date
        await withTaskGroup(of: (Date, DailyHealthData).self) { group in
            for date in dates {
                group.addTask {
                    let data = await self.fetchHealthData(for: date)
                    return (date, data)
                }
            }

            for await (date, data) in group {
                results[date] = data
            }
        }

        return results
    }

    // MARK: - Health Averages for AI Analysis

    /// Aggregated health metrics for a period - passed to AI for correlation analysis
    struct HealthAverages {
        // Activity
        let avgSteps: Int?
        let avgExerciseMinutes: Int?
        let avgActiveCalories: Int?
        let avgDistanceKm: Double?

        // Heart health
        let avgHeartRate: Double?
        let avgRestingHeartRate: Double?
        let avgHRV: Double?  // milliseconds

        // Recovery
        let avgSleepHours: Double?
        let avgMindfulMinutes: Int?

        // Sample sizes (for AI context)
        let daysWithHealthData: Int
        let totalMoodEntries: Int

        var hasData: Bool {
            daysWithHealthData > 0
        }
    }

    /// Compute aggregate health averages for a set of mood entries
    /// The AI will analyze these alongside mood data to find correlations
    func computeHealthAverages(entries: [MoodEntryModel], healthData: [Date: DailyHealthData]) -> HealthAverages {
        let calendar = Calendar.current

        var steps: [Int] = []
        var exercise: [Int] = []
        var calories: [Int] = []
        var distance: [Double] = []
        var heartRate: [Double] = []
        var restingHR: [Double] = []
        var hrv: [Double] = []
        var sleep: [Double] = []
        var mindful: [Int] = []

        for entry in entries {
            let date = calendar.startOfDay(for: entry.forDate)
            guard let health = healthData[date] else { continue }

            if let s = health.steps { steps.append(s) }
            if let e = health.exerciseMinutes { exercise.append(e) }
            if let c = health.activeCalories { calories.append(c) }
            if let d = health.distanceKm { distance.append(d) }
            if let h = health.averageHeartRate { heartRate.append(h) }
            if let r = health.restingHeartRate { restingHR.append(r) }
            if let v = health.hrv { hrv.append(v) }
            if let sl = health.sleepHours { sleep.append(sl) }
            if let m = health.mindfulMinutes { mindful.append(m) }
        }

        let daysWithData = Set(healthData.keys).count

        return HealthAverages(
            avgSteps: steps.isEmpty ? nil : steps.reduce(0, +) / steps.count,
            avgExerciseMinutes: exercise.isEmpty ? nil : exercise.reduce(0, +) / exercise.count,
            avgActiveCalories: calories.isEmpty ? nil : calories.reduce(0, +) / calories.count,
            avgDistanceKm: distance.isEmpty ? nil : distance.reduce(0, +) / Double(distance.count),
            avgHeartRate: heartRate.isEmpty ? nil : heartRate.reduce(0, +) / Double(heartRate.count),
            avgRestingHeartRate: restingHR.isEmpty ? nil : restingHR.reduce(0, +) / Double(restingHR.count),
            avgHRV: hrv.isEmpty ? nil : hrv.reduce(0, +) / Double(hrv.count),
            avgSleepHours: sleep.isEmpty ? nil : sleep.reduce(0, +) / Double(sleep.count),
            avgMindfulMinutes: mindful.isEmpty ? nil : mindful.reduce(0, +) / mindful.count,
            daysWithHealthData: daysWithData,
            totalMoodEntries: entries.count
        )
    }
}
