import Combine
import Foundation
import HealthKit

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

    private let healthStore = HKHealthStore()
    private var currentWorkoutStartDate: Date?
    private var currentWorkoutSessionId: UUID?

    @Published var isAuthorized = false
    @Published var isEnabled = false

    private let userDefaults = UserDefaults.standard
    private let isEnabledKey = "healthKitEnabled"
    private let workoutStartDateKey = "healthKitWorkoutStartDate"
    private let workoutSessionIdKey = "healthKitWorkoutSessionId"

    private init() {
        loadSettings()
        restoreActiveWorkout()
    }

    func loadSettings() {
        isEnabled = userDefaults.bool(forKey: isEnabledKey)

        if HKHealthStore.isHealthDataAvailable() {
            checkAuthorization()
        }
    }

    private func restoreActiveWorkout() {
        if let startDate = userDefaults.object(forKey: workoutStartDateKey) as? Date,
            let sessionIdString = userDefaults.string(forKey: workoutSessionIdKey),
            let sessionId = UUID(uuidString: sessionIdString)
        {
            currentWorkoutStartDate = startDate
            currentWorkoutSessionId = sessionId
            AppLogger.info("HealthKit: Restored active workout from \(startDate)", tag: "HealthKit")
        }
    }

    private func persistActiveWorkout() {
        if let startDate = currentWorkoutStartDate, let sessionId = currentWorkoutSessionId {
            userDefaults.set(startDate, forKey: workoutStartDateKey)
            userDefaults.set(sessionId.uuidString, forKey: workoutSessionIdKey)
        } else {
            userDefaults.removeObject(forKey: workoutStartDateKey)
            userDefaults.removeObject(forKey: workoutSessionIdKey)
        }
    }

    func verifyAndRestoreIntegration() async {
        guard isEnabled else { return }

        guard HKHealthStore.isHealthDataAvailable() else {
            AppLogger.warning("HealthKit: Device does not support HealthKit", tag: "HealthKit")
            return
        }

        checkAuthorization()

        if !isAuthorized {
            AppLogger.warning(
                "HealthKit: Integration was enabled but authorization lost, attempting to restore...",
                tag: "HealthKit")

            do {
                try await requestAuthorization()
                AppLogger.info("HealthKit: Authorization restored successfully", tag: "HealthKit")
            } catch {
                AppLogger.error(
                    "HealthKit: Failed to restore authorization: \(error.localizedDescription)",
                    tag: "HealthKit")
            }
        } else {
            AppLogger.info(
                "HealthKit: Integration verified - authorization is valid", tag: "HealthKit")
        }

        if hasActiveWorkout() {
            AppLogger.info(
                "HealthKit: Active workout restored - started at \(currentWorkoutStartDate!)",
                tag: "HealthKit")
        }
    }

    func setEnabled(_ enabled: Bool) {
        isEnabled = enabled
        userDefaults.set(enabled, forKey: isEnabledKey)
    }

    func requestAuthorization() async throws {
        guard HKHealthStore.isHealthDataAvailable() else {
            throw HealthKitError.notAvailable
        }

        let workoutType = HKObjectType.workoutType()
        let energyBurnedType = HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!

        let typesToShare: Set<HKSampleType> = [
            workoutType,
            energyBurnedType,
        ]

        let typesToRead: Set<HKObjectType> = [
            workoutType
        ]

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

        self.isAuthorized = true
    }

    private func checkAuthorization() {
        let workoutType = HKObjectType.workoutType()
        let status = healthStore.authorizationStatus(for: workoutType)

        isAuthorized = (status == .sharingAuthorized)
    }

    func startWorkout(startDate: Date, sessionId: UUID) async throws {
        guard isEnabled && isAuthorized else {
            return
        }

        guard HKHealthStore.isHealthDataAvailable() else {
            throw HealthKitError.notAvailable
        }

        currentWorkoutStartDate = startDate
        currentWorkoutSessionId = sessionId
        persistActiveWorkout()
        AppLogger.info("HealthKit: Started workout for session \(sessionId)", tag: "HealthKit")
    }

    func endWorkout(endDate: Date) async throws {
        guard isEnabled && isAuthorized else {
            return
        }

        guard let startDate = currentWorkoutStartDate else {
            return
        }

        guard HKHealthStore.isHealthDataAvailable() else {
            throw HealthKitError.notAvailable
        }

        let duration = endDate.timeIntervalSince(startDate)
        let calories = estimateCalories(durationInMinutes: duration / 60.0)

        let energyBurned = HKQuantity(unit: .kilocalorie(), doubleValue: calories)

        let workoutConfiguration = HKWorkoutConfiguration()
        workoutConfiguration.activityType = .climbing
        workoutConfiguration.locationType = .indoor

        let builder = HKWorkoutBuilder(
            healthStore: healthStore,
            configuration: workoutConfiguration,
            device: .local()
        )

        do {
            try await builder.beginCollection(at: startDate)

            let energyBurnedType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!
            let energySample = HKQuantitySample(
                type: energyBurnedType,
                quantity: energyBurned,
                start: startDate,
                end: endDate
            )
            try await builder.addSamples([energySample])

            try await builder.addMetadata([HKMetadataKeyIndoorWorkout: true])

            try await builder.endCollection(at: endDate)
            let workout = try await builder.finishWorkout()

            AppLogger.info(
                "HealthKit: Workout saved successfully with id: \(workout?.uuid.uuidString ?? "unknown")",
                tag: "HealthKit")

            currentWorkoutStartDate = nil
            currentWorkoutSessionId = nil
            persistActiveWorkout()
        } catch {
            AppLogger.error(
                "HealthKit: Failed to save workout: \(error.localizedDescription)", tag: "HealthKit"
            )
            currentWorkoutStartDate = nil
            currentWorkoutSessionId = nil
            persistActiveWorkout()

            throw HealthKitError.workoutSaveFailed
        }
    }

    func cancelWorkout() {
        currentWorkoutStartDate = nil
        currentWorkoutSessionId = nil
        persistActiveWorkout()
        AppLogger.info("HealthKit: Workout cancelled", tag: "HealthKit")
    }

    func hasActiveWorkout() -> Bool {
        return currentWorkoutStartDate != nil
    }

    private func estimateCalories(durationInMinutes: Double) -> Double {
        let caloriesPerMinute = 8.0
        return durationInMinutes * caloriesPerMinute
    }
}

enum HealthKitError: LocalizedError {
    case notAvailable
    case notAuthorized
    case workoutStartFailed
    case workoutSaveFailed

    var errorDescription: String? {
        switch self {
        case .notAvailable:
            return "HealthKit is not available on this device"
        case .notAuthorized:
            return "HealthKit authorization not granted"
        case .workoutStartFailed:
            return "Failed to start HealthKit workout"
        case .workoutSaveFailed:
            return "Failed to save workout to HealthKit"
        }
    }
}
