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

import Foundation
import NIOCore

actor SDLPuncherActor {
    // 10秒内只需要提交一次查询
    nonisolated private let cooldownInterval: TimeInterval = 10
    // 等待peerInfo返回的超时时间
    nonisolated private let peerInfoTimeout: TimeInterval = 3

    struct RegisterRequest {
        let srcMac: Data
        let dstMac: Data
        let networkId: UInt32
    }
    
    private enum RequestPhase {
        case waitingPeerInfo(deadline: Date)
        case coolingDown
    }
    
    private struct RequestEntry {
        let request: RegisterRequest
        let cooldownUntil: Date
        var phase: RequestPhase
        
        func canSubmit(at now: Date) -> Bool {
            return cooldownUntil <= now
        }
        
        func isWaitingPeerInfo(at now: Date) -> Bool {
            guard case .waitingPeerInfo(let deadline) = self.phase else {
                return false
            }
            
            return deadline > now
        }
        
        mutating func markCoolingDown() {
            self.phase = .coolingDown
        }
    }
    
    // dstMac
    private var requestEntries: [Data: RequestEntry] = [:]
    private var cleanupTask: Task<Void, Never>?
    
    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?.cleanupExpiredEntries()
            }
        }
    }
    
    func makeQueryInfoRequest(request: RegisterRequest) async -> Data? {
        let now = Date()
        self.cleanupExpiredEntries(now: now)

        if let entry = self.requestEntries[request.dstMac], !entry.canSubmit(at: now) {
            return nil
        }

        var queryInfo = SDLQueryInfo()
        queryInfo.dstMac = request.dstMac

        guard let queryData = try? queryInfo.serializedData() else {
            SDLLogger.log("[SDLPuncherActor] failed to encode queryInfo", for: .debug)
            return nil
        }

        self.requestEntries[request.dstMac] = RequestEntry(
            request: request,
            cooldownUntil: now.addingTimeInterval(self.cooldownInterval),
            phase: .waitingPeerInfo(deadline: now.addingTimeInterval(self.peerInfoTimeout))
        )

        return queryData
    }

    func makeRegisterPackets(peerInfo: SDLPeerInfo) async -> [(data: Data, remoteAddress: SocketAddress)] {
        let now = Date()
        self.cleanupExpiredEntries(now: now)

        guard var entry = self.requestEntries[peerInfo.dstMac], entry.isWaitingPeerInfo(at: now) else {
            return []
        }

        entry.markCoolingDown()
        self.requestEntries[peerInfo.dstMac] = entry

        var register = SDLRegister()
        register.networkID = entry.request.networkId
        register.srcMac = entry.request.srcMac
        register.dstMac = entry.request.dstMac

        guard let registerData = try? register.serializedData() else {
            SDLLogger.log("[SDLPuncherActor] failed to encode register", for: .debug)
            return []
        }

        var packets: [(data: Data, remoteAddress: SocketAddress)] = []
        if peerInfo.hasV4Info, let remoteAddress = try? await peerInfo.v4Info.socketAddress() {
            packets.append((data: registerData, remoteAddress: remoteAddress))
        }
        if peerInfo.hasV6Info, let remoteAddress = try? await peerInfo.v6Info.socketAddress() {
            packets.append((data: registerData, remoteAddress: remoteAddress))
        }

        return packets
    }
    
    func stop() {
        self.cleanupTask?.cancel()
        self.cleanupTask = nil
        self.requestEntries.removeAll()
    }
    
    private func cleanupExpiredEntries(now: Date = Date()) {
        self.requestEntries = self.requestEntries.filter { _, entry in
            !entry.canSubmit(at: now)
        }
    }
    
    deinit {
        self.cleanupTask?.cancel()
    }
}
