package dev.inmo.postssystem.features.roles.common.keyvalue

import dev.inmo.postssystem.features.common.common.default
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.micro_utils.pagination.changeResults
import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import kotlinx.serialization.KSerializer
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json

open class ReadKeyValueRolesStorage<T : Role>(
    private val keyValuesRepo: ReadKeyValuesRepo<String, String>,
    private val serializer: KSerializer<T>,
    private val format: StringFormat = defaultJson
) : ReadRolesStorage<T> {
    override suspend fun getSubjects(role: T): List<RoleSubject> {
        val serialized = format.encodeToString(serializer, role)

        return keyValuesRepo.getAllByWithNextPaging {
            keys(serialized, it).let { paginationResult ->
                paginationResult.changeResults(
                    paginationResult.results.map { serializedSubject -> format.decodeFromString(RoleSubject.serializer(), serializedSubject) }
                )
            }
        }
    }

    override suspend fun getRoles(subject: RoleSubject): List<T> {
        val subjectString = format.encodeToString(RoleSubject.serializer(), subject)
        return keyValuesRepo.getAllByWithNextPaging {
            get(subjectString, it).let { paginationResult ->
                paginationResult.changeResults(
                    paginationResult.results.map { serialized ->
                        format.decodeFromString(serializer, serialized)
                    }
                )
            }
        }
    }

    override suspend fun contains(subject: RoleSubject, role: T): Boolean {
        val serialized = format.encodeToString(serializer, role)
        val subjectString = format.encodeToString(RoleSubject.serializer(), subject)

        return keyValuesRepo.contains(subjectString, serialized)
    }

    override suspend fun containsAny(subject: RoleSubject, roles: List<T>): Boolean {
        val subjectString = format.encodeToString(RoleSubject.serializer(), subject)
        return roles.any {
            val serialized = format.encodeToString(serializer, it)
            keyValuesRepo.contains(subjectString, serialized)
        }
    }

    companion object {
        internal val defaultJson = Json.default
    }
}
