//
//  ArpResolver.swift
//  sdlan
//  1. 通过ip地址查找mac地址
//  2. 要限制单位时间内，同一个ip的查询
//  Created by 安礼成 on 2025/7/14.
//
import Foundation
import Darwin

actor ArpResolver {
    // 增加缓存时间逻辑
    struct ArpEntry {
        var mac: Data
        var expireTime: TimeInterval
    }
    
    private var coolingDown: [UInt32: Date] = [:]
    
    private var known_macs: [UInt32: ArpEntry] = [:]
    private let arpTTL: TimeInterval
    nonisolated private let snapshotPublisher: SnapshotPublisher<ArpSnapshot>
    
    private var cleanupTask: Task<Void, Never>?
    
    init(arpTTL: TimeInterval = 300) {
        self.arpTTL = arpTTL
        self.snapshotPublisher = SnapshotPublisher(initial: ArpSnapshot.empty())
    }
    
    func start() {
        guard self.cleanupTask == nil else {
            return
        }
        
        self.cleanupTask = Task { [weak self] in
            while !Task.isCancelled {
                try? await Task.sleep(for: .seconds(1))
                await self?.cleanup()
            }
        }
    }
    
    func query(ip: UInt32) -> Data? {
        guard let entry = known_macs[ip] else {
            return nil
        }
        
        if entry.expireTime < Date().timeIntervalSince1970 {
            known_macs.removeValue(forKey: ip)
            self.publishSnapshot()
            return nil
        }
        
        return entry.mac
    }
    
    func append(ip: UInt32, mac: Data) {
        let expireAt = Date().timeIntervalSince1970 + arpTTL
        self.known_macs[ip] = ArpEntry(mac: mac, expireTime: expireAt)
        self.publishSnapshot()
    }
    
    func remove(ip: UInt32) {
        self.known_macs.removeValue(forKey: ip)
        self.publishSnapshot()
    }
    
    func dropMacs(macs: [Data]) {
        self.known_macs = self.known_macs.filter { !macs.contains($0.value.mac) }
        self.publishSnapshot()
    }
    
    func clear() {
        self.known_macs = [:]
        self.coolingDown = [:]
        self.publishSnapshot()
    }

    func stop() {
        self.cleanupTask?.cancel()
        self.cleanupTask = nil
        self.clear()
    }

    func makeArpRequest(targetIp: UInt32) throws -> Data? {
        guard self.coolingDown[targetIp] == nil else {
            return nil
        }

        self.coolingDown[targetIp] = Date().addingTimeInterval(3)

        var arpRequest = SDLArpRequest()
        arpRequest.targetIp = targetIp

        return try arpRequest.serializedData()
    }
    
    func handleArpResponse(arpResponse: SDLArpResponse) {
        let targetIp = arpResponse.targetIp
        let targetMac = arpResponse.targetMac
        if !targetMac.isEmpty {
            let expireAt = Date().timeIntervalSince1970 + arpTTL
            self.known_macs[targetIp] = ArpEntry(mac: targetMac, expireTime: expireAt)
            self.publishSnapshot()
        }
    }

    nonisolated func snapshot() -> ArpSnapshot {
        return self.snapshotPublisher.current()
    }
    
    private func cleanup() {
        let now = Date()
        self.coolingDown = self.coolingDown.filter { $0.value > now }
        let oldCount = self.known_macs.count
        self.known_macs = self.known_macs.filter { $0.value.expireTime >= now.timeIntervalSince1970 }
        if self.known_macs.count != oldCount {
            self.publishSnapshot()
        }
    }

    private func publishSnapshot() {
        self.snapshotPublisher.publish(self.compileSnapshot())
    }

    private func compileSnapshot() -> ArpSnapshot {
        let now = Date().timeIntervalSince1970
        let entries = self.known_macs.compactMapValues { entry in
            entry.expireTime >= now ? entry.mac : nil
        }
        return ArpSnapshot(entries: entries)
    }
    
    deinit {
        self.cleanupTask?.cancel()
    }
    
}
