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

import Foundation
import NIOCore

actor SDLPuncherActor {
    nonisolated private let cooldownInterval: TimeInterval = 5
    
    // dstMac
    private var coolingDown: [Data: (UInt32, Date)] = [:]
    private var pktId: UInt32 = 1
    // 提交后还没有响应的请求
    private var pendingRequests: [UInt32: RegisterRequest] = [:]
    private var cleanupTask: Task<Void, Never>?
    
    struct RegisterRequest {
        let srcMac: Data
        let dstMac: Data
        let networkId: UInt32
    }
    
    // 启动定时清理任务
    func start() {
        self.cleanupTask?.cancel()
        
        self.cleanupTask = Task {
            while !Task.isCancelled {
                try? await Task.sleep(for: .seconds(1))
                self.cleanExpiredCoolingDown()
            }
        }
    }
    
    func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) {
        let dstMac = request.dstMac
        guard let quicClient, coolingDown[dstMac] == nil else {
            return
        }
        
        let pktId = self.nextPacketId()
        coolingDown[dstMac] = (pktId, Date().addingTimeInterval(cooldownInterval))

        // 触发一次打洞
        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)
        }
    }
    
    func handlePeerInfo(using udpHole: SDLUDPHole?, peerInfo: SDLPeerInfo) async {
        guard let udpHole, 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 nextPacketId() -> UInt32 {
        let pktId = self.pktId
        self.pktId &+= 1
        if self.pktId == 0 {
            self.pktId = 1
        }
        return pktId
    }
    
    private func cleanExpiredCoolingDown() {
        let date = Date()
        for (key, (pktId, expireAt)) in coolingDown {
            if expireAt < date {
                self.coolingDown.removeValue(forKey: key)
                self.pendingRequests.removeValue(forKey: pktId)
            }
        }
    }
    
    deinit {
        self.cleanupTask?.cancel()
        self.cleanupTask = nil
    }
    
}
