package com.flagship.abox.manager.testing.fakes

import com.flagship.abox.manager.domain.DomainError
import com.flagship.abox.manager.domain.DomainResult
import com.flagship.abox.manager.domain.LoginCredentials
import com.flagship.abox.manager.domain.ProviderProfileId
import com.flagship.abox.manager.domain.Session
import com.flagship.abox.manager.domain.SessionGateway
import com.flagship.abox.manager.domain.SessionValidity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import java.util.concurrent.ConcurrentHashMap

class FakeSessionGateway : SessionGateway {
    private val sessions = ConcurrentHashMap<ProviderProfileId, MutableStateFlow<Session?>>()
    private val loginResults = ConcurrentHashMap<ProviderProfileId, DomainResult<Session>>()
    private val validationResults = ConcurrentHashMap<ProviderProfileId, DomainResult<SessionValidity>>()

    override fun observeSession(profileId: ProviderProfileId): Flow<Session?> = sessionFlow(profileId)

    override suspend fun login(
        profileId: ProviderProfileId,
        credentials: LoginCredentials,
    ): DomainResult<Session> {
        val result = loginResults[profileId] ?: DomainResult.Success(
            Session(profileId = profileId, username = credentials.username),
        )
        if (result is DomainResult.Success) {
            sessionFlow(profileId).value = result.value
        }
        return result
    }

    override suspend fun validateSession(
        profileId: ProviderProfileId,
    ): DomainResult<SessionValidity> = validationResults[profileId]
        ?: DomainResult.Success(
            if (sessionFlow(profileId).value == null) {
                SessionValidity.INVALID
            } else {
                SessionValidity.VALID
            },
        )

    override suspend fun logout(profileId: ProviderProfileId): DomainResult<Unit> {
        sessionFlow(profileId).value = null
        return DomainResult.Success(Unit)
    }

    fun setLoginResult(profileId: ProviderProfileId, result: DomainResult<Session>) {
        loginResults[profileId] = result
    }

    fun setValidationResult(
        profileId: ProviderProfileId,
        result: DomainResult<SessionValidity>,
    ) {
        validationResults[profileId] = result
    }

    fun failLogin(
        profileId: ProviderProfileId,
        error: DomainError = DomainError.Authentication(),
    ) {
        setLoginResult(profileId, DomainResult.Failure(error))
    }

    private fun sessionFlow(profileId: ProviderProfileId): MutableStateFlow<Session?> =
        sessions.getOrPut(profileId) { MutableStateFlow(null) }
}
