//
//  RefreshableMoleculeBehavior.swift
//  MVMCoreUI
//
//  Created by Kyle Hedden on 9/12/23.
//  Copyright © 2023 Verizon Wireless. All rights reserved.
//

import Foundation
import MVMCore

public class PollingBehaviorModel: PageBehaviorModelProtocol {
    public class var identifier: String { "pollingBehavior" }
    public var shouldAllowMultipleInstances: Bool { true }
    
    public let refreshInterval: TimeInterval
    public let refreshAction: ActionModelProtocol
    
    public var runWhileHidden: Bool
    public var refreshOnFirstLoad: Bool
    public var refreshOnShown: Bool
    
    private enum CodingKeys: String, CodingKey {
        case refreshInterval
        case refreshAction
        case runWhileHidden
        case refreshOnFirstLoad
        case refreshOnShown
    }
    
    required public init(from decoder: Decoder) throws {
        let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
        refreshInterval = try typeContainer.decode(TimeInterval.self, forKey: .refreshInterval)
        refreshAction = try typeContainer.decodeModel(codingKey: .refreshAction)
        runWhileHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .runWhileHidden) ?? false
        refreshOnFirstLoad = try typeContainer.decodeIfPresent(Bool.self, forKey: .refreshOnFirstLoad) ?? false
        refreshOnShown = try typeContainer.decodeIfPresent(Bool.self, forKey: .refreshOnShown) ?? false
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(refreshInterval, forKey: .refreshInterval)
        try container.encode(refreshAction, forKey: .refreshAction)
        try container.encode(runWhileHidden, forKey: .runWhileHidden)
        try container.encode(refreshOnFirstLoad, forKey: .refreshOnFirstLoad)
        try container.encode(refreshOnShown, forKey: .refreshOnShown)
    }
}

public class PollingBehavior: NSObject, PageVisibilityBehavior {
    
    var model: PollingBehaviorModel
    var delegateObject: MVMCoreUIDelegateObject?
    var lastRefresh = Date.distantPast
    var pollTimer: DispatchSourceTimer?
    
    var remainingTimeToRefresh: TimeInterval {
        lastRefresh.timeIntervalSinceNow - model.refreshInterval
    }
    
    var firstTimeLoad = true
    
    var refreshOnShown: Bool {
        if model.refreshOnFirstLoad && firstTimeLoad {
            return true
        }
        firstTimeLoad = false
        return model.refreshOnShown
    }
    
    public required init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
        self.model = model as! PollingBehaviorModel
        self.delegateObject = delegateObject
    }

    public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
        resumePollingTimer(withRemainingTime: refreshOnShown ? 0 : remainingTimeToRefresh, refreshAction: model.refreshAction, interval: model.refreshInterval)
    }
    
    public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) {
        pollTimer?.cancel()
    }
    
    func resumePollingTimer(withRemainingTime timeRemaining: TimeInterval, refreshAction: ActionModelProtocol, interval: TimeInterval) {
        let delegateObject = delegateObject
        pollTimer?.cancel()
        pollTimer = DispatchSource.makeTimerSource()
        pollTimer?.schedule(deadline: .now() + timeRemaining, repeating: interval)
        pollTimer?.setEventHandler(qos:.utility) {
            Task {
                if let delegateActionHandler = delegateObject?.actionDelegate as? ActionDelegateProtocol {
                    try? await delegateActionHandler.performAction(with: refreshAction, additionalData: nil, delegateObject: delegateObject)
                } else {
                    try? await MVMCoreActionHandler.shared()?.handleAction(with: refreshAction, additionalData: nil, delegateObject: delegateObject)
                }
            }
        }
        pollTimer?.resume()
    }
    
    deinit {
        pollTimer?.cancel()
    }
}
