package dev.inmo.postssystem.features.files.common

import com.benasher44.uuid.uuid4
import dev.inmo.postssystem.features.files.common.storage.WriteFilesStorage
import dev.inmo.micro_utils.repos.*
import io.ktor.utils.io.core.copyTo
import io.ktor.utils.io.streams.asOutput
import kotlinx.coroutines.flow.*
import java.io.File

class WriteDistFilesStorage(
    private val filesFolder: File,
    private val metasKeyValueRepo: WriteKeyValueRepo<FileId, MetaFileInfo>
) : WriteFilesStorage {
    private val FileId.file
        get() = File(filesFolder, string)

    private val _deletedObjectsIdsFlow = MutableSharedFlow<FileId>()
    private val _newObjectsFlow = MutableSharedFlow<FullFileInfoStorageWrapper>()
    private val _updatedObjectsFlow = MutableSharedFlow<FullFileInfoStorageWrapper>()
    override val deletedObjectsIdsFlow: Flow<FileId> = _deletedObjectsIdsFlow.asSharedFlow()
    override val newObjectsFlow: Flow<FullFileInfoStorageWrapper> = _newObjectsFlow.asSharedFlow()
    override val updatedObjectsFlow: Flow<FullFileInfoStorageWrapper> = _updatedObjectsFlow.asSharedFlow()

    init {
        if (!filesFolder.exists()) {
            filesFolder.mkdirs()
        } else {
            require(filesFolder.isDirectory) { "$filesFolder must be a directory" }
        }
    }

    override suspend fun create(values: List<FullFileInfo>): List<FullFileInfoStorageWrapper> = values.map {
        var newId: FileId
        var file: File
        do {
            newId = FileId(uuid4().toString())
            file = newId.file
        } while (file.exists())
        metasKeyValueRepo.set(newId, it.toMetaFileInfo())
        it.inputProvider().copyTo(file.outputStream().asOutput())
        FullFileInfoStorageWrapper(newId, it)
    }

    override suspend fun deleteById(ids: List<FileId>) {
        ids.forEach {
            if (it.file.delete()) {
                metasKeyValueRepo.unset(it)
                _deletedObjectsIdsFlow.emit(it)
            }
        }
    }

    override suspend fun update(
        id: FileId,
        value: FullFileInfo
    ): FullFileInfoStorageWrapper? {
        val file = id.file.takeIf { it.exists() } ?: return null
        value.inputProvider().copyTo(file.outputStream().asOutput())
        val result = FullFileInfoStorageWrapper(id, value.copy())

        metasKeyValueRepo.set(id, value.toMetaFileInfo())
        _updatedObjectsFlow.emit(result)

        return result
    }

    override suspend fun update(
        values: List<UpdatedValuePair<FileId, FullFileInfo>>
    ): List<FullFileInfoStorageWrapper> = values.mapNotNull { (id, file) ->
        update(id, file)
    }
}
