package com.syncflow.ui.auth

import android.accounts.Account
import android.accounts.AccountManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.syncflow.data.db.entities.CloudAccountEntity
import com.syncflow.data.providers.ProviderFactory
import com.syncflow.data.repository.AccountRepository
import com.syncflow.domain.model.ProviderType
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.serialization.json.*
import javax.inject.Inject

data class AccountSetupState(
    val providerType: ProviderType? = null,
    val displayName: String = "",
    val serverUrl: String = "",
    val port: String = "",
    val username: String = "",
    val password: String = "",
    val privateKey: String = "",
    val oauthToken: String = "",
    val oauthEmail: String = "",
    val httpWarning: Boolean = false,
    val isTestingConnection: Boolean = false,
    val testResult: TestResult? = null,
    val isSaving: Boolean = false,
    val error: String? = null,
    val done: Boolean = false,
)

sealed interface TestResult {
    object Success : TestResult
    data class Failure(val message: String) : TestResult
}

@HiltViewModel
class AccountSetupViewModel @Inject constructor(
    private val accountRepository: AccountRepository,
    private val providerFactory: ProviderFactory,
    val credentialStore: com.syncflow.data.security.CredentialStore,
    @ApplicationContext private val context: Context,
) : ViewModel() {

    private val _state = MutableStateFlow(AccountSetupState())
    val state = _state.asStateFlow()

    private val oauthReceiver = object : BroadcastReceiver() {
        override fun onReceive(ctx: Context, intent: Intent) {
            val token = intent.getStringExtra(OAUTH_EXTRA_TOKEN) ?: return
            val email = intent.getStringExtra(OAUTH_EXTRA_EMAIL) ?: ""
            _state.update { s ->
                s.copy(
                    oauthToken = token,
                    oauthEmail = email,
                    displayName = s.displayName.ifBlank { email.ifBlank { s.providerType?.friendlyName() ?: "" } },
                )
            }
        }
    }

    init {
        LocalBroadcastManager.getInstance(context)
            .registerReceiver(oauthReceiver, IntentFilter(OAUTH_REDIRECT_ACTION))
    }

    override fun onCleared() {
        LocalBroadcastManager.getInstance(context).unregisterReceiver(oauthReceiver)
        super.onCleared()
    }

    fun update(transform: AccountSetupState.() -> AccountSetupState) {
        _state.update { s ->
            val next = transform(s).copy(testResult = null)
            next.copy(httpWarning = next.serverUrl.startsWith("http://", ignoreCase = true))
        }
    }

    fun onGoogleAccountChosen(accountName: String) {
        _state.update { it.copy(oauthEmail = accountName, oauthToken = "google_account:$accountName", displayName = it.displayName.ifBlank { accountName }) }
    }

    fun testConnection() {
        val entity = buildEntity() ?: run {
            _state.update { it.copy(error = "Fill in all required fields first") }
            return
        }
        viewModelScope.launch {
            _state.update { it.copy(isTestingConnection = true, testResult = null, error = null) }
            val provider = runCatching { providerFactory.create(entity.toDomain()) }.getOrElse { e ->
                _state.update { it.copy(isTestingConnection = false, testResult = TestResult.Failure(e.message ?: "Provider error")) }
                return@launch
            }
            val result = provider.testConnection()
            _state.update { s ->
                s.copy(
                    isTestingConnection = false,
                    testResult = if (result.isSuccess) TestResult.Success else TestResult.Failure(result.exceptionOrNull()?.message ?: "Connection failed"),
                )
            }
        }
    }

    fun save() {
        val built = buildEntityWithCreds() ?: run {
            _state.update { it.copy(error = "Fill in all required fields") }
            return
        }
        viewModelScope.launch {
            _state.update { it.copy(isSaving = true, error = null) }
            runCatching { accountRepository.insert(built.first, built.second) }
                .onSuccess { _state.update { it.copy(done = true) } }
                .onFailure { e -> _state.update { it.copy(isSaving = false, error = e.message) } }
        }
    }

    /** Returns Pair(entity, credentialJson) or null if validation fails. */
    private fun buildEntityWithCreds(): Pair<CloudAccountEntity, String>? {
        val s = _state.value
        val provider = s.providerType ?: return null
        val credJson: String
        val serverUrl: String?
        val port: Int?
        val email: String?

        when {
            provider.isOAuth() -> {
                if (s.oauthToken.isBlank()) return null
                credJson = buildJsonObject { put("access_token", s.oauthToken) }.toString()
                serverUrl = null
                port = null
                email = s.oauthEmail.ifBlank { null }
            }
            provider == ProviderType.SFTP -> {
                if (s.serverUrl.isBlank() || s.username.isBlank()) return null
                credJson = buildJsonObject {
                    put("username", s.username)
                    if (s.privateKey.isNotBlank()) put("private_key", s.privateKey)
                    else put("password", s.password)
                }.toString()
                serverUrl = s.serverUrl
                port = s.port.toIntOrNull() ?: 22
                email = s.username
            }
            else -> {
                if (s.serverUrl.isBlank() || s.username.isBlank()) return null
                credJson = buildJsonObject {
                    put("username", s.username)
                    put("password", s.password)
                }.toString()
                serverUrl = s.serverUrl.trimEnd('/')
                port = s.port.toIntOrNull()
                email = s.username
            }
        }

        val entity = CloudAccountEntity(
            displayName = s.displayName.ifBlank { provider.friendlyName() },
            email = email,
            providerType = provider,
            credentialJson = "",   // never persisted to plaintext DB
            serverUrl = serverUrl,
            port = port,
        )
        return Pair(entity, credJson)
    }

    // testConnection() still uses in-memory credentials — build a temporary CloudAccount
    private fun buildEntity(): CloudAccountEntity? = buildEntityWithCreds()?.let { (entity, cred) ->
        entity.copy(credentialJson = cred)
    }
}

fun ProviderType.isOAuth() = this in setOf(ProviderType.GOOGLE_DRIVE, ProviderType.DROPBOX, ProviderType.ONEDRIVE)

fun ProviderType.friendlyName() = when (this) {
    ProviderType.GOOGLE_DRIVE -> "Google Drive"
    ProviderType.DROPBOX -> "Dropbox"
    ProviderType.ONEDRIVE -> "OneDrive"
    ProviderType.WEBDAV -> "WebDAV"
    ProviderType.SFTP -> "SFTP"
    ProviderType.NEXTCLOUD -> "Nextcloud"
    ProviderType.OWNCLOUD -> "ownCloud"
    ProviderType.SFTPGO -> "SFTPGo"
}

private fun CloudAccountEntity.toDomain() = com.syncflow.domain.model.CloudAccount(
    id = id, displayName = displayName, email = email, providerType = providerType,
    credentialJson = credentialJson, serverUrl = serverUrl, port = port,
)
