//
//  PolicyRuntime.swift
//  Tun
//
//  Created by Codex on 2026/5/21.
//

import Foundation

struct PolicyRuntime: @unchecked Sendable {
    enum InboundDecision {
        case allow
        case deny
        case missingPolicy
    }

    private let policyRuleSnapshot: PolicyRuleSnapshot
    private let flowSessionTable: FlowSessionTable

    init(policyRuleSnapshot: PolicyRuleSnapshot, flowSessionTable: FlowSessionTable) {
        self.policyRuleSnapshot = policyRuleSnapshot
        self.flowSessionTable = flowSessionTable
    }

    func evaluateInbound(srcIdentityID: UInt32, ipPacket: IPPacketView) -> InboundDecision {
        if let reverseFlowSession = ipPacket.flowSession()?.reverse(),
           self.flowSessionTable.hasSession(reverseFlowSession) {
            self.flowSessionTable.updateSession(reverseFlowSession)
            return .allow
        }

        if case .icmp = ipPacket.transportPacket {
            return .allow
        }

        guard let ruleMap = self.policyRuleSnapshot.lookup(srcIdentityID) else {
            return .missingPolicy
        }

        return self.isAllowedByRule(ruleMap: ruleMap, ipPacket: ipPacket) ? .allow : .deny
    }

    private func isAllowedByRule(ruleMap: PolicyRuleMap, ipPacket: IPPacketView) -> Bool {
        let proto = ipPacket.header.proto

        switch ipPacket.transportPacket {
        case .tcp(_, let dstPort):
            return ruleMap.isAllow(proto: proto, port: dstPort)
        case .udp(_, let dstPort, _):
            return ruleMap.isAllow(proto: proto, port: dstPort)
        default:
            return false
        }
    }
}
