import Testing
@testable import Rosetta

// MARK: - Push Notification Extended Tests

struct PushNotificationExtendedTests {

    @Test("Realistic FCM token with device ID round-trip")
    func fcmTokenWithDeviceIdRoundTrip() throws {
        // Real FCM tokens are ~163 chars
        let fcmToken = "dQw4w9WgXcQ:APA91bHnzPc5Y0z4R8kP3mN6vX2tL7wJ1qA5sD8fG0hK3lZ9xC2vB4nM7oP1iU8yT6rE5wQ3jF4kL2mN0bV7cX9sD1aF3gH5jK7lP9oI2uY4tR6eW8qZ0xC"
        var packet = PacketPushNotification()
        packet.notificationsToken = fcmToken
        packet.action = .subscribe
        packet.tokenType = .fcm
        packet.deviceId = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop"

        let decoded = try decode(packet)
        #expect(decoded.notificationsToken == fcmToken)
        #expect(decoded.action == .subscribe)
        #expect(decoded.tokenType == .fcm)
        #expect(decoded.deviceId == "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop")
    }

    @Test("Realistic VoIP hex token round-trip")
    func voipTokenWithDeviceIdRoundTrip() throws {
        // PushKit tokens are 32 bytes = 64 hex chars
        let voipToken = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
        var packet = PacketPushNotification()
        packet.notificationsToken = voipToken
        packet.action = .subscribe
        packet.tokenType = .voipApns
        packet.deviceId = "device-xyz-123"

        let decoded = try decode(packet)
        #expect(decoded.notificationsToken == voipToken)
        #expect(decoded.tokenType == .voipApns)
    }

    @Test("Long token (256 chars) round-trip — stress test UInt32 string length")
    func longTokenRoundTrip() throws {
        let longToken = String(repeating: "x", count: 256)
        var packet = PacketPushNotification()
        packet.notificationsToken = longToken
        packet.action = .subscribe
        packet.tokenType = .fcm
        packet.deviceId = "dev"

        let decoded = try decode(packet)
        #expect(decoded.notificationsToken == longToken)
        #expect(decoded.notificationsToken.count == 256)
    }

    @Test("Unicode device ID with emoji and Cyrillic round-trip")
    func unicodeDeviceIdRoundTrip() throws {
        let unicodeId = "Телефон Гайдара 📱"
        var packet = PacketPushNotification()
        packet.notificationsToken = "token"
        packet.action = .subscribe
        packet.tokenType = .fcm
        packet.deviceId = unicodeId

        let decoded = try decode(packet)
        #expect(decoded.deviceId == unicodeId)
    }

    @Test("Unsubscribe action round-trip for both token types",
          arguments: [PushTokenType.fcm, PushTokenType.voipApns])
    func unsubscribeRoundTrip(tokenType: PushTokenType) throws {
        var packet = PacketPushNotification()
        packet.notificationsToken = "test-token"
        packet.action = .unsubscribe
        packet.tokenType = tokenType
        packet.deviceId = "dev"

        let decoded = try decode(packet)
        #expect(decoded.action == .unsubscribe)
        #expect(decoded.tokenType == tokenType)
    }

    private func decode(_ packet: PacketPushNotification) throws -> PacketPushNotification {
        let data = PacketRegistry.encode(packet)
        guard let result = PacketRegistry.decode(from: data),
              let decoded = result.packet as? PacketPushNotification
        else { throw TestError("Failed to decode PacketPushNotification") }
        #expect(result.packetId == 0x10)
        return decoded
    }
}

// MARK: - Signal Peer Call Flow Tests

struct SignalPeerCallFlowTests {

    @Test("Incoming call signal with realistic secp256k1 keys")
    func incomingCallSignalRoundTrip() throws {
        let caller = "02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
        let callee = "03f0e1d2c3b4a5968778695a4b3c2d1e0f9e8d7c6b5a49382716051a2b3c4d5e6f"
        let packet = PacketSignalPeer(src: caller, dst: callee, sharedPublic: "",
                                      signalType: .call, callId: "call-1", joinToken: "join-1", roomId: "")
        let decoded = try decode(packet)
        #expect(decoded.signalType == .call)
        #expect(decoded.src == caller)
        #expect(decoded.dst == callee)
        #expect(decoded.sharedPublic == "")
        #expect(decoded.callId == "call-1")
        #expect(decoded.joinToken == "join-1")
        #expect(decoded.roomId == "")
    }

    @Test("Key exchange with X25519 public key")
    func keyExchangeRoundTrip() throws {
        let x25519Key = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
        let packet = PacketSignalPeer(src: "02src", dst: "02dst", sharedPublic: x25519Key,
                                      signalType: .keyExchange, roomId: "")
        let decoded = try decode(packet)
        #expect(decoded.signalType == .keyExchange)
        #expect(decoded.sharedPublic == x25519Key)
        #expect(decoded.roomId == "")
    }

    @Test("Create room with UUID room ID")
    func createRoomRoundTrip() throws {
        let roomId = "550e8400-e29b-41d4-a716-446655440000"
        let packet = PacketSignalPeer(src: "02src", dst: "02dst", sharedPublic: "",
                                      signalType: .createRoom, roomId: roomId)
        let decoded = try decode(packet)
        #expect(decoded.signalType == .createRoom)
        #expect(decoded.roomId == roomId)
        #expect(decoded.sharedPublic == "")
    }

    @Test("endCallBecauseBusy short format — 3 bytes wire size, no src/dst")
    func endCallBusyShortFormat() throws {
        let packet = PacketSignalPeer(src: "ignored", dst: "ignored", sharedPublic: "ignored",
                                      signalType: .endCallBecauseBusy, roomId: "ignored")
        let data = PacketRegistry.encode(packet)
        // Short form: 2 bytes packetId + 1 byte signalType = 3 bytes
        #expect(data.count == 3)

        let decoded = try decode(packet)
        #expect(decoded.signalType == .endCallBecauseBusy)
        #expect(decoded.src == "")
        #expect(decoded.dst == "")
    }

    @Test("endCallBecausePeerDisconnected short format — 3 bytes wire size")
    func endCallDisconnectedShortFormat() throws {
        let packet = PacketSignalPeer(src: "ignored", dst: "ignored", sharedPublic: "ignored",
                                      signalType: .endCallBecausePeerDisconnected, roomId: "ignored")
        let data = PacketRegistry.encode(packet)
        #expect(data.count == 3)

        let decoded = try decode(packet)
        #expect(decoded.signalType == .endCallBecausePeerDisconnected)
    }

    @Test("RINGING_TIMEOUT short format — 3 bytes wire size")
    func ringingTimeoutShortFormat() throws {
        let packet = PacketSignalPeer(src: "ignored", dst: "ignored", sharedPublic: "ignored",
                                      signalType: .ringingTimeout, roomId: "ignored")
        let data = PacketRegistry.encode(packet)
        #expect(data.count == 3)

        let decoded = try decode(packet)
        #expect(decoded.signalType == .ringingTimeout)
        #expect(decoded.src == "")
        #expect(decoded.dst == "")
    }

    private func decode(_ packet: PacketSignalPeer) throws -> PacketSignalPeer {
        let data = PacketRegistry.encode(packet)
        guard let result = PacketRegistry.decode(from: data),
              let decoded = result.packet as? PacketSignalPeer
        else { throw TestError("Failed to decode PacketSignalPeer") }
        #expect(result.packetId == 0x1A)
        return decoded
    }
}

// MARK: - Enum Parity Tests

struct CallPushEnumParityTests {

    @Test("SignalType enum values match server",
          arguments: [
            (SignalType.call, 0), (SignalType.keyExchange, 1), (SignalType.activeCall, 2),
            (SignalType.endCall, 3), (SignalType.createRoom, 4),
            (SignalType.endCallBecausePeerDisconnected, 5), (SignalType.endCallBecauseBusy, 6),
            (SignalType.accept, 7), (SignalType.ringingTimeout, 8)
          ])
    func signalTypeEnumValues(pair: (SignalType, Int)) {
        #expect(pair.0.rawValue == pair.1)
    }

    @Test("WebRTCSignalType enum values match server",
          arguments: [(WebRTCSignalType.offer, 0), (WebRTCSignalType.answer, 1),
                      (WebRTCSignalType.iceCandidate, 2)])
    func webRTCSignalTypeValues(pair: (WebRTCSignalType, Int)) {
        #expect(pair.0.rawValue == pair.1)
    }

    @Test("PushTokenType enum values match server")
    func pushTokenTypeValues() {
        #expect(PushTokenType.fcm.rawValue == 0)
        #expect(PushTokenType.voipApns.rawValue == 1)
    }
}

// MARK: - Wire Format Byte-Level Tests

struct CallPushWireFormatTests {

    @Test("PushNotification byte layout: token→action→tokenType→deviceId")
    func pushNotificationByteLayout() {
        var packet = PacketPushNotification()
        packet.notificationsToken = "A"
        packet.action = .unsubscribe
        packet.tokenType = .fcm
        packet.deviceId = "B"

        let data = PacketRegistry.encode(packet)
        #expect(data.count == 16)

        // packetId = 0x0010
        #expect(data[0] == 0x00); #expect(data[1] == 0x10)
        // token "A": length=1, 'A'=0x0041
        #expect(data[2] == 0x00); #expect(data[3] == 0x00)
        #expect(data[4] == 0x00); #expect(data[5] == 0x01)
        #expect(data[6] == 0x00); #expect(data[7] == 0x41)
        // action = 1 (unsubscribe)
        #expect(data[8] == 0x01)
        // tokenType = 0 (fcm)
        #expect(data[9] == 0x00)
        // deviceId "B": length=1, 'B'=0x0042
        #expect(data[10] == 0x00); #expect(data[11] == 0x00)
        #expect(data[12] == 0x00); #expect(data[13] == 0x01)
        #expect(data[14] == 0x00); #expect(data[15] == 0x42)
    }

    @Test("SignalPeer call byte layout: signalType→src→dst→callId→joinToken")
    func signalPeerCallByteLayout() {
        let packet = PacketSignalPeer(src: "S", dst: "D", sharedPublic: "",
                                      signalType: .call, roomId: "")
        let data = PacketRegistry.encode(packet)
        #expect(data.count == 23)

        // packetId = 0x001A
        #expect(data[0] == 0x00); #expect(data[1] == 0x1A)
        // signalType = 0 (call)
        #expect(data[2] == 0x00)
        // src "S": length=1, 'S'=0x0053
        #expect(data[3] == 0x00); #expect(data[4] == 0x00)
        #expect(data[5] == 0x00); #expect(data[6] == 0x01)
        #expect(data[7] == 0x00); #expect(data[8] == 0x53)
        // dst "D": length=1, 'D'=0x0044
        #expect(data[9] == 0x00); #expect(data[10] == 0x00)
        #expect(data[11] == 0x00); #expect(data[12] == 0x01)
        #expect(data[13] == 0x00); #expect(data[14] == 0x44)
        // callId "": length=0
        #expect(data[15] == 0x00); #expect(data[16] == 0x00)
        #expect(data[17] == 0x00); #expect(data[18] == 0x00)
        // joinToken "": length=0
        #expect(data[19] == 0x00); #expect(data[20] == 0x00)
        #expect(data[21] == 0x00); #expect(data[22] == 0x00)
    }
}

// MARK: - Helpers

private struct TestError: Error, CustomStringConvertible {
    let description: String
    init(_ message: String) { self.description = message }
}
