import Foundation
import Testing
import BitFoundation
@testable import bitchat

struct BLENoisePayloadFactoryTests {
    @Test
    func privateMessagePayloadPrefixesTLVWithNoiseType() throws {
        let payload = try #require(BLENoisePayloadFactory.privateMessage(content: "secret", messageID: "pm-1"))

        #expect(payload.first == NoisePayloadType.privateMessage.rawValue)

        let packet = try #require(PrivateMessagePacket.decode(from: Data(payload.dropFirst())))
        #expect(packet.messageID == "pm-1")
        #expect(packet.content == "secret")
    }

    @Test
    func receiptPayloadsUseMessageIDBytes() {
        let read = BLENoisePayloadFactory.readReceipt(originalMessageID: "read-id")
        let delivered = BLENoisePayloadFactory.delivered(messageID: "delivered-id")

        #expect(read.first == NoisePayloadType.readReceipt.rawValue)
        #expect(String(data: Data(read.dropFirst()), encoding: .utf8) == "read-id")
        #expect(delivered.first == NoisePayloadType.delivered.rawValue)
        #expect(String(data: Data(delivered.dropFirst()), encoding: .utf8) == "delivered-id")
    }

    @Test
    func typedPayloadKeepsOpaqueDataUnchanged() {
        let payload = BLENoisePayloadFactory.typedPayload(.verifyChallenge, payload: Data([0xCA, 0xFE]))

        #expect(payload == Data([NoisePayloadType.verifyChallenge.rawValue, 0xCA, 0xFE]))
    }

    @Test
    func privateFilePayloadPrefixesCanonicalFilePacket() throws {
        let content = Data("%PDF-secret".utf8)
        let file = BitchatFilePacket(
            fileName: "secret.pdf",
            fileSize: UInt64(content.count),
            mimeType: "application/pdf",
            content: content
        )

        let payload = try #require(BLENoisePayloadFactory.privateFile(file))

        #expect(payload.first == 0x20, "Encrypted files must use Android's deployed wire value")
        let decoded = try #require(BitchatFilePacket.decode(Data(payload.dropFirst())))
        #expect(decoded.fileName == "secret.pdf")
        #expect(decoded.mimeType == "application/pdf")
        #expect(decoded.content == content)
    }

    @Test
    func androidB7f0b33PrivateFilePlaintextFixtureIsByteCompatible() throws {
        // Runtime-emitted by Android commit b7f0b33d from
        // BitchatFilePacket("a.txt", 3, "text/plain", [01, 02, 03]) and
        // NoisePayload(type = FILE_TRANSFER, data = file.encode()).encode().
        let fixtureHex = "20010005612e7478740200040000000303000a746578742f706c61696e0400000003010203"
        let fixture = try #require(Data(hexString: fixtureHex))

        let typed = try #require(NoisePayload.decode(fixture))
        #expect(typed.type == .privateFile)
        let file = try #require(BitchatFilePacket.decode(typed.data))
        #expect(file.fileName == "a.txt")
        #expect(file.fileSize == 3)
        #expect(file.mimeType == "text/plain")
        #expect(file.content == Data([0x01, 0x02, 0x03]))
        #expect(BLENoisePayloadFactory.privateFile(file) == fixture)
    }

    @Test
    func prereleasePrivateFileTypeCanonicalizesOnDecode() throws {
        let encoded = Data([NoisePayloadType.prereleasePrivateFileRawValue, 0xCA, 0xFE])
        let decoded = try #require(NoisePayload.decode(encoded))

        #expect(decoded.type == .privateFile)
        #expect(decoded.data == Data([0xCA, 0xFE]))
        #expect(decoded.encode().first == 0x20)
    }

    @Test
    func authenticatedPeerStateUsesPermanent0x21Type() throws {
        let state = AuthenticatedPeerStatePacket(
            capabilities: .privateMedia,
            signingPublicKey: Data(repeating: 0x77, count: 32)
        )
        let encoded = try #require(BLENoisePayloadFactory.authenticatedPeerState(state))

        #expect(encoded.first == 0x21)
        #expect(AuthenticatedPeerStatePacket.decode(from: Data(encoded.dropFirst())) == state)
    }
}
