package dev.inmo.postssystem.services.posts.common

import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.micro_utils.mime_types.MimeTypeSerializer
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.content.common.BinaryContent
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.reflect.KClass

@Serializer(BinaryContent::class)
class BinaryContentSerializer<T: SimpleInputProvider>(
    private val kClass: KClass<T>,
    private val serializer: KSerializer<T>
) : KSerializer<BinaryContent> {
    @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
    override val descriptor: SerialDescriptor = buildSerialDescriptor(
        "BinaryContent",
        SerialKind.CONTEXTUAL
    ) {
        element("filename", FileName.serializer().descriptor)
        element("mimetype", MimeTypeSerializer.descriptor)
        element("data", serializer.descriptor)
    }

    override fun deserialize(decoder: Decoder): BinaryContent {
        return decoder.decodeStructure(descriptor) {
            var fileName: FileName? = null
            var mimeType: MimeType? = null
            var data: T? = null
            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> fileName = decodeSerializableElement(descriptor, 0, FileName.serializer())
                    1 -> mimeType = decodeSerializableElement(descriptor, 1, MimeTypeSerializer)
                    2 -> data = decodeSerializableElement(descriptor, 2, serializer)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
            BinaryContent(
                fileName ?: error("Filename must be presented, but was absent"),
                mimeType ?: error("Mimetype must be presented, but was absent"),
                data ?: error("Data must be presented, but was absent")
            )
        }
    }

    override fun serialize(encoder: Encoder, value: BinaryContent) {
        require(kClass.isInstance(value.inputProvider)) { "${value.inputProvider} do not inherit $kClass" }
        encoder.encodeStructure(descriptor) {
            encodeSerializableElement(descriptor, 0, FileName.serializer(), value.filename)
            encodeSerializableElement(descriptor, 1, MimeTypeSerializer, value.mimeType)
            encodeSerializableElement(descriptor, 2, serializer, value.inputProvider as T)
        }
    }
}
