//
//  WatchWorkout.swift
//  Werkout_watch Watch App
//
//  Created by Trey Tartt on 7/1/24.
//

import Foundation
import HealthKit

class WatchWorkout: NSObject, ObservableObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
    static let shared = WatchWorkout()
    let healthStore = HKHealthStore()
    var hkWorkoutSession: HKWorkoutSession!
    var hkBuilder: HKLiveWorkoutBuilder!
    var heartRates = [Int]()
    
    @Published var heartValue: Int?
    @Published var isInWorkout = false
    @Published var isPaused = false

    private override init() {
        super.init()
        setupCore()
    }
    
    func setupCore() {
        do {
            let configuration = HKWorkoutConfiguration()
            configuration.activityType = .functionalStrengthTraining
            configuration.locationType = .indoor
            
            hkWorkoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
            hkBuilder = hkWorkoutSession.associatedWorkoutBuilder()
            hkWorkoutSession.delegate = self
            hkBuilder.delegate = self
            
            /// Set the workout builder's data source.
            hkBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
                                                           workoutConfiguration: configuration)
        } catch {
            fatalError()
        }
    }
    
    func startWorkout() {
        if isInWorkout { return }
        isInWorkout = true
        setupCore()
        hkWorkoutSession.startActivity(with: Date())
        //WKInterfaceDevice.current().play(.start)
    }
    
    func stopWorkout(sendDetails: Bool) {
//        hkWorkoutSession.endCurrentActivity(on: Date())
        hkWorkoutSession.end()
    }
    
    func togglePaused() {
        self.isPaused.toggle()
    }
    
    func beginDataCollection() {
        hkBuilder.beginCollection(withStart: Date()) { (succ, error) in
            if !succ {
                fatalError("Error beginning collection from builder: \(String(describing: error)))")
            }
        }
        DispatchQueue.main.async {
            self.isInWorkout = true
        }
    }
    
    func getWorkoutBuilderDetails(completion: @escaping (() -> Void)) {
        DispatchQueue.main.async {
            self.heartRates.removeAll()
            self.isInWorkout = false
        }
        
        hkBuilder.endCollection(withEnd: Date()) { (success, error) in
            if !success || error != nil {
                completion()
                return
            }
            
            self.hkBuilder.finishWorkout { (workout, error) in
                guard let workout = workout else {
                    completion()
                    return
                }
//                if !sendDetails { return }
                
                DispatchQueue.main.async() {
                    let watchFinishWorkoutModel = WatchFinishWorkoutModel(healthKitUUID: workout.uuid)
                    let data = try! JSONEncoder().encode(watchFinishWorkoutModel)
                    let watchAction = WatchActions.workoutComplete(data)
                    let watchActionData = try! JSONEncoder().encode(watchAction)
                    DataSender.send(watchActionData)
                    completion()
                }
            }
        }
    }
    
    func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
        switch toState {
        case .notStarted:
            print("not started")
        case .running:
            print("running")
            startWorkout()
            beginDataCollection()
        case .ended:
            print("ended")
            getWorkoutBuilderDetails(completion: {
                self.setupCore()
            })
        case .paused:
            print("paused")
        case .prepared:
            print("prepared")
        case .stopped:
            print("stopped")
        @unknown default:
            fatalError()
        }
        print("[workoutSession] Changed State: \(toState.rawValue)")
    }
    
    func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
        print("[didFailWithError] Workout Builder changed event: \(error.localizedDescription)")
        // trying to go from ended to something so just end it all
        if workoutSession.state == .ended {
            getWorkoutBuilderDetails(completion: {
                self.setupCore()
            })
        }
    }
    
    func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
        for type in collectedTypes {
            guard let quantityType = type as? HKQuantityType else {
                return
            }
            switch quantityType {
            case HKQuantityType.quantityType(forIdentifier: .heartRate):
                DispatchQueue.main.async() {
                    let statistics = workoutBuilder.statistics(for: quantityType)
                    let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
                    let value = statistics!.mostRecentQuantity()?.doubleValue(for: heartRateUnit)
                    self.heartValue = Int(Double(round(1 * value!) / 1))
                    self.heartRates.append(Int(Double(round(1 * value!) / 1)))
                    print("[workoutBuilder] Heart Rate: \(String(describing: self.heartValue))")
                }
            default:
                return
            }
        }
    }
    
    func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
        guard let workoutEventType = workoutBuilder.workoutEvents.last?.type else { return }
        print("[workoutBuilderDidCollectEvent] Workout Builder changed event: \(workoutEventType.rawValue)")
    }
}
