package com.rosetta.messenger.network

/**
 * Message packet (ID: 0x06)
 * Отправка и получение сообщений
 */
class PacketMessage : Packet() {
    var fromPublicKey: String = ""
    var toPublicKey: String = ""
    var content: String = ""            // Зашифрованный текст
    var chachaKey: String = ""          // RSA зашифрованный ключ
    var timestamp: Long = 0
    var privateKey: String = ""         // Hash приватного ключа (для авторизации)
    var messageId: String = ""
    var aesChachaKey: String = ""       // ChaCha key+nonce зашифрованный приватным ключом отправителя
    var attachments: List<MessageAttachment> = emptyList()

    private data class ParsedPacketMessage(
            val fromPublicKey: String,
            val toPublicKey: String,
            val content: String,
            val chachaKey: String,
            val timestamp: Long,
            val privateKey: String,
            val messageId: String,
            val attachments: List<MessageAttachment>,
            val aesChachaKey: String
    )

    override fun getPacketId(): Int = 0x06

    override fun receive(stream: Stream) {
        val startPointer = stream.getReadPointerBits()
        val parsed =
                listOf(4, 2, 0)
                        .asSequence()
                        .mapNotNull { attachmentMetaFieldCount ->
                            stream.setReadPointerBits(startPointer)
                            parseFromStream(stream, attachmentMetaFieldCount)
                                    ?.takeIf { !stream.hasRemainingBits() }
                        }
                        .firstOrNull()
                        ?: run {
                            stream.setReadPointerBits(startPointer)
                            parseFromStream(stream, 2)
                                    ?: throw IllegalStateException(
                                            "Failed to parse PacketMessage payload"
                                    )
                        }

        fromPublicKey = parsed.fromPublicKey
        toPublicKey = parsed.toPublicKey
        content = parsed.content
        chachaKey = parsed.chachaKey
        timestamp = parsed.timestamp
        privateKey = parsed.privateKey
        messageId = parsed.messageId
        attachments = parsed.attachments
        aesChachaKey = parsed.aesChachaKey
    }

    override fun send(): Stream {
        val stream = Stream()
        stream.writeInt16(getPacketId())
        stream.writeString(fromPublicKey)
        stream.writeString(toPublicKey)
        stream.writeString(content)
        stream.writeString(chachaKey)
        stream.writeInt64(timestamp)
        stream.writeString(privateKey)
        stream.writeString(messageId)
        stream.writeInt8(attachments.size)

        for (attachment in attachments) {
            stream.writeString(attachment.id)
            stream.writeString(attachment.preview)
            stream.writeString(attachment.blob)
            stream.writeInt8(attachment.type.value)
            stream.writeString(attachment.transportTag)
            stream.writeString(attachment.transportServer)
        }
        stream.writeString(aesChachaKey)

        return stream
    }

    private fun parseFromStream(
            parser: Stream,
            attachmentMetaFieldCount: Int
    ): ParsedPacketMessage? {
        return runCatching {
            val parsedFromPublicKey = parser.readString()
            val parsedToPublicKey = parser.readString()
            val parsedContent = parser.readString()
            val parsedChachaKey = parser.readString()
            val parsedTimestamp = parser.readInt64()
            val parsedPrivateKey = parser.readString()
            val parsedMessageId = parser.readString()

            val attachmentCount = parser.readInt8().coerceAtLeast(0)
            val parsedAttachments = ArrayList<MessageAttachment>(attachmentCount)
            repeat(attachmentCount) {
                val id = parser.readString()
                val preview = parser.readString()
                val blob = parser.readString()
                val type = AttachmentType.fromInt(parser.readInt8())

                val transportTag: String
                val transportServer: String
                val encodedFor: String
                val encoder: String
                if (attachmentMetaFieldCount >= 2) {
                    transportTag = parser.readString()
                    transportServer = parser.readString()
                } else {
                    transportTag = ""
                    transportServer = ""
                }
                if (attachmentMetaFieldCount >= 4) {
                    encodedFor = parser.readString()
                    encoder = parser.readString()
                } else {
                    encodedFor = ""
                    encoder = ""
                }

                parsedAttachments.add(
                        MessageAttachment(
                                id = id,
                                preview = preview,
                                blob = blob,
                                type = type,
                                transportTag = transportTag,
                                transportServer = transportServer,
                                encodedFor = encodedFor,
                                encoder = encoder
                        )
                )
            }

            val parsedAesChachaKey = parser.readString()
            ParsedPacketMessage(
                    fromPublicKey = parsedFromPublicKey,
                    toPublicKey = parsedToPublicKey,
                    content = parsedContent,
                    chachaKey = parsedChachaKey,
                    timestamp = parsedTimestamp,
                    privateKey = parsedPrivateKey,
                    messageId = parsedMessageId,
                    attachments = parsedAttachments,
                    aesChachaKey = parsedAesChachaKey
            )
        }.getOrNull()
    }
}
