import Testing
@testable import Rosetta

/// Verifies PacketPushNotification wire format matches server
/// (im.rosetta.packet.Packet16PushNotification).
///
/// Server wire format:
///   writeInt16(packetId=0x10)
///   writeString(notificationToken)
///   writeInt8(action)          — 0=subscribe, 1=unsubscribe
///   writeInt8(tokenType)       — 0=FCM, 1=VoIPApns
///   writeString(deviceId)
struct PushNotificationPacketTests {

    // MARK: - Enum Value Parity

    @Test("PushNotificationAction.subscribe == 0 (server: SUBSCRIBE)")
    func subscribeActionValue() {
        #expect(PushNotificationAction.subscribe.rawValue == 0)
    }

    @Test("PushNotificationAction.unsubscribe == 1 (server: UNSUBSCRIBE)")
    func unsubscribeActionValue() {
        #expect(PushNotificationAction.unsubscribe.rawValue == 1)
    }

    @Test("PushTokenType.fcm == 0 (server: FCM)")
    func fcmTokenTypeValue() {
        #expect(PushTokenType.fcm.rawValue == 0)
    }

    @Test("PushTokenType.voipApns == 1 (server: VoIPApns)")
    func voipTokenTypeValue() {
        #expect(PushTokenType.voipApns.rawValue == 1)
    }

    // MARK: - Round Trip (encode → decode)

    @Test("FCM subscribe round-trip preserves all fields")
    func fcmSubscribeRoundTrip() throws {
        var packet = PacketPushNotification()
        packet.notificationsToken = "test-fcm-token-abc123"
        packet.action = .subscribe
        packet.tokenType = .fcm
        packet.deviceId = "device-id-xyz"

        let decoded = try decodePushNotification(packet)
        #expect(decoded.notificationsToken == "test-fcm-token-abc123")
        #expect(decoded.action == .subscribe)
        #expect(decoded.tokenType == .fcm)
        #expect(decoded.deviceId == "device-id-xyz")
    }

    @Test("VoIP unsubscribe round-trip preserves all fields")
    func voipUnsubscribeRoundTrip() throws {
        var packet = PacketPushNotification()
        packet.notificationsToken = "voip-hex-token-deadbeef"
        packet.action = .unsubscribe
        packet.tokenType = .voipApns
        packet.deviceId = "another-device-id"

        let decoded = try decodePushNotification(packet)
        #expect(decoded.notificationsToken == "voip-hex-token-deadbeef")
        #expect(decoded.action == .unsubscribe)
        #expect(decoded.tokenType == .voipApns)
        #expect(decoded.deviceId == "another-device-id")
    }

    @Test("Empty token and deviceId round-trip")
    func emptyFieldsRoundTrip() throws {
        var packet = PacketPushNotification()
        packet.notificationsToken = ""
        packet.action = .subscribe
        packet.tokenType = .fcm
        packet.deviceId = ""

        let decoded = try decodePushNotification(packet)
        #expect(decoded.notificationsToken == "")
        #expect(decoded.deviceId == "")
    }

    // MARK: - Wire Format Byte Verification

    @Test("Packet ID is 0x10 in encoded data")
    func packetIdInEncodedData() {
        #expect(PacketPushNotification.packetId == 0x10)

        let packet = PacketPushNotification()
        let data = PacketRegistry.encode(packet)

        // First 2 bytes = packetId in big-endian: 0x00 0x10
        #expect(data.count >= 2)
        #expect(data[0] == 0x00)
        #expect(data[1] == 0x10)
    }

    @Test("Wire format field order matches server: token → action → tokenType → deviceId")
    func wireFormatFieldOrder() throws {
        // Use known short values so we can verify byte positions.
        var packet = PacketPushNotification()
        packet.notificationsToken = "T"   // 1 UTF-16 code unit
        packet.action = .subscribe        // 0
        packet.tokenType = .voipApns      // 1
        packet.deviceId = "D"             // 1 UTF-16 code unit

        let data = PacketRegistry.encode(packet)

        // Expected layout:
        // [0-1]   packetId = 0x0010 (2 bytes)
        // [2-5]   string length = 1 (UInt32 big-endian) for "T"
        // [6-7]   'T' = 0x0054 (UInt16 big-endian)
        // [8]     action = 0 (subscribe)
        // [9]     tokenType = 1 (voipApns)
        // [10-13] string length = 1 for "D"
        // [14-15] 'D' = 0x0044 (UInt16 big-endian)

        #expect(data.count == 16)

        // packetId
        #expect(data[0] == 0x00)
        #expect(data[1] == 0x10)

        // token string length = 1
        #expect(data[2] == 0x00)
        #expect(data[3] == 0x00)
        #expect(data[4] == 0x00)
        #expect(data[5] == 0x01)

        // 'T' in UTF-16 BE
        #expect(data[6] == 0x00)
        #expect(data[7] == 0x54)

        // action = 0 (subscribe)
        #expect(data[8] == 0x00)

        // tokenType = 1 (voipApns)
        #expect(data[9] == 0x01)

        // deviceId string length = 1
        #expect(data[10] == 0x00)
        #expect(data[11] == 0x00)
        #expect(data[12] == 0x00)
        #expect(data[13] == 0x01)

        // 'D' in UTF-16 BE
        #expect(data[14] == 0x00)
        #expect(data[15] == 0x44)
    }

    // MARK: - Helper

    private func decodePushNotification(
        _ packet: PacketPushNotification
    ) throws -> PacketPushNotification {
        let encoded = PacketRegistry.encode(packet)
        guard let decoded = PacketRegistry.decode(from: encoded),
              let decodedPacket = decoded.packet as? PacketPushNotification
        else {
            throw NSError(
                domain: "PushNotificationPacketTests", code: 1,
                userInfo: [NSLocalizedDescriptionKey: "Failed to decode PacketPushNotification"]
            )
        }
        #expect(decoded.packetId == 0x10)
        return decodedPacket
    }
}
