//
//  PacketOutboundRouter.swift
//  Tun
//
//  Created by 安礼成 on 2026/4/14.
//

import Foundation

struct PacketOutboundRouter {
    enum DropReason: String {
        case invalidDNSRequest
        case noRoute
    }
    
    enum ForwardKind {
        case sameNetwork
        case exitNode
        case dnsExitNode
    }
    
    enum RouteDecision {
        case loopback(ipPacketData: Data)
        case cloudDNS(name: String, ipPacketData: Data)
        case localDNS(name: String, payload: Data, tracker: DNSLocalClient.DNSTracker)
        case forwardToNextHop(ip: UInt32, type: LayerPacket.PacketType, data: Data, kind: ForwardKind)
        case drop(reason: DropReason)
    }
    
    let networkAddress: SDLConfiguration.NetworkAddress
    let exitNode: SDLConfiguration.ExitNode?
    
    func route(packet: IPPacketView, now: Date = Date()) -> RouteDecision {
        let dstIp = packet.header.destination
        
        // 本地通讯, 目标地址是本地服务器的ip地址
        if dstIp == self.networkAddress.ip {
            return .loopback(ipPacketData: packet.data)
        }
        
        // 处理dns的解析
        if let dnsDecision = self.routeDNS(packet: packet, now: now) {
            return dnsDecision
        }
        
        // 判断目标地址是否和当前的网络地址是否在同一个网段
        // 只有在同一个网段的ip数据才直接发送
        if SDLUtil.inSameNetwork(ip: dstIp, compareIp: self.networkAddress.ip, maskLen: self.networkAddress.maskLen) {
            return .forwardToNextHop(ip: dstIp, type: .ipv4, data: packet.data, kind: .sameNetwork)
        }
        
        // 不在同一个网段的数据，看是否配置了网络出口, 如果配置了，转发数据个网络出口，否则丢弃
        if let exitNode = self.exitNode {
            return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .exitNode)
        }
        
        return .drop(reason: .noRoute)
    }
    
    private func routeDNS(packet: IPPacketView, now: Date) -> RouteDecision? {
        guard DNSHelper.isDnsRequestPacket(ipPacket: packet) else {
            return nil
        }
        
        guard case .udp(let srcPort, _, let payloadOffset) = packet.transportPacket else {
            return .drop(reason: .invalidDNSRequest)
        }
        
        guard let query = DNSParser.parseFirstQuestion(data: packet.data, offset: payloadOffset) else {
            return .drop(reason: .invalidDNSRequest)
        }
        
        // 如果是内部域名，则转发整个ip包的内容到云端服务器
        if query.name.contains(self.networkAddress.networkDomain) {
            return .cloudDNS(name: query.name, ipPacketData: packet.data)
        }
        
        // 如果开启了出口节点，则转发给出口节点
        if let exitNode = self.exitNode {
            return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .dnsExitNode)
        }
        
        // 通过本地的dns解析，发送的是udp的payload部分
        let dnsPayload = Data(packet.data[payloadOffset..<packet.data.count])
        let tracker = DNSLocalClient.DNSTracker(
            transactionID: query.transactionID,
            clientIP: packet.header.source,
            clientPort: srcPort,
            createdAt: now
        )
        return .localDNS(name: query.name, payload: dnsPayload, tracker: tracker)
    }
}

extension PacketOutboundRouter.RouteDecision {
    
    var shouldTrackFlow: Bool {
        switch self {
        case .forwardToNextHop(_, _, _, let kind):
            switch kind {
            case .sameNetwork, .exitNode, .dnsExitNode:
                return true
            }
        default:
            return false
        }
    }
    
}
