//
//  SDLUDPHoleHandler.swift
//  punchnet
//  上游的SDLUDPHole是基于actor实现的，串行实现的逻辑已经保证了SDLUDPHoleHandler的内部的一致性
//  Created by 安礼成 on 2026/5/22.
//
import Foundation
import NIOCore
import NIOPosix

// 处理和sn-server服务器之间的通讯
final class SDLUDPHoleHandler: ChannelInboundHandler {
    typealias InboundIn = AddressedEnvelope<ByteBuffer>
    
    struct SDLHoleDatagram {
        let remoteAddress: SocketAddress
        let message: SDLHoleMessage
    }
    
    private let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
    private var channel: Channel?
    
    public let messageStream: AsyncThrowingStream<SDLHoleDatagram, Error>
    private let messageContinuation: AsyncThrowingStream<SDLHoleDatagram, Error>.Continuation
    
    // 启动函数
    init() throws {
        let (stream, continuation) = AsyncThrowingStream.makeStream(of: SDLHoleDatagram.self, bufferingPolicy: .bufferingNewest(2048))
        self.messageStream = stream
        self.messageContinuation = continuation
    }
    
    func start() throws -> SocketAddress {
        let bootstrap = DatagramBootstrap(group: group)
            .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
            .channelInitializer { channel in
                channel.pipeline.addHandler(self)
            }
        
        // 绑定到IPv4通配地址，只处理IPv4流量
        let channel = try bootstrap.bind(host: "0.0.0.0", port: 0).wait()
        guard let localAddress = channel.localAddress else {
            throw SDLUDPHoleError.invalidLocalAddress
        }
        
        self.channel = channel
        
        return localAddress
    }
    
    // --MARK: ChannelInboundHandler delegate
    
    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        let envelope = unwrapInboundIn(data)
        var buffer = envelope.data
        let remoteAddress = envelope.remoteAddress
        
        do {
            if let message = try SDLHoleMessage.decode(buffer: &buffer) {
                self.messageContinuation.yield(SDLHoleDatagram(remoteAddress: remoteAddress, message: message))
            } else {
                SDLLogger.log("[SDLUDPHole] decode message, get null", for: .debug)
            }
        } catch let err {
            SDLLogger.log("[SDLUDPHole] decode message, get error: \(err)", for: .debug)
            self.messageContinuation.finish(throwing: err)
        }
    }
    
    func channelInactive(context: ChannelHandlerContext) {
        self.messageContinuation.finish(throwing: SDLUDPHoleError.closed)
    }
    
    func errorCaught(context: ChannelHandlerContext, error: any Error) {
        context.close(promise: nil)
        self.messageContinuation.finish(throwing: SDLUDPHoleError.errorCaught)
    }
    
    // MARK: 处理写入逻辑
    func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) {
        guard let channel = self.channel else {
            return
        }
        
        var buffer = channel.allocator.buffer(capacity: data.count + 1)
        buffer.writeBytes([type.rawValue])
        buffer.writeBytes(data)
        
        let envelope = AddressedEnvelope<ByteBuffer>(remoteAddress: remoteAddress, data: buffer)
        
        let promise = channel.eventLoop.makePromise(of: Void.self)
        channel.eventLoop.execute {
            channel.writeAndFlush(envelope, promise: promise)
        }
        
        promise.futureResult.whenFailure { [weak self] err in
            self?.messageContinuation.finish(throwing: SDLUDPHoleError.sendFaied(err))
        }
    }
        
    func stop() {
        self.messageContinuation.finish()
        let channel = self.channel
        self.channel = nil
        try? channel?.close().wait()
        try? self.group.syncShutdownGracefully()
        
        SDLLogger.log("[SDLUDPHole] stopped", for: .debug)
    }
    
    deinit {
        SDLLogger.log("[SDLUDPHole] deinit", for: .debug)
    }
    
}
