/*
 * F7cloud embed — sync F7cloud AuthSession into talk-android User DB.
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package ru.f7cloud.talk.f7cloud

import android.util.Log
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare
import ru.f7cloud.talk.api.NcApi
import ru.f7cloud.talk.application.F7cloudTalkApplication
import ru.f7cloud.talk.jobs.SignalingSettingsWorker
import ru.f7cloud.talk.users.UserManager
import ru.f7cloud.talk.utils.ApiUtils
import ru.f7cloud.talk.utils.bundle.BundleKeys
import javax.inject.Inject

@AutoInjector(F7cloudTalkApplication::class)
class F7TalkUserSync {
    @Inject lateinit var userManager: UserManager
    @Inject lateinit var ncApi: NcApi

    fun ensureUser(
        serverUrl: String,
        username: String,
        appPassword: String,
        davUserId: String?,
    ): Long? {
        val baseUrl = serverUrl.trimEnd('/')
        val credentials = ApiUtils.getCredentials(username, appPassword)
        if (credentials.isNullOrBlank()) {
            Log.w(TAG, "Missing credentials for $username")
            return null
        }

        val existingId = findExistingUserId(username, baseUrl)
        if (existingId != null && hasCapabilities(existingId)) {
            refreshTokenIfNeeded(existingId, appPassword)
            userManager.setUserAsActive(userManager.getUserWithId(existingId).blockingGet()).blockingGet()
            enqueueSignalingSettings(existingId)
            Log.d(TAG, "User already synced: $username@$baseUrl")
            return existingId
        }

        val capabilities = ncApi
            .getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl))
            .blockingFirst()
        val profile = ncApi
            .getUserProfile(credentials, ApiUtils.getUrlForUserProfile(baseUrl))
            .blockingFirst()

        val profileData = profile.ocs?.data
        val capsData = capabilities.ocs?.data
        if (capsData?.capabilities == null || capsData.serverVersion == null) {
            Log.w(TAG, "Capabilities response incomplete for $baseUrl")
            return null
        }

        val displayName = profileData?.displayName?.takeIf { it.isNotBlank() }
            ?: profileData?.displayNameAlt?.takeIf { it.isNotBlank() }
            ?: username
        val userId = profileData?.userId?.takeIf { it.isNotBlank() }
            ?: davUserId?.takeIf { it.isNotBlank() }
            ?: username

        val user = userManager.storeProfile(
            username,
            UserManager.UserAttributes(
                id = existingId,
                serverUrl = baseUrl,
                currentUser = true,
                userId = userId,
                token = appPassword,
                displayName = displayName,
                pushConfigurationState = null,
                capabilities = LoganSquare.serialize(capsData.capabilities),
                serverVersion = LoganSquare.serialize(capsData.serverVersion),
                certificateAlias = null,
                externalSignalingServer = null,
            ),
        ).blockingGet()

        val internalId = user.id
        if (internalId != null) {
            enqueueSignalingSettings(internalId)
        }
        Log.d(TAG, "Synced talk user id=$internalId for $username@$baseUrl")
        return internalId
    }

    private fun enqueueSignalingSettings(internalUserId: Long) {
        val app = F7cloudTalkApplication.sharedApplication ?: return
        val data = Data.Builder()
            .putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalUserId)
            .build()
        val work = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java)
            .setInputData(data)
            .build()
        WorkManager.getInstance(app).enqueue(work)
        Log.d(TAG, "Enqueued SignalingSettingsWorker for user $internalUserId (HPB)")
    }

    private fun findExistingUserId(username: String, baseUrl: String): Long? =
        userManager.users.blockingGet()
            .firstOrNull { it.username == username && it.baseUrl == baseUrl && !it.scheduledForDeletion }
            ?.id

    private fun hasCapabilities(userId: Long): Boolean {
        val user = userManager.getUserWithId(userId).blockingGet()
        return user.capabilities?.spreedCapability != null
    }

    private fun refreshTokenIfNeeded(userId: Long, token: String) {
        val user = userManager.getUserWithId(userId).blockingGet()
        if (user.token != token) {
            user.token = token
            userManager.saveUser(user).blockingGet()
        }
    }

    companion object {
        private const val TAG = "F7TalkUserSync"

        @Volatile
        private var instance: F7TalkUserSync? = null

        fun get(): F7TalkUserSync {
            val app = F7cloudTalkApplication.sharedApplication
                ?: error("F7cloudTalkApplication not initialized")
            return instance ?: synchronized(this) {
                instance ?: F7TalkUserSync().also {
                    app.componentApplication.inject(it)
                    instance = it
                }
            }
        }
    }
}
