package dev.inmo.postssystem.services.posts.client

import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.ktor.common.encodeHex
import dev.inmo.micro_utils.repos.ktor.common.crud.createRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.updateRouting
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.removeRoute
import dev.inmo.postssystem.features.content.common.*
import dev.inmo.postssystem.features.posts.common.PostId
import dev.inmo.postssystem.features.posts.common.RegisteredPost
import dev.inmo.postssystem.services.posts.common.*
import io.ktor.client.request.forms.InputProvider
import io.ktor.client.request.forms.formData
import io.ktor.client.request.headers
import io.ktor.client.request.post
import io.ktor.http.HttpHeaders
import kotlinx.serialization.builtins.*

class ClientWritePostsService(
    private val baseUrl: String,
    private val unifiedRequester: UnifiedRequester
) : WritePostsService {
    private val root = buildStandardUrl(baseUrl, postsRootPath)

    private val contentEitherSerializer = EitherSerializer(ContentId.serializer(), ContentSerializer)
    private val contentsEitherSerializer = ListSerializer(contentEitherSerializer)
    private val contentsSerializer = ListSerializer(ContentSerializer)

    private val createFullPath = buildStandardUrl(
        root,
        createRouting
    )
    private val removeFullPath = buildStandardUrl(
        root,
        removeRoute
    )

    override suspend fun create(newPost: FullNewPost): RegisteredPost? {
        return if (newPost.content.any { it is BinaryContent }) {
            val answer = unifiedRequester.client.post<ByteArray>(createFullPath) {
                formData {
                    newPost.content.forEachIndexed { i, content ->
                        when (content) {
                            is BinaryContent -> append(
                                i.toString(),
                                InputProvider(block = content.inputProvider),
                                headers {
                                    append(HttpHeaders.ContentType, content.mimeType.raw)
                                    append(HttpHeaders.ContentDisposition, "filename=\"${content.filename.name}\"")
                                }.build()
                            )
                            else -> append(
                                i.toString(),
                                unifiedRequester.serialFormat.encodeHex(ContentSerializer, content)
                            )
                        }
                    }
                }
            }
            unifiedRequester.serialFormat.decodeFromByteArray(RegisteredPost.serializer().nullable, answer)
        } else {
            unifiedRequester.unipost(
                createFullPath,
                contentsSerializer to newPost.content,
                RegisteredPost.serializer().nullable
            )
        }
    }

    override suspend fun update(
        postId: PostId,
        content: List<Either<ContentId, Content>>
    ): RegisteredPost? {
        return if (content.any { it.optionalT2.data is BinaryContent }) {
            val answer = unifiedRequester.client.post<ByteArray>(createFullPath) {
                formData {
                    content.forEachIndexed { i, eitherContent ->
                        eitherContent.onFirst {
                            append(
                                i.toString(),
                                unifiedRequester.serialFormat.encodeHex(contentEitherSerializer, it.either())
                            )
                        }.onSecond {
                            when (it) {
                                is BinaryContent -> append(
                                    i.toString(),
                                    InputProvider(block = it.inputProvider),
                                    headers {
                                        append(HttpHeaders.ContentType, it.mimeType.raw)
                                        append(HttpHeaders.ContentDisposition, "filename=\"${it.filename.name}\"")
                                    }.build()
                                )
                                else -> append(
                                    i.toString(),
                                    unifiedRequester.serialFormat.encodeHex(contentEitherSerializer, it.either())
                                )
                            }
                        }
                    }
                }
            }
            unifiedRequester.serialFormat.decodeFromByteArray(RegisteredPost.serializer().nullable, answer)
        } else {
            unifiedRequester.unipost(
                buildStandardUrl(
                    root,
                    updateRouting,
                    postsPostIdParameter to unifiedRequester.encodeUrlQueryValue(PostId.serializer(), postId)
                ),
                contentsEitherSerializer to content,
                RegisteredPost.serializer().nullable
            )
        }
    }

    override suspend fun remove(postId: PostId) = unifiedRequester.unipost(
        removeFullPath,
        PostId.serializer() to postId,
        Unit.serializer()
    )
}
