//
//  NSESubscriber.swift
//  SimpleXChat
//
//  Created by Evgeny on 09/12/2023.
//  Copyright © 2023 SimpleX Chat. All rights reserved.
//

import Foundation
import SimpleXChat

private var nseSubscribers: [UUID:NSESubscriber] = [:]

// timeout for active notification service extension going into "suspending" state.
// If in two seconds the state does not change, we assume that it was not running and proceed with app activation/answering call.
private let SUSPENDING_TIMEOUT: TimeInterval = 2

// timeout should be larger than SUSPENDING_TIMEOUT
func waitNSESuspended(timeout: TimeInterval, suspended: @escaping (Bool) -> Void) {
    if timeout <= SUSPENDING_TIMEOUT {
        logger.warning("waitNSESuspended: small timeout \(timeout), using \(SUSPENDING_TIMEOUT + 1)")
    }
    var state = nseStateGroupDefault.get()
    if case .suspended = state {
        DispatchQueue.main.async { suspended(true) }
        return
    }
    let id = UUID()
    var suspendedCalled = false
    checkTimeout()
    nseSubscribers[id] = nseMessageSubscriber { msg in
        if case let .state(newState) = msg {
            state = newState
            logger.debug("waitNSESuspended state: \(state.rawValue)")
            if case .suspended = newState {
                notifySuspended(true)
            }
        }
    }
    return

    func notifySuspended(_ ok: Bool) {
        logger.debug("waitNSESuspended notifySuspended: \(ok)")
        if !suspendedCalled {
            logger.debug("waitNSESuspended notifySuspended: calling suspended(\(ok))")
            suspendedCalled = true
            nseSubscribers.removeValue(forKey: id)
            DispatchQueue.main.async { suspended(ok) }
        }
    }

    func checkTimeout() {
        if !suspending() {
            checkSuspendingTimeout()
        } else if state == .suspending {
            checkSuspendedTimeout()
        }
    }

    func suspending() -> Bool {
        suspendedCalled || state == .suspended || state == .suspending
    }

    func checkSuspendingTimeout() {
        DispatchQueue.global().asyncAfter(deadline: .now() + SUSPENDING_TIMEOUT) {
            logger.debug("waitNSESuspended check suspending timeout")
            if !suspending() {
                notifySuspended(false)
            } else if state != .suspended {
                checkSuspendedTimeout()
            }
        }
    }

    func checkSuspendedTimeout() {
        DispatchQueue.global().asyncAfter(deadline: .now() + min(timeout - SUSPENDING_TIMEOUT, 1)) {
            logger.debug("waitNSESuspended check suspended timeout")
            if state != .suspended {
                notifySuspended(false)
            }
        }
    }
}
