//
//  HMG_Geofence.swift
//  Runner
//
//  Created by ZiKambrani on 13/12/2020.
//

import UIKit
import CoreLocation

fileprivate var df = DateFormatter()
fileprivate var transition = ""

enum Transition:Int {
    case entry = 1
    case exit = 2
    func name() -> String{
        return self.rawValue == 1 ? "Enter" : "Exit"
    }
}

class HMG_Geofence:NSObject{
    
    var geoZones:[GeoZoneModel]?
    var locationManager:CLLocationManager!{
        didSet{
            // https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
            
            locationManager.allowsBackgroundLocationUpdates = true
            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.activityType = .other
            locationManager.delegate = self
            locationManager.requestAlwaysAuthorization()
            //        locationManager.distanceFilter = 500
            //        locationManager.startMonitoringSignificantLocationChanges()
        }
    }
    
    private static var shared_:HMG_Geofence?
    class func shared() -> HMG_Geofence{
        if HMG_Geofence.shared_ == nil{
            HMG_Geofence.initGeofencing()
        }
        return shared_!
    }
    
    class func initGeofencing(){
        shared_ = HMG_Geofence()
        shared_?.locationManager = CLLocationManager()
    }
    
    func register(geoZones:[GeoZoneModel]){
        
        self.geoZones  = geoZones
        
        let monitoredRegions_ =  monitoredRegions()
        self.geoZones?.forEach({ (zone) in
            if let region = zone.toRegion(locationManager: locationManager){
                if let already =  monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){
                    debugPrint("Already monitering region: \(already)")
                }else{
                    startMonitoring(region: region)
                }
            }else{
                debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())")
            }
        })
    }
    
    func monitoredRegions() -> Set<CLRegion>{
        return locationManager.monitoredRegions
    }
    
    func unRegisterAll(){
        for region in locationManager.monitoredRegions {
            locationManager.stopMonitoring(for: region)
        }
    }
    
}

// CLLocationManager Delegates
extension HMG_Geofence : CLLocationManagerDelegate{
    
    func startMonitoring(region: CLCircularRegion) {
        if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
            return
        }
        
        if CLLocationManager.authorizationStatus() != .authorizedAlways {
            let message = """
        Your geotification is saved but will only be activated once you grant
        HMG permission to access the device location.
        """
            debugPrint(message)
        }
        
        locationManager.startMonitoring(for: region)
        locationManager.requestState(for: region)
        debugPrint("Starts monitering region: \(region)")
    }
    
    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        debugPrint("didEnterRegion: \(region)")
        if region is CLCircularRegion {
            handleEvent(for: region,transition: .entry, location: manager.location)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        debugPrint("didExitRegion: \(region)")
        if region is CLCircularRegion {
            handleEvent(for: region,transition: .exit, location: manager.location)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        debugPrint("didDetermineState: \(state)")
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        debugPrint("didUpdateLocations: \(locations)")
    }
    
    
}

// Helpers
extension HMG_Geofence{
    
    func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) {
        if let userProfile = userProfile(){
            notifyUser(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
            notifyServer(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
        }
    }
    
    func geoZone(by id: String) -> GeoZoneModel? {
        var zone:GeoZoneModel? = nil
        if let zones_ = geoZones{
            zone =  zones_.first(where: { $0.identifier() == id})
        }else{
            //            let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences")
        }
        return zone
    }
    
    
    func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
        if let patientId = userProfile["PatientID"] as? Int{
            
        }
    }
    
    func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
        if let patientId = userProfile["PatientID"] as? Int{
            
            if let idString = forRegion.identifier.split(separator: "_").first,  let idInt = Int(idString){
                let body:[String:Any] = [
                    "PointsID":idInt,
                    "GeoType":transition.rawValue,
                    "PatientID":patientId
                ]

                var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "GEOFENCE_LOGS") ?? [:]
                var geo = (logs[forRegion.identifier] as? [String]) ?? []
                
                let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo"
                httpPostRequest(urlString: url, jsonBody: body){ (status,json) in
                    let status_ = status ?  "Notified successfully:" : "Failed to notify:"
                    showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_)
                    
                    
                    geo.append("\(status_) \(transition.name()) at \(Date().toString(format: "dd/MMM/yyy hh:mm:ss"))")
                    logs.updateValue( geo, forKey: forRegion.identifier)
                    
                    UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "GEOFENCE_LOGS")
                }
            }
        }
    }
}

