//
//  SDLPuncherActor.swift
//  Tun
//
//  Created by 安礼成 on 2026/1/7.
//

import Foundation
import NIOCore

actor SDLPuncherActor {
    nonisolated private let cooldown: Duration = .seconds(5)
    
    // dstMac
    private var coolingDown: Set<Data> = []
    private var pktId: UInt32 = 1
    // 提交后还没有响应的请求
    private var pendingRequests: [UInt32: RegisterRequest] = [:]
    
    struct RegisterRequest {
        let srcMac: Data
        let dstMac: Data
        let networkId: UInt32
    }
    
    func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) {
        let dstMac = request.dstMac
        guard let quicClient, !coolingDown.contains(dstMac) else {
            return
        }
        
        // 触发一次打洞
        coolingDown.insert(dstMac)
        
        let pktId = self.nextPacketId()
        self.tryHole(using: quicClient, pktId: pktId, request: request)
        
        Task {
            // 启动冷却期
            try? await Task.sleep(for: .seconds(5))
            self.endCooldown(for: dstMac)
            self.removePendingRequest(for: pktId)
        }
    }
    
    func handlePeerInfo(using udpHole: SDLUDPHole, peerInfo: SDLPeerInfo) async {
        guard let request = pendingRequests.removeValue(forKey: peerInfo.pktID),
              let remoteAddress = try? await peerInfo.v4Info.socketAddress() else {
            return
        }
        
        SDLLogger.shared.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug)
        // 发送register包
        var register = SDLRegister()
        register.networkID = request.networkId
        register.srcMac = request.srcMac
        register.dstMac = request.dstMac
        
        if let registerData = try? register.serializedData() {
            udpHole.send(type: .register, data: registerData, remoteAddress: remoteAddress)
        }
    }
    
    private func endCooldown(for key: Data) {
        self.coolingDown.remove(key)
    }
    
    private func removePendingRequest(for pktId: UInt32) {
        self.pendingRequests.removeValue(forKey: pktId)
    }
    
    private func tryHole(using quicClient: SDLQUICClient, pktId: UInt32, request: RegisterRequest) {
        var queryInfo = SDLQueryInfo()
        queryInfo.pktID = pktId
        queryInfo.dstMac = request.dstMac
        self.pendingRequests[pktId] = request
        
        if let queryData = try? queryInfo.serializedData() {
            quicClient.send(type: .queryInfo, data: queryData)
        }
    }
    
    private func nextPacketId() -> UInt32 {
        let pktId = self.pktId
        self.pktId &+= 1
        if self.pktId == 0 {
            self.pktId = 1
        }
        return pktId
    }
    
}
