//
//  SDLNetworkMonitor.swift
//  Tun
//
//  Created by 安礼成 on 2024/5/16.
//

import Foundation
import Network
import Combine

// 监控网络的变化
class SDLNetworkMonitor: @unchecked Sendable {
    private var monitor: NWPathMonitor
    private var interfaceType: NWInterface.InterfaceType?
    private let publisher = PassthroughSubject<NWInterface.InterfaceType, Never>()
    private var cancel: AnyCancellable?
    private var isStopped = false
    
    public let eventStream: AsyncStream<MonitorEvent>
    private let eventContinuation: AsyncStream<MonitorEvent>.Continuation
    
    enum MonitorEvent {
        case changed
        case unreachable
    }
    
    init() {
        self.monitor = NWPathMonitor()
        (self.eventStream , self.eventContinuation) = AsyncStream.makeStream(of: MonitorEvent.self, bufferingPolicy: .unbounded)
    }
    
    func start() {
        self.monitor.pathUpdateHandler = {path in
            if path.status == .satisfied {
                if path.usesInterfaceType(.wifi) {
                    self.publisher.send(.wifi)
                } else if path.usesInterfaceType(.cellular) {
                    self.publisher.send(.cellular)
                } else if path.usesInterfaceType(.wiredEthernet) {
                    self.publisher.send(.wiredEthernet)
                }
            } else {
                self.eventContinuation.yield(.unreachable)
                self.interfaceType = nil
            }
        }
        self.monitor.start(queue: DispatchQueue.global())
        
        self.cancel = publisher.throttle(for: 5.0, scheduler: DispatchQueue.global(), latest: true)
            .sink { type in
                if self.interfaceType != nil && self.interfaceType != type {
                    self.eventContinuation.yield(.changed)
                }
                self.interfaceType = type
            }
    }
    
    func stop() {
        guard !self.isStopped else {
            return
        }
        
        self.isStopped = true
        self.monitor.cancel()
        self.cancel?.cancel()
        self.eventContinuation.finish()
    }
    
    deinit {
        self.stop()
    }
    
}
