//
//  FinalViewModel.swift
//  MyTatvaCare
//
//  Created by Nirav Ramani on 31/05/25.
//

import Foundation

@objc public class FinalViewModelManager: NSObject {
    @objc public static let shared = FinalViewModelManager()
    @objc let viewModel = FinalViewModel()

    private override init() {
        super.init()
        // viewModel.initialize()
        // KLTDatabaseHandler.shared().reloadQueryHistoryData()
        KLTStartUp.shared().setup()
       viewModel.initialize()
       viewModel.syncDeviceStatus()
    }
    
//    @objc public func callForObserveTransmitterUnbindStatus() {
//        let sensorID = UserDefaults.standard.string(forKey: "sensorId") ?? ""
//        if !sensorID.isEmpty {
//            viewModel.getStatus()
//        }
//    }
    
    
    @objc(callForObserveTransmitterUnbindStatusWithCompletionWithPatientId:completion:)
    public func callForObserveTransmitterUnbindStatusWithCompletion(
        patientId: String,
        _ completion: @escaping (_ finalResponse: [String: Any]?, _ error: Error?) -> Void
    ) {
        // Load JSON data from UserDefaults
        guard let data = UserDefaults.standard.data(forKey: "CGMStatusItem") else {
            let error = NSError(
                domain: "SensorData",
                code: 404,
                userInfo: [NSLocalizedDescriptionKey: "No cached response found"]
            )
            completion(nil, error)
            return
        }

        do {
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            // Forward to actual handler, which includes a safety check
            self.callForObserveTransmitterUnbindStatusWithResponse(patientId: patientId, json, completion: completion)
        } catch {
            completion(nil, NSError(domain: "SensorData", code: 405, userInfo: [NSLocalizedDescriptionKey: "Failed to parse cached JSON"]))
        }
    }


    @objc public func callForObserveTransmitterUnbindStatusWithResponse(
        patientId: String,
        _ response: Any,  // Changed from [String: Any] to Any to add type safety
        completion: @escaping (_ finalResponse: [String: Any]?, _ error: Error?) -> Void
    ) {
        // Ensure response is a dictionary
        guard let responseDict = response as? [String: Any] else {
            let error = NSError(
                domain: "SensorData",
                code: 406,
                userInfo: [NSLocalizedDescriptionKey: "Response is not a dictionary"]
            )
            completion(nil, error)
            return
        }

        guard let status = responseDict["status"] as? String, status == "success",
              let dataArray = responseDict["data"] as? [[String: Any]],
              let data = dataArray.first else {
            let error = NSError(
                domain: "SensorData",
                code: 400,
                userInfo: [NSLocalizedDescriptionKey: "Invalid status or missing data"]
            )
            completion(nil, error)
            return
        }

        guard let sensorId = data["sensorId"] as? String,
              let startDate = data["startDate"] as? String,
              let endDate = data["endDate"] as? String,
              let statusValue = data["status"] as? String,
              let daysToExpire = data["daysToExpire"] as? Int else {
            let error = NSError(
                domain: "SensorData",
                code: 401,
                userInfo: [NSLocalizedDescriptionKey: "Missing or invalid sensor data in 'data'"]
            )
            completion(nil, error)
            return
        }
        UserDefaults.standard.set(sensorId, forKey: "sensorId")
        let baseResponse: [String: Any] = [
            "sensorId": sensorId,
            "startDate": startDate,
            "endDate": endDate,
            "status": statusValue,
            "daysToExpire": daysToExpire
        ]

        let finalResponse = self.evaluateUnbindStatus(response: baseResponse, startDate: startDate, endDate: endDate, sensorId: sensorId, patientId: patientId)
        completion(finalResponse, nil)
    }

       // MARK: - Main Logic
    @objc public func evaluateUnbindStatus(
        response: [String: Any],
        startDate: String,
        endDate: String,
        sensorId: String,
        patientId: String
    ) -> [String: Any] {
        let updatedResponse = response
        let lastSensorId = UserDefaults.standard.string(forKey: "sensorId") ?? ""
        let lastPatientId = UserDefaults.standard.string(forKey: "patientId") ?? ""
        
        if (lastPatientId != "" && lastSensorId != "" && (lastPatientId != patientId || lastSensorId != sensorId)) {
            KLTDatabaseHandler.shared().deleteSQLiteDataArray()
            return [:]
        }
        
        if viewModel.isCurrentDateInRange(startDate: startDate, endDate: endDate) {
            print("Current date is in range")

            let canConnectOther = KLTLocalSettingManager.shareInstance().canConnectOtherDevice
            let connectedPeripheral = viewModel.manager.connectedPeripheral
            
            
            if connectedPeripheral == nil && !canConnectOther {
                let bluetoothOff = UserDefaults.standard.bool(forKey: "bluetoothOff")
//                if bluetoothOff {
//                    viewModel.debouncer.update(with: .bluetoothOff)
//                }
                
                if bluetoothOff == false {
                    let data = KLTDatabaseHandler.shared().hasDeviceData
                    if data() {
                        viewModel.manager.startScan()
                       // viewModel.debouncer.update(with: .transmitterDisconnect)
                    } else {
                        viewModel.debouncer.update(with: .transmitterDisconnect)
                    }
                    
                } else {
                    viewModel.debouncer.update(with: .bluetoothOff)
                }
            } else {
                if let device = KLTBluetoothManager.shared().currentDevice {
                    if device.connectedDateTime == nil && device.disconnectedDateTime == nil {
                        viewModel.manager.startScan()
                        viewModel.debouncer.update(with: .transmitterDisconnect)
                    } else {
                        UserDefaults.standard.set(sensorId, forKey: "sensorId")
                        UserDefaults.standard.set(patientId, forKey: "patientId")
                    }
                } else { //when user app unistall and then install
                    print("⚠️ currentDevice is nil")
                    viewModel.manager.startScan()
                    viewModel.debouncer.update(with: .transmitterDisconnect)
                }
            }
        }

        return updatedResponse
    }
}

class FinalViewModel: NSObject {
    var initialPeriod = 0
    var totalSeconds : TimeInterval = 60 * 60
    let manager: KLTBluetoothManager!
    var countdownTimer: Timer?
    var startScanTimer: Timer?
    let debouncer = EnumDebouncer<CGMConnectionStatus>()
    
    var lastBluetoothStatus: BluetoothManagerStatus = .disconnected
    var delayedDisconnectTimer: Timer?

    private var isUploading = false
    
    
    @objc public override init() {
        self.manager = KLTBluetoothManager.shared()
        super.init()
        debouncer.onDebounceSuccess = { value in
            print("Blutooth is------------------->>>>>>>>>> \(value)")
            let envType = UserDefaults.standard.string(forKey: "envType") ?? "uat"
            API.shared.sendStatus(environment: .stage, status: value)
//            if value == .errorCommon {
//                self.manager.startScan()
//            }
            print("Same type held for an hour!")
        }
     }

     func syncDeviceStatus() {
       if manager.currentDevice != nil {
           if manager.currentDevice.disconnectedDateTime == nil && manager.currentDevice.connectedDateTime != nil{
               debouncer.update(with: .connected)
           } else if manager.currentDevice.disconnectedDateTime == nil && manager.currentDevice.connectedDateTime != nil{
               debouncer.update(with: .disconnected)
           }
       }
   }

    @objc public func initialize() {
        //getStatus()
        startCountDown()
        
        if ((manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {

            let data = KLTDatabaseHandler.shared().hasDeviceData
            if data() {
                manager.startScan()
              //  debouncer.update(with: .disconnected)
                print("✅ Device data exists")
            } else{
                debouncer.update(with: .transmitterDisconnect)
                print("❌ Failed to get app delegate or context")
            }
        
        }  else {
            print("here")
            //debouncer.update(with: .connected)
            //API.shared.sendStatus(status: .connected) 计算 算法入参
            if let last = manager.currentDevice { }
            else {
                //self.manager.closeBleSensor()
                //manager.startScan()
                //  debouncer.update(with: .disconnected)
                //debouncer.update(with: .transmitterDisconnectBox)
            }
        }
    }
    
    func isDeviceDataAvailable(context: NSManagedObjectContext) -> Bool {
        let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Device")
        fetchRequest.fetchLimit = 1
        
        do {
            let count = try context.count(for: fetchRequest)
            return count > 0
        } catch {
            print("❌ Failed to fetch Device count: \(error.localizedDescription)")
            return false
        }
    }
    
    func getStatus(encryptionKey: String = PROD_ENC_KEY,
                   encryptionIv: String = PROD_ENC_IV) {
        print("ios token:", TOKEN)
        let url = URL(string: "https://api-feature2.mytatva.in/api/v8/cgm/status")!
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        // Set headers
        //request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue(PROD_API_KEY, forHTTPHeaderField: "api-key")
        request.addValue(TOKEN, forHTTPHeaderField: "token")
        
      
        // Set request body
        
        // Use regular URLSession for foreground/background compatibility
        let session = URLSession.shared
        
        let task = session.dataTask(with: request) { data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }
            guard let data = data else {
                print("No data received")
                return
            }
            if let responseString = String(data: data, encoding: .utf8) {
                print("===>Server Response: \(responseString)")
                if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
                    print("===>Decrypted response (for verification): \(decrypted)")
                    
                    if let decrptedData = decrypted.data(using: .utf8) {
                        let decoded = try? JSONDecoder().decode(CGMStatusResponse.self, from: decrptedData)
                        
                        guard let sensor = decoded?.data.first,
                              !sensor.startDate.isEmpty,
                              !sensor.endDate.isEmpty else {
                            print("No valid sensor data")
                            return
                        }
                        
                        let startDateStr = sensor.startDate
                        let endDateStr = sensor.endDate
                        
                        print("Start Date: \(startDateStr)")
                        print("End Date: \(endDateStr)")
                        UserDefaults.standard.set(sensor.sensorId, forKey: "sensorId")
                        if self.isCurrentDateInRange(startDate: startDateStr, endDate: endDateStr) {
                            print("Current date is in range")
                            if ((self.manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
                                self.manager.startScan()
                                self.debouncer.update(with: .disconnected)
                            }  else {
                                print("here")
                                //debouncer.update(with: .connected)
                                //API.shared.sendStatus(status: .connected) 计算 算法入参
                                if let last = self.manager.currentDevice { }
                                else {
                                    //self.manager.closeBleSensor()
                                    //manager.startScan()
                                    //self.debouncer.update(with: .disconnected)
                                    //debouncer.update(with: .transmitterDisconnectBox)
                                }
                            }
//                            if KLTBluetoothManager.shared().currentDevice.initialEndDate != nil  {
//                                self.sendStatus(status: .transmitterDisconnect)
//                            }
                        }
                    }
                }
            }
        }
        task.resume()
        
    }
    
    func isCurrentDateInRange(startDate: String, endDate: String) -> Bool {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        guard let start = formatter.date(from: startDate),
              let end = formatter.date(from: endDate) else {
            return false
        }
        
        let now = Date()
        return (now >= start) && (now <= end)
    }
    
    func startCountDown() {
        
        // Most recently connected device
        if let last = manager.currentDevice {
            let eDevice = EDevice.getEnumDevice(last.advertise?.localName ?? "")
            initialPeriod = Int(eDevice.initNumber * 3 * 60)
            // Calculate initialization countdown time
            if let beginDate = KLTDateFormatter.shared.getDateFromWholeString(last.initialBeginDate ?? "") {
                let time = Date().timeIntervalSince(beginDate)
                if Int(time) > self.initialPeriod {
                    // Initialization completed, normal use
                    
                } else {
                    
                    // During initialization, notify the initialization page to restart the countdown
                    totalSeconds = Double(initialPeriod) - TimeInterval(time)
                    
                    //for countdownlabel
                    print(String.getTimeString(with: Int(totalSeconds)))

                    if countdownTimer != nil {
                        countdownTimer?.invalidate()
                        countdownTimer = nil
                    }
                    countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
                     
                }
            }
        } else {
            // no device is connected
            print("no device is connected")
        }
        
        addAllObservers()
    }

    @objc func onBLEStatusUpdate(_ notification: Notification) {
        if let statusNumber = notification.object as? NSNumber {
            let status = statusNumber.intValue
            print("[UI] BLE status********: \(status)")
            // update UI or call startScan again
        }
    }

    func addAllObservers() {
        manager.addObserver(self, forKeyPath: "status", options: .new, context: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(bluetoothEnable(_:)), name: NSNotification.Name("BluetoothEnable"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onBLEStatusUpdate(_:)), name: NSNotification.Name("BLEStatusUpdated"), object: nil)

      //  NotificationCenter.default.addObserver(self, selector: #selector(errorStatusFromTransmitter(_:)), name: NSNotification.Name("ErrorStatusFromTransmitter"), object: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(handleLatestReceiveData(_:)), name: NSNotification.Name(KLTAlertCurrentInInitialNotify), object: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(updateData(_:)), name: NSNotification.Name(KLTUpdateDataNotify), object: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(checkErrorStatusFromLastBatchData(_:)), name: NSNotification.Name("CheckErrorStatusFromLastBatchData"), object: nil)
    }

    func removeAllObservers() {
        manager.removeObserver(self, forKeyPath: "status")
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name("BluetoothEnable"), object: nil)
        
       // NotificationCenter.default.removeObserver(self, name: NSNotification.Name("ErrorStatusFromTransmitter"), object: nil)
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(KLTAlertCurrentInInitialNotify), object: nil)
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(KLTUpdateDataNotify), object: nil)
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name("CheckErrorStatusFromLastBatchData"), object: nil)
    }
    
   
    func uploadData(data: [GlucoseData]) {
        isUploading = true
        let batchSize = 40
        let batches = stride(from: 0, to: data.count, by: batchSize).map {
            Array(data[$0..<min($0 + batchSize, data.count)])
        }
        uploadBatch(batches: batches, index: 0)
    }

    private func uploadBatch(batches: [[GlucoseData]], index: Int) {
        guard index < batches.count else {
            if index != 0 {
                print("Last batch ===>>> \(batches.last?.last)")
                NotificationCenter.default.post(name: Notification.Name("CheckErrorStatusFromLastBatchData"), object: batches.last?.last)
            }
            print("✅ All batches uploaded")
            isUploading = false
            return
        }

        let batch = batches[index]
        
        let filteredBatch = batch.filter {
            let gluADCValue = ($0.gluADC as? Int) ?? 0
            let isDeleteValue = ($0.isDelete as? Int) ?? 0
            return  isDeleteValue == 0 //&& gluADCValue != 0
        }

        
        let cgmLogs = filteredBatch.map { ReceiveDataToLog(data: $0) }
        let payload = Payload(logs: cgmLogs)

        // Check if logs array is empty - skip API call if so
        if cgmLogs.isEmpty {
            print("Batch \(index + 1) skipped - logs array is empty, not hitting API")
            // Move to next batch
            self.uploadBatch(batches: batches, index: index + 1)
            return
        }

        print("====================================> called uploadBatch")
        API.shared.postCGMData(data: payload, environment: .stage) {
            print("====================================> uploaded successfully")
            print("✅ Batch \(index + 1) uploaded successfully")
            KLTDatabaseHandler.shared().deleteReceiveDataArray(batch)
            self.uploadBatch(batches: batches, index: index + 1)
        } onFailure: { error in
            print("❌ Failed to upload batch \(index + 1): \(String(describing: error?.localizedDescription))")
            self.isUploading = false
        }
    }

    
//    private func uploadBatch(batches: [[GlucoseData]], index: Int) {
//        guard index < batches.count else {
//            if index != 0 {
//                print("Last batch ===>>> \(batches.last?.last)")
//                NotificationCenter.default.post(name: Notification.Name("CheckErrorStatusFromLastBatchData"), object: batches.last?.last)
//            }
//            print("✅ All batches uploaded")
//            return
//        }
//
//        let batch = batches[index]
//        let cgmLogs = batch.map { ReceiveDataToLog(data: $0) }
//        let payload = Payload(logs: cgmLogs)
//
//        print("====================================> called uploadBatch")
//        API.shared.postCGMData(data: payload, environment: .stage) {
//            print("====================================> uploaded successfully")
//            print("✅ Batch \(index + 1) uploaded successfully")
//            KLTDatabaseHandler.shared().deleteReceiveDataArray(batch)
//            self.uploadBatch(batches: batches, index: index + 1)
//        } onFailure: { error in
//            print("❌ Failed to upload batch \(index + 1): \(String(describing: error?.localizedDescription))")
//            // Optionally retry or stop here
//        }
//    }

    
    @objc func updateData(_ notification: Notification) {
        // Prevent concurrent duplicate uploads triggered by rapid KLTUpdateDataNotify posts
        // (e.g. multiple 0x22 resend packets firing the notification in quick succession)
        guard !isUploading else {
            print("⚠️ Upload already in progress — skipping duplicate updateData call")
            return
        }

        guard let data = KLTDatabaseHandler.shared().queryAllReceiveData() as? [ReceiveData] else {
            print("⚠️ Failed to cast queryAllReceiveData() to [ReceiveData]")
            return
        }
        print("===> all data count: \(data.count)")

        guard let device = KLTBluetoothManager.shared().currentDevice else {
            print("⚠️ No current device found")
            return
        }

        guard let arrayFromInitial = KLTDatabaseHandler.shared().queryGlucoseData(with: device) else {
            print("⚠️ Failed to cast queryGlucoseData to [GlucoseData]")
            return
        }

        print("======================> arrayFromInitial count: \(arrayFromInitial.count)")

        self.uploadData(data: arrayFromInitial)
    }

    @objc func checkErrorStatusFromLastBatchData(_ notification: Notification) {
        guard let receiveData = notification.object as? GlucoseData else { return }

        let errorCode = ErrorCode(rawValue: Int32(receiveData.error?.intValue ?? 0))
        if errorCode == ErrorCode.ERROR_CODE_FLOODING_WATER {
            debouncer.update(with: CGMConnectionStatus.moistureDetect)
            //API.shared.sendStatus(status: .moistureDetect)
        } else if errorCode == ErrorCode.ERROR_CODE_CURRENT_SMALL || errorCode == ErrorCode.ERROR_CODE_NOISE || errorCode == ErrorCode.ERROR_CODE_SENSITIVITY_ATTENUATION {
            debouncer.update(with: CGMConnectionStatus.weakSignal)
            //API.shared.sendStatus(status: .weakSignal)
        } else if errorCode != ErrorCode.ERROR_CODE_NONE {
            debouncer.update(with: CGMConnectionStatus.errorCommon)
            //API.shared.sendStatus(status: .errorCommon)
        }
        if receiveData.countdownDays == 0 && receiveData.countdownHours == 0 && receiveData.countdownMinutes == 0 {
            debouncer.update(with: CGMConnectionStatus.expired)
            //API.shared.sendStatus(status: .expired)
        }
    }
    
//    @objc func errorStatusFromTransmitter(_ notification: Notification) {
//        guard let receiveData = notification.object as? ReceiveData else { return }
//
//        let errorCode = ErrorCode(rawValue: Int32(receiveData.error?.intValue ?? 0))
//        if errorCode == ErrorCode.ERROR_CODE_FLOODING_WATER {
//            debouncer.update(with: CGMConnectionStatus.moistureDetect)
//            //API.shared.sendStatus(status: .moistureDetect)
//        } else if errorCode == ErrorCode.ERROR_CODE_CURRENT_SMALL || errorCode == ErrorCode.ERROR_CODE_NOISE || errorCode == ErrorCode.ERROR_CODE_SENSITIVITY_ATTENUATION {
//            debouncer.update(with: CGMConnectionStatus.weakSignal)
//            //API.shared.sendStatus(status: .weakSignal)
//        } else if errorCode != ErrorCode.ERROR_CODE_NONE {
//            debouncer.update(with: CGMConnectionStatus.errorCommon)
//            //API.shared.sendStatus(status: .errorCommon)
//        }
//        if receiveData.countdownDays == 0 && receiveData.countdownHours == 0 && receiveData.countdownMinutes == 0 {
//            debouncer.update(with: CGMConnectionStatus.expired)
//            //API.shared.sendStatus(status: .expired)
//        }
//    }
    
    @objc func handleLatestReceiveData(_ notification: Notification) {
        print("====> handleLatestReceiveData")
        guard let receiveData = notification.object as? ReceiveData else { return }

        let errorCode = ErrorCode(rawValue: Int32(receiveData.error?.intValue ?? 0))
        
        let hasExceptionCurrent: Bool = (errorCode == ErrorCode.ERROR_CODE_CURRENT_SMALL || errorCode == ErrorCode.ERROR_CODE_NOISE ||
                                         errorCode == ErrorCode.ERROR_CODE_SENSITIVITY_ATTENUATION || errorCode == ErrorCode.ERROR_CODE_TAKE_OFF ||
                                         errorCode == ErrorCode.ERROR_CODE_BREAKAGE || errorCode == ErrorCode.ERROR_CODE_TOUCH ||
                                         errorCode == ErrorCode.ERROR_CODE_FLOODING_WATER)

        if hasExceptionCurrent {
            guard let startDate = KLTFormat.getDate(from: manager.currentDevice.initialBeginDate ?? "") else { return }

            // Get current UTC date from local date string
            let now = Date()
            let nowString = KLTDateFormatter.shared.getWholeString(from: now)
            let utcDateString = KLTDateFormatter.shared.getUTCFormatDate(from: nowString) ?? ""
            guard let currentDate = KLTDateFormatter.shared.getDateFromWholeString(utcDateString) else { return }

            let seconds = Calendar(identifier: .gregorian)
                .dateComponents([.second], from: startDate, to: currentDate).second ?? 0

            let minutes = seconds / 60
            if minutes > 17 {
               
            }
        }
    }
    
    @objc func bluetoothEnable(_ notification: Notification) {
        if let isBluetoothEnabled = notification.object as? Bool {
            if !isBluetoothEnabled {
                UserDefaults.standard.set(true, forKey: "bluetoothOff")
                debouncer.update(with: CGMConnectionStatus.bluetoothOff)
                //API.shared.sendStatus(status: .bluetoothOff)
            } else {
                UserDefaults.standard.set(false, forKey: "bluetoothOff")
            }
        }
        if ((manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
            manager.startScan()
        }
    }
    
    @objc func countdown() {
        if let last = KLTDatabaseHandler.shared().isLatestDeviceDisconnected() {
            if let beginDate = KLTDateFormatter.shared.getDateFromWholeString(last.initialBeginDate ?? "") {
                let time = Date().timeIntervalSince(beginDate)
                totalSeconds = TimeInterval(3600 - Int(time))
            }
        }

        if totalSeconds <= 0 {
            countdownTimer?.invalidate()
            countdownTimer = nil

            startScanTimer?.invalidate()
            startScanTimer = nil

            //removeAllObservers()

            //KLTLaunchConfiguration.sharedManager.resetRootViewController(type: .normalUsed)
            return
        }

        totalSeconds -= 1
        print(String.getTimeString(with: Int(totalSeconds)))
        //countDownValueLabel.text =
    }
    
    override func observeValue(forKeyPath keyPath: String?,
                               of object: Any?,
                               change: [NSKeyValueChangeKey : Any]?,
                               context: UnsafeMutableRawPointer?) {
        
        guard keyPath == "status",
              let newValue = change?[.newKey] as? Int,
              let status = BluetoothManagerStatus(rawValue: newValue) else {
            return
        }
        
        print("===>device status is -> \(status)")
        
        // Cancel any pending delayed disconnect if state changes
//        if status != .disconnected, delayedDisconnectTimer != nil {
//            delayedDisconnectTimer?.invalidate()
//            delayedDisconnectTimer = nil
//            print("===> Cancelled delayed disconnect due to state change to \(status)")
//        }

        switch status {
            
        case .disconnected:
            print("lastBluetoothStatus-------->> \(lastBluetoothStatus)")
            if lastBluetoothStatus == .connected {
                print("===> Previously connected, delaying disconnect for 1 minute")
                self.lastBluetoothStatus = .disconnected
                delayedDisconnectTimer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: false) { [weak self] _ in
                    guard let self = self else { return }
                    print("lastBluetoothStatus-------->> \(lastBluetoothStatus)")
                    if self.lastBluetoothStatus == .disconnected {
                        //1 minute passed, confirmed disconnected
                        self.debouncer.update(with: CGMConnectionStatus.disconnected)
                    } else {
                        print("===> Status changed in between, ignoring disconnect")
                    }
                    self.delayedDisconnectTimer?.invalidate()
                    self.delayedDisconnectTimer = nil
                }
            } else {
                print("===> Not previously connected, immediate disconnect")
                lastBluetoothStatus = .disconnected
                delayedDisconnectTimer?.invalidate()
                delayedDisconnectTimer = nil
                debouncer.update(with: CGMConnectionStatus.disconnected)
            }
            
            if KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
                endBleSensorCycle()
            } else {//if UserDefaults.standard.integer(forKey: "bgmode") != 1 
                manager.startScan()
            }

        case .connected:
            lastBluetoothStatus = .connected
            if let timer = startScanTimer {
                timer.invalidate()
                startScanTimer = nil
                print("--------------------------- startScanTimer is nil")
            }

            debouncer.update(with: CGMConnectionStatus.connected)

        case .timeOut:
            lastBluetoothStatus = .timeOut
            if !KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
                startScanTimer = Timer.scheduledTimer(timeInterval: 20,
                                                      target: self,
                                                      selector: #selector(startScan),
                                                      userInfo: nil,
                                                      repeats: false)
            }

        case .sensorLostPower:
            lastBluetoothStatus = .sensorLostPower
            sensorLostPower()
            
        case .updateBindWatchSuccess:
            lastBluetoothStatus = .updateBindWatchSuccess
            startCountDown()
            
        case .closed:
            lastBluetoothStatus = .closed
            debouncer.update(with: CGMConnectionStatus.transmitterDisconnect)
            
        default:
            break
        }
    }

    
//    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
//        guard keyPath == "status",
//              let newValue = change?[.newKey] as? Int,
//              let status = BluetoothManagerStatus(rawValue: newValue) else {
//            return
//        }
//
//        print("===>device status is -> \(status)")
//
//        switch status {
//
//        case .disconnected:
//            if KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
//                // Unbind then disconnect
//                endBleSensorCycle()
//            } else {
//                if UserDefaults.standard.integer(forKey: "bgmode") != 1 {
//                    manager.startScan()
//                }
//            }
//            //API.shared.sendStatus(status: .disconnected)
//            print("===>device is disconnected")
//
//            debouncer.update(with: CGMConnectionStatus.disconnected)
//
//        case .tryToConnect:
//            //            if UserDefaults.standard.integer(forKey: "bgmode") != 1 {
//            //                DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
//            //                    self.manager.startScan()
//            //                }
//            //            }
//            break
//        case .connected:
//            if let timer = startScanTimer {
//                timer.invalidate()
//                startScanTimer = nil
//                print("--------------------------- startScanTimer is nil")
//            }
//            //API.shared.sendStatus(status: .connected)
//            print("===>device is connected")
//            debouncer.update(with: CGMConnectionStatus.connected)
//        case .timeOut:
//            if !KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
//                startScanTimer = Timer.scheduledTimer(timeInterval: 20,
//                                                      target: self,
//                                                      selector: #selector(startScan),
//                                                      userInfo: nil,
//                                                      repeats: false)
//            }
//            print("===>time out")
//
//        case .sensorLostPower:
//            sensorLostPower()
//            print("===>sensor lost power")
//        case .updateBindWatchSuccess:
//            //KLTLaunchConfiguration.sharedManager.bindSuccessRootViewController()
//            startCountDown()
//            print("===>Success done")
//        case .closed:
//            debouncer.update(with: CGMConnectionStatus.transmitterDisconnect)
//            //API.shared.sendStatus(status: .transmitterDisconnectBox)
//            print("===>closed")
//        default:
//            break
//        }
//    }
    
//    if ((self.manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
//        self.manager.startScan()
//        self.debouncer.update(with: .disconnected)
//    }
    
    func sensorLostPower() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            // Will trigger `BluetoothManagerStatusDisconnected` due to forced disconnection
            self.manager.forceEndBleSensor()
        }
    }

    @objc func startScan() {
        print("--------------------------- Scanning inprocess")
        manager.startScan()
        //debouncer.update(with: .disconnected)
    }

    func endBleSensorCycle() {
        if let timer = countdownTimer {
            timer.invalidate()
            countdownTimer = nil
        }

        removeAllObservers()

        print("initial State all end")
       // KLTLaunchConfiguration.sharedManager.resetRootViewController(.beforeUse)
    }
}


struct KLTDateFormatter {
    static let shared = KLTDateFormatter()
    private let calendar = Calendar(identifier: .gregorian)
    private let locale = Locale(identifier: "en_GB")

    private func configuredFormatter(withFormat format: String, timeZone: TimeZone? = nil) -> DateFormatter {
        let formatter = DateFormatter()
        formatter.calendar = calendar
        formatter.locale = locale
        formatter.dateFormat = format
        if let tz = timeZone {
            formatter.timeZone = tz
        }
        return formatter
    }

    func getWholeString(from date: Date) -> String {
        return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss").string(from: date)
    }

    func getDayString(from date: Date) -> String {
        return configuredFormatter(withFormat: "MM-dd").string(from: date)
    }

    func getHourString(from date: Date) -> String {
        let hour = configuredFormatter(withFormat: "HH").string(from: date)
        return "\(hour):00"
    }

    func getMinuteString(from date: Date) -> String {
        return configuredFormatter(withFormat: "HH:mm").string(from: date)
    }

    func getMinuteDate(from string: String) -> Date? {
        return configuredFormatter(withFormat: "HH:mm").date(from: string)
    }

    func getHourDate(from string: String) -> Date? {
        return configuredFormatter(withFormat: "HH:00").date(from: string)
    }

    func getDayDate(from string: String) -> Date? {
        return configuredFormatter(withFormat: "MM-dd").date(from: string)
    }

    func getDateTime(from string: String) -> Date? {
        return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm").date(from: string)
    }

    func getDateFromWholeString(_ string: String) -> Date? {
        return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss").date(from: string)
    }

    func getUTCFormatDate(from localDate: String) -> String? {
        guard let date = getDateFromWholeString(localDate) else { return nil }
        return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone(abbreviation: "UTC")).string(from: date)
    }

    func getLocalFromUTC(_ utc: String) -> String? {
        let formatter = configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone.current)
        guard let date = formatter.date(from: utc) else { return nil }
        return getWholeString(from: date)
    }
}

extension String {
    func klt_contains(_ other: String) -> Bool {
        return self.range(of: other) != nil
    }

    func klt_hasValidTransmit() -> Bool {
        guard self.count >= 2 else { return false }
        let lastHex = String(self.suffix(2))
        return lastHex == "00"
    }

    func reverseToString(groupSize: Int) -> String {
        guard groupSize > 0 && self.count > groupSize else { return self }

        var result = ""
        var index = self.count

        while index >= groupSize {
            let start = self.index(self.startIndex, offsetBy: index - groupSize)
            let end = self.index(self.startIndex, offsetBy: index)
            result += self[start..<end]
            index -= groupSize
        }

        if index > 0 {
            let leftover = self.prefix(index)
            result += leftover
        }

        return result
    }

    static func getTimeString(with count: Int) -> String {
        let hours = String(format: "%02d", count / 3600)
        let minutes = String(format: "%02d", (count % 3600) / 60)
        let seconds = String(format: "%02d", count % 60)
        return "\(hours):\(minutes):\(seconds)"
    }
}

