package com.github.nullptroma.wallenc.usecases

import com.github.nullptroma.wallenc.domain.interfaces.IStorageSyncGroupStore
import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroup
import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroupEncryptionKind
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton

data class StorageSyncCompatibilityInput(
    val isEncrypted: Boolean,
    val encryptionSecret: String? = null,
)

sealed interface AddStorageToSyncGroupResult {
    data object Added : AddStorageToSyncGroupResult
    data object GroupNotFound : AddStorageToSyncGroupResult
    data object AlreadyInGroup : AddStorageToSyncGroupResult
    data object EncryptedStorageNotAllowed : AddStorageToSyncGroupResult
    data object IncompatibleEncryption : AddStorageToSyncGroupResult
    data object MissingEncryptionSecret : AddStorageToSyncGroupResult
}

@Singleton
class ManageStorageSyncGroupsUseCase @Inject constructor(
    private val groupStore: IStorageSyncGroupStore,
) {
    suspend fun getGroups(): List<StorageSyncGroup> = groupStore.getGroups()

    suspend fun createGroup(): StorageSyncGroup {
        val existingIds = getGroups().map { it.id }.toSet()
        var index = 1
        var candidate = "group-$index"
        while (candidate in existingIds) {
            index++
            candidate = "group-$index"
        }
        val group = StorageSyncGroup(
            id = candidate,
            storageUuids = emptySet(),
            encryptionKind = StorageSyncGroupEncryptionKind.UNSET,
            encryptionSecret = null,
        )
        groupStore.putGroup(group)
        return group
    }

    suspend fun removeGroup(groupId: String) {
        groupStore.removeGroup(groupId.trim())
    }

    suspend fun addStorageToGroup(
        groupId: String,
        storageUuid: UUID,
        compatibility: StorageSyncCompatibilityInput,
    ): AddStorageToSyncGroupResult {
        val current = getGroups().firstOrNull { it.id == groupId }
            ?: return AddStorageToSyncGroupResult.GroupNotFound

        if (storageUuid in current.storageUuids) {
            return AddStorageToSyncGroupResult.AlreadyInGroup
        }
        if (compatibility.isEncrypted) {
            return AddStorageToSyncGroupResult.EncryptedStorageNotAllowed
        }

        val effectiveEncryption = StorageSyncGroupEncryptionKind.NONE to null

        val (nextKind, nextSecret) = when (current.encryptionKind) {
            StorageSyncGroupEncryptionKind.UNSET -> effectiveEncryption
            StorageSyncGroupEncryptionKind.NONE -> {
                if (effectiveEncryption.first != StorageSyncGroupEncryptionKind.NONE) {
                    return AddStorageToSyncGroupResult.IncompatibleEncryption
                }
                StorageSyncGroupEncryptionKind.NONE to null
            }

            StorageSyncGroupEncryptionKind.PASSWORD -> {
                return AddStorageToSyncGroupResult.IncompatibleEncryption
            }
        }

        groupStore.putGroup(
            current.copy(
                storageUuids = current.storageUuids + storageUuid,
                encryptionKind = nextKind,
                encryptionSecret = nextSecret,
            ),
        )
        return AddStorageToSyncGroupResult.Added
    }

    suspend fun removeStorageFromGroup(groupId: String, storageUuid: UUID) {
        val current = getGroups().firstOrNull { it.id == groupId } ?: return
        val remaining = current.storageUuids - storageUuid
        groupStore.putGroup(
            current.copy(
                storageUuids = remaining,
                encryptionKind = if (remaining.isEmpty()) {
                    StorageSyncGroupEncryptionKind.UNSET
                } else {
                    current.encryptionKind
                },
                encryptionSecret = if (remaining.isEmpty()) null else current.encryptionSecret,
            ),
        )
    }
}
