//
//  HealthKitManager.swift
//  Reflect
//
//  HealthKit State of Mind API integration for syncing mood data with Apple Health
//

import Foundation
import HealthKit
import os.log

@MainActor
class HealthKitManager: ObservableObject {
    static let shared = HealthKitManager()

    private let healthStore = HKHealthStore()
    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.88oakapps.reflect", category: "HealthKit")

    @Published var isAuthorized = false
    @Published var authorizationError: Error?
    @Published var isSyncing = false
    @Published var syncProgress: Double = 0
    @Published var syncedCount: Int = 0
    @Published var totalToSync: Int = 0
    @Published var syncStatus: String = ""

    // State of Mind sample type
    private var stateOfMindType: HKSampleType? {
        HKSampleType.stateOfMindType()
    }

    // Health data types for insights (read-only)
    // Core activity metrics
    private let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
    private let exerciseTimeType = HKQuantityType.quantityType(forIdentifier: .appleExerciseTime)!
    private let activeEnergyType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!
    private let distanceType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!

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

    // Sleep & recovery
    private let sleepAnalysisType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis)!

    // Mindfulness
    private let mindfulSessionType = HKCategoryType.categoryType(forIdentifier: .mindfulSession)!

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

    // MARK: - Authorization

    var isHealthKitAvailable: Bool {
        HKHealthStore.isHealthDataAvailable()
    }

    /// Request all HealthKit permissions in a single dialog
    /// Returns true if State of Mind write access was granted
    func requestAllPermissions() async throws -> Bool {
        guard isHealthKitAvailable else {
            throw HealthKitError.notAvailable
        }

        guard let stateOfMindType = stateOfMindType else {
            throw HealthKitError.typeNotAvailable
        }

        // Write permission for State of Mind
        let typesToShare: Set<HKSampleType> = [stateOfMindType]

        // Read permissions for mood-health correlation insights
        // These help Apple's AI provide personalized health insights
        let typesToRead: Set<HKObjectType> = [
            // State of Mind (read back our own data)
            stateOfMindType,

            // Activity - correlates with mood and energy levels
            stepCountType,
            exerciseTimeType,
            activeEnergyType,
            distanceType,
            workoutType,

            // Heart metrics - stress and recovery indicators
            heartRateType,
            restingHeartRateType,
            hrvType,

            // Sleep - strong correlation with mood
            sleepAnalysisType,

            // Mindfulness - meditation impact on mood
            mindfulSessionType
        ]

        logger.info("Requesting HealthKit permissions: share=1, read=\(typesToRead.count)")

        try await healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead)

        // Wait briefly for the authorization status to update after user interaction
        // The authorization sheet may have just dismissed
        try? await Task.sleep(nanoseconds: 500_000_000) // 0.5 second

        let status = checkAuthorizationStatus()
        isAuthorized = status == .sharingAuthorized
        logger.info("HealthKit authorization result: \(status.rawValue), isAuthorized=\(self.isAuthorized)")

        return isAuthorized
    }

    func requestAuthorization() async throws {
        _ = try await requestAllPermissions()
    }

    func checkAuthorizationStatus() -> HKAuthorizationStatus {
        guard let stateOfMindType = stateOfMindType else {
            return .notDetermined
        }
        return healthStore.authorizationStatus(for: stateOfMindType)
    }

    // MARK: - Save Mood to HealthKit

    func saveMood(_ mood: Mood, for date: Date, note: String? = nil) async throws {
        guard isHealthKitAvailable else {
            throw HealthKitError.notAvailable
        }

        guard checkAuthorizationStatus() == .sharingAuthorized else {
            throw HealthKitError.notAuthorized
        }

        // Convert Reflect mood to HealthKit valence (-1 to 1 scale)
        let valence = moodToValence(mood)

        // Create State of Mind sample
        let stateOfMind = HKStateOfMind(
            date: date,
            kind: .dailyMood,
            valence: valence,
            labels: labelsForMood(mood),
            associations: [.currentEvents]
        )

        try await healthStore.save(stateOfMind)
    }

    // MARK: - Sync All Existing Moods to HealthKit

    /// Syncs all existing mood entries from the app to HealthKit
    /// Call this after HealthKit authorization is granted to backfill historical data
    func syncAllMoods() async {
        guard isHealthKitAvailable else {
            logger.warning("HealthKit not available, skipping sync")
            syncStatus = "HealthKit not available"
            return
        }

        // Update UI immediately
        isSyncing = true
        syncProgress = 0
        syncedCount = 0
        syncStatus = "Preparing to sync..."

        // Check authorization with retry - user may have just granted permission
        var authorized = checkAuthorizationStatus() == .sharingAuthorized
        if !authorized {
            logger.info("Waiting for authorization status to update...")
            syncStatus = "Checking permissions..."

            // Retry a few times with delay - iOS may take a moment to update status
            for attempt in 1...5 {
                try? await Task.sleep(nanoseconds: 500_000_000) // 0.5 second
                authorized = checkAuthorizationStatus() == .sharingAuthorized
                if authorized {
                    logger.info("Authorization confirmed on attempt \(attempt)")
                    break
                }
            }
        }

        guard authorized else {
            logger.warning("HealthKit not authorized for sharing after retries, skipping sync")
            syncStatus = "Permission not granted for State of Mind"
            isSyncing = false
            return
        }

        syncStatus = "Loading mood entries..."

        // Fetch all mood entries
        let allEntries = DataController.shared.getData(
            startDate: Date(timeIntervalSince1970: 0),
            endDate: Date(),
            includedDays: [1, 2, 3, 4, 5, 6, 7]
        )

        // Filter out missing and placeholder entries
        let validEntries = allEntries.filter { entry in
            entry.mood != .missing && entry.mood != .placeholder
        }

        totalToSync = validEntries.count
        logger.info("Starting HealthKit sync for \(validEntries.count) mood entries")

        guard totalToSync > 0 else {
            logger.info("No mood entries to sync")
            syncStatus = "No moods to sync"
            isSyncing = false
            return
        }

        syncStatus = "Syncing \(totalToSync) moods..."

        // Create all samples upfront
        let samples: [HKStateOfMind] = validEntries.map { entry in
            HKStateOfMind(
                date: entry.forDate,
                kind: .dailyMood,
                valence: moodToValence(entry.mood),
                labels: labelsForMood(entry.mood),
                associations: [.currentEvents]
            )
        }

        // Save in batches for better performance
        let batchSize = 50
        var successCount = 0
        var failCount = 0

        for batchStart in stride(from: 0, to: samples.count, by: batchSize) {
            let batchEnd = min(batchStart + batchSize, samples.count)
            let batch = Array(samples[batchStart..<batchEnd])

            do {
                try await healthStore.save(batch)
                successCount += batch.count
            } catch {
                failCount += batch.count
                logger.error("Failed to sync batch starting at \(batchStart): \(error.localizedDescription)")
            }

            syncedCount = batchEnd
            syncProgress = Double(syncedCount) / Double(totalToSync)
            syncStatus = "Synced \(syncedCount) of \(totalToSync)..."
        }

        isSyncing = false
        if failCount == 0 {
            syncStatus = "✓ Synced \(successCount) moods to Health"
        } else {
            syncStatus = "Synced \(successCount), failed \(failCount)"
        }
        logger.info("HealthKit sync complete: \(successCount) succeeded, \(failCount) failed")
        AnalyticsManager.shared.track(.healthKitSyncCompleted(total: totalToSync, success: successCount, failed: failCount))
    }

    // MARK: - Delete All Moods from HealthKit

    /// Deletes all State of Mind samples created by this app
    /// Note: HealthKit only allows deleting samples that your app created
    func deleteAllMoods() async throws -> Int {
        guard isHealthKitAvailable else {
            throw HealthKitError.notAvailable
        }

        guard let stateOfMindType = stateOfMindType else {
            throw HealthKitError.typeNotAvailable
        }

        guard checkAuthorizationStatus() == .sharingAuthorized else {
            throw HealthKitError.notAuthorized
        }

        logger.info("Starting deletion of all State of Mind samples from this app")

        // Fetch all State of Mind samples (HealthKit will only return ones we can delete - our own)
        let samples = try await fetchMoods(
            from: Date(timeIntervalSince1970: 0),
            to: Date().addingTimeInterval(86400) // Include today + 1 day buffer
        )

        guard !samples.isEmpty else {
            logger.info("No State of Mind samples found to delete")
            return 0
        }

        logger.info("Found \(samples.count) State of Mind samples to delete")

        // Delete in batches
        let batchSize = 50
        var deletedCount = 0

        for batchStart in stride(from: 0, to: samples.count, by: batchSize) {
            let batchEnd = min(batchStart + batchSize, samples.count)
            let batch = Array(samples[batchStart..<batchEnd])

            do {
                try await healthStore.delete(batch)
                deletedCount += batch.count
                logger.info("Deleted batch \(batchStart/batchSize + 1): \(batch.count) samples")
            } catch {
                logger.error("Failed to delete batch starting at \(batchStart): \(error.localizedDescription)")
                throw error
            }
        }

        logger.info("Successfully deleted \(deletedCount) State of Mind samples from HealthKit")
        return deletedCount
    }

    // MARK: - Delete Mood for Date from HealthKit

    /// Deletes State of Mind samples created by this app for a specific date
    /// Note: HealthKit only allows deleting samples that your app created
    func deleteMood(for date: Date) async throws {
        guard isHealthKitAvailable else {
            throw HealthKitError.notAvailable
        }

        guard let stateOfMindType = stateOfMindType else {
            throw HealthKitError.typeNotAvailable
        }

        guard checkAuthorizationStatus() == .sharingAuthorized else {
            throw HealthKitError.notAuthorized
        }

        let calendar = Calendar.current
        let dayStart = calendar.startOfDay(for: date)
        guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return }

        let samples = try await fetchMoods(from: dayStart, to: dayEnd)

        guard !samples.isEmpty else {
            logger.info("No State of Mind samples found for \(date) to delete")
            return
        }

        try await healthStore.delete(samples)
        logger.info("Deleted \(samples.count) State of Mind samples for \(date)")
    }

    // MARK: - Read Mood from HealthKit

    func fetchMoods(from startDate: Date, to endDate: Date) async throws -> [HKStateOfMind] {
        guard isHealthKitAvailable else {
            throw HealthKitError.notAvailable
        }

        guard let stateOfMindType = stateOfMindType else {
            throw HealthKitError.typeNotAvailable
        }

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

        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)

        return try await withCheckedThrowingContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: stateOfMindType,
                predicate: predicate,
                limit: HKObjectQueryNoLimit,
                sortDescriptors: [sortDescriptor]
            ) { _, samples, error in
                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }

                let stateOfMindSamples = samples?.compactMap { $0 as? HKStateOfMind } ?? []
                continuation.resume(returning: stateOfMindSamples)
            }

            healthStore.execute(query)
        }
    }

    // MARK: - Conversion Helpers

    /// Convert Reflect Mood to HealthKit valence (-1 to 1)
    private func moodToValence(_ mood: Mood) -> Double {
        switch mood {
        case .horrible: return -1.0
        case .bad: return -0.5
        case .average: return 0.0
        case .good: return 0.5
        case .great: return 1.0
        case .missing, .placeholder: return 0.0
        }
    }

    /// Convert HealthKit valence to Reflect Mood
    func valenceToMood(_ valence: Double) -> Mood {
        switch valence {
        case ..<(-0.75): return .horrible
        case -0.75..<(-0.25): return .bad
        case -0.25..<0.25: return .average
        case 0.25..<0.75: return .good
        default: return .great
        }
    }

    /// Get HealthKit labels for a mood
    private func labelsForMood(_ mood: Mood) -> [HKStateOfMind.Label] {
        switch mood {
        case .horrible:
            return [.sad, .stressed, .anxious]
        case .bad:
            return [.sad, .stressed]
        case .average:
            return [.calm, .indifferent]
        case .good:
            return [.happy, .calm, .content]
        case .great:
            return [.happy, .excited, .joyful]
        case .missing, .placeholder:
            return []
        }
    }
}

// MARK: - Errors

enum HealthKitError: LocalizedError {
    case notAvailable
    case notAuthorized
    case typeNotAvailable

    var errorDescription: String? {
        switch self {
        case .notAvailable:
            return "HealthKit is not available on this device"
        case .notAuthorized:
            return "HealthKit access not authorized"
        case .typeNotAvailable:
            return "State of Mind type not available"
        }
    }
}

