import BitFoundation
import Foundation
import Testing

@testable import bitchat

struct PacketsTests {
    @Test
    func announcementPacketRoundTripsNeighborsAndSkipsUnknownTLVs() throws {
        let neighbors = (0..<12).map { index in
            Data(repeating: UInt8(index), count: 8)
        }
        let packet = AnnouncementPacket(
            nickname: "alice",
            noisePublicKey: Data(repeating: 0x11, count: 32),
            signingPublicKey: Data(repeating: 0x22, count: 32),
            directNeighbors: neighbors
        )

        var encoded = try #require(packet.encode())
        encoded.append(makeTLV(type: 0xFF, value: Data([0xAB])))

        let decoded = try #require(AnnouncementPacket.decode(from: encoded))
        #expect(decoded.nickname == "alice")
        #expect(decoded.noisePublicKey == Data(repeating: 0x11, count: 32))
        #expect(decoded.signingPublicKey == Data(repeating: 0x22, count: 32))
        #expect(decoded.directNeighbors?.count == 10)
        #expect(decoded.directNeighbors?.first == neighbors.first)
        #expect(decoded.directNeighbors?.last == neighbors[9])
    }

    @Test
    func announcementPacketEncodeRejectsOversizedFieldsAndInvalidNeighborGroups() {
        let oversizedNickname = String(repeating: "a", count: 256)
        let validKey = Data(repeating: 0x44, count: 32)

        #expect(
            AnnouncementPacket(
                nickname: oversizedNickname,
                noisePublicKey: validKey,
                signingPublicKey: validKey,
                directNeighbors: nil
            ).encode() == nil
        )

        #expect(
            AnnouncementPacket(
                nickname: "alice",
                noisePublicKey: Data(repeating: 0x55, count: 256),
                signingPublicKey: validKey,
                directNeighbors: nil
            ).encode() == nil
        )

        #expect(
            AnnouncementPacket(
                nickname: "alice",
                noisePublicKey: validKey,
                signingPublicKey: Data(repeating: 0x66, count: 256),
                directNeighbors: nil
            ).encode() == nil
        )

        let invalidNeighborPacket = AnnouncementPacket(
            nickname: "alice",
            noisePublicKey: validKey,
            signingPublicKey: validKey,
            directNeighbors: [Data([0x01, 0x02, 0x03])]
        )
        let encodedWithoutNeighbors = AnnouncementPacket(
            nickname: "alice",
            noisePublicKey: validKey,
            signingPublicKey: validKey,
            directNeighbors: nil
        ).encode()
        #expect(invalidNeighborPacket.encode() == encodedWithoutNeighbors)
    }

    @Test
    func announcementPacketDecodeRejectsMissingFieldsAndTruncation() throws {
        let missingSigningKey = makeTLV(type: 0x01, value: Data("alice".utf8))
            + makeTLV(type: 0x02, value: Data(repeating: 0x11, count: 32))
        #expect(AnnouncementPacket.decode(from: missingSigningKey) == nil)

        let validPacket = try #require(
            AnnouncementPacket(
                nickname: "alice",
                noisePublicKey: Data(repeating: 0x11, count: 32),
                signingPublicKey: Data(repeating: 0x22, count: 32),
                directNeighbors: nil
            ).encode()
        )
        #expect(AnnouncementPacket.decode(from: validPacket.dropLast()) == nil)
    }

    @Test
    func announcementPacketDecodeIgnoresInvalidNeighborLengths() throws {
        var encoded = try #require(
            AnnouncementPacket(
                nickname: "alice",
                noisePublicKey: Data(repeating: 0x11, count: 32),
                signingPublicKey: Data(repeating: 0x22, count: 32),
                directNeighbors: nil
            ).encode()
        )
        encoded.append(makeTLV(type: 0x04, value: Data(repeating: 0x99, count: 7)))

        let decoded = try #require(AnnouncementPacket.decode(from: encoded))
        #expect(decoded.directNeighbors == nil)
    }

    @Test
    func announcementPacketRoundTripsCapabilities() throws {
        let capabilities: PeerCapabilities = [.prekeys, .board, .meshDiagnostics]
        let packet = AnnouncementPacket(
            nickname: "alice",
            noisePublicKey: Data(repeating: 0x11, count: 32),
            signingPublicKey: Data(repeating: 0x22, count: 32),
            directNeighbors: nil,
            capabilities: capabilities
        )

        let encoded = try #require(packet.encode())
        let decoded = try #require(AnnouncementPacket.decode(from: encoded))
        #expect(decoded.capabilities == capabilities)
    }

    @Test
    func announcementPacketWithoutCapabilitiesDecodesNilAndUnknownBitsSurvive() throws {
        let legacy = try #require(
            AnnouncementPacket(
                nickname: "alice",
                noisePublicKey: Data(repeating: 0x11, count: 32),
                signingPublicKey: Data(repeating: 0x22, count: 32),
                directNeighbors: nil
            ).encode()
        )
        // The TLV is emitted only when capabilities are set, so legacy peers
        // (and this packet) decode as nil rather than empty.
        #expect(try #require(AnnouncementPacket.decode(from: legacy)).capabilities == nil)

        var withFutureBits = legacy
        withFutureBits.append(makeTLV(type: 0x05, value: Data([0x80, 0x01])))
        let decoded = try #require(AnnouncementPacket.decode(from: withFutureBits))
        #expect(decoded.capabilities?.rawValue == 0x0180)
    }

    @Test
    func authenticatedPeerStateUsesVersionedCanonicalTLVs() throws {
        let signingKey = Data(repeating: 0xA5, count: 32)
        let packet = AuthenticatedPeerStatePacket(
            capabilities: [.privateMedia, .vouch],
            signingPublicKey: signingKey
        )

        var encoded = try #require(packet.encode())
        #expect(encoded.prefix(5) == Data([0x01, 0x01, 0x02, 0x20, 0x01]))
        // Unknown TLVs are forward-compatible and do not alter v1 state.
        encoded.append(makeTLV(type: 0x7F, value: Data([0xCA, 0xFE])))

        #expect(AuthenticatedPeerStatePacket.decode(from: encoded) == packet)
    }

    @Test
    func authenticatedPeerStateRejectsMalformedAmbiguousOrUnknownVersion() {
        let key = Data(repeating: 0x44, count: 32)
        let capabilities = makeTLV(type: 0x01, value: Data([0x00, 0x01]))
        let signing = makeTLV(type: 0x02, value: key)

        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x02]) + capabilities + signing) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + signing) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + capabilities + signing) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01, 0x01, 0x00]) + signing) == nil)
        // 0x0001 is non-minimal little endian; the canonical form is [0x01].
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + makeTLV(type: 0x01, value: Data([0x01, 0x00])) + signing) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + makeTLV(type: 0x02, value: Data(key.dropLast()))) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + Data(signing.dropLast())) == nil)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + makeTLV(type: 0x01, value: Data(repeating: 0x01, count: 9)) + signing) == nil)
    }

    @Test
    func privateMessagePacketRejectsUnknownTypeAndTruncation() {
        let unknownTLV = Data([0x7F, 0x01, 0x41])
        #expect(PrivateMessagePacket.decode(from: unknownTLV) == nil)

        let truncated = Data([0x00, 0x05, 0x61])
        #expect(PrivateMessagePacket.decode(from: truncated) == nil)
    }

    private func makeTLV(type: UInt8, value: Data) -> Data {
        var data = Data([type, UInt8(value.count)])
        data.append(value)
        return data
    }
}
