package com.rosetta.messenger.providers

import android.content.Context
import androidx.compose.runtime.*
import com.rosetta.messenger.crypto.CryptoManager
import com.rosetta.messenger.database.DatabaseService
import com.rosetta.messenger.database.DecryptedAccountData
import com.rosetta.messenger.network.ProtocolManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * Auth state management - matches React Native architecture
 */
sealed class AuthStatus {
    object Loading : AuthStatus()
    object Unauthenticated : AuthStatus()
    data class Authenticated(val account: DecryptedAccountData) : AuthStatus()
    data class Locked(val publicKey: String) : AuthStatus()
}

data class AuthStateData(
    val status: AuthStatus = AuthStatus.Loading,
    val hasExistingAccounts: Boolean = false,
    val availableAccounts: List<String> = emptyList()
)

class AuthStateManager(
    private val context: Context,
    private val scope: CoroutineScope
) {
    private val databaseService = DatabaseService.getInstance(context)
    
    private val _state = MutableStateFlow(AuthStateData())
    val state: StateFlow<AuthStateData> = _state.asStateFlow()
    
    private var currentDecryptedAccount: DecryptedAccountData? = null
    
    // 🚀 ОПТИМИЗАЦИЯ: Кэш списка аккаунтов для UI
    private var accountsCache: List<String>? = null
    private var lastAccountsLoadTime = 0L
    private val accountsCacheTTL = 5000L // 5 секунд
    
    companion object {
        private const val TAG = "AuthStateManager"
    }
    
    init {
        scope.launch {
            loadAccounts()
            checkAuthStatus()
        }
    }
    
    private suspend fun loadAccounts() = withContext(Dispatchers.IO) {
        try {
            // 🚀 ОПТИМИЗАЦИЯ: Используем кэш если он свежий
            val currentTime = System.currentTimeMillis()
            if (accountsCache != null && (currentTime - lastAccountsLoadTime) < accountsCacheTTL) {
                _state.update { it.copy(
                    hasExistingAccounts = accountsCache!!.isNotEmpty(),
                    availableAccounts = accountsCache!!
                )}
                return@withContext
            }
            
            val accounts = databaseService.getAllEncryptedAccounts()
            val hasAccounts = accounts.isNotEmpty()
            val accountKeys = accounts.map { it.publicKey }
            
            // Обновляем кэш
            accountsCache = accountKeys
            lastAccountsLoadTime = currentTime
            
            _state.update { it.copy(
                hasExistingAccounts = hasAccounts,
                availableAccounts = accountKeys
)}
        } catch (e: Exception) {
        }
    }
    
    private suspend fun checkAuthStatus() {
        try {
            val hasAccounts = databaseService.hasAccounts()
            if (!hasAccounts) {
                _state.update { it.copy(
                    status = AuthStatus.Unauthenticated
                )}
            } else {
                _state.update { it.copy(
                    status = AuthStatus.Unauthenticated
                )}
            }
        } catch (e: Exception) {
            _state.update { it.copy(
                status = AuthStatus.Unauthenticated
            )}
        }
    }
    
    /**
     * Create new account from seed phrase
     * Matches createAccountFromSeedPhrase from React Native
     * 🚀 ОПТИМИЗАЦИЯ: Dispatchers.Default для CPU-интенсивной криптографии
     */
    suspend fun createAccount(
        seedPhrase: List<String>,
        password: String
    ): Result<DecryptedAccountData> = withContext(Dispatchers.Default) {
        try {
            // Step 1: Generate key pair from seed phrase (using BIP39)
            val keyPair = CryptoManager.generateKeyPairFromSeed(seedPhrase)
            
            // Step 2: Generate private key hash for protocol
            val privateKeyHash = CryptoManager.generatePrivateKeyHash(keyPair.privateKey)
            
            // Step 3: Encrypt private key with password
            val encryptedPrivateKey = CryptoManager.encryptWithPassword(
                keyPair.privateKey, password
            )
            
            // Step 4: Encrypt seed phrase with password
            val encryptedSeedPhrase = CryptoManager.encryptWithPassword(
                seedPhrase.joinToString(" "), password
            )
            
            // Step 5: Save to database
            val saved = withContext(Dispatchers.IO) {
                databaseService.saveEncryptedAccount(
                    publicKey = keyPair.publicKey,
                    privateKeyEncrypted = encryptedPrivateKey,
                    seedPhraseEncrypted = encryptedSeedPhrase
                )
            }
            
            if (!saved) {
                return@withContext Result.failure(Exception("Failed to save account to database"))
            }
            
            // Step 6: Create decrypted account object
            val decryptedAccount = DecryptedAccountData(
                publicKey = keyPair.publicKey,
                privateKey = keyPair.privateKey,
                privateKeyHash = privateKeyHash,
                seedPhrase = seedPhrase
            )
            
            // Step 7: Update state and reload accounts
            currentDecryptedAccount = decryptedAccount
            _state.update { it.copy(
                status = AuthStatus.Authenticated(decryptedAccount)
            )}

            loadAccounts()

            // Initialize MessageRepository BEFORE connecting/authenticating
            // so incoming messages from server are stored under the correct account
            ProtocolManager.initializeAccount(keyPair.publicKey, keyPair.privateKey)

            // Step 8: Connect and authenticate with protocol
            ProtocolManager.connect()
            ProtocolManager.authenticate(keyPair.publicKey, privateKeyHash)
            ProtocolManager.reconnectNowIfNeeded("auth_state_create")
            
            Result.success(decryptedAccount)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    /**
     * Unlock account with password
     * Matches loginWithPassword from React Native
     */
    suspend fun unlock(
        publicKey: String,
        password: String
    ): Result<DecryptedAccountData> = withContext(Dispatchers.Default) {
        try {
            // Decrypt account from database
            val decryptedAccount = withContext(Dispatchers.IO) {
                databaseService.decryptAccount(publicKey, password)
            }
            
            if (decryptedAccount == null) {
                return@withContext Result.failure(Exception("Invalid password or account not found"))
            }
            
            // Update last used timestamp
            withContext(Dispatchers.IO) {
                databaseService.updateLastUsed(publicKey)
            }
            
            // Update state
            currentDecryptedAccount = decryptedAccount
            _state.update { it.copy(
                status = AuthStatus.Authenticated(decryptedAccount)
            )}

            // Initialize MessageRepository BEFORE connecting/authenticating
            // so incoming messages from server are stored under the correct account
            ProtocolManager.initializeAccount(decryptedAccount.publicKey, decryptedAccount.privateKey)

            // Connect and authenticate with protocol
            ProtocolManager.connect()
            ProtocolManager.authenticate(decryptedAccount.publicKey, decryptedAccount.privateKeyHash)
            ProtocolManager.reconnectNowIfNeeded("auth_state_unlock")
            
            Result.success(decryptedAccount)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    /**
     * Logout - clears decrypted account from memory
     */
    fun logout() {
        currentDecryptedAccount = null
        _state.update { it.copy(
            status = AuthStatus.Unauthenticated
        )}
    }
    
    /**
     * Delete account from database
     */
    suspend fun deleteAccount(publicKey: String): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val success = databaseService.deleteAccount(publicKey)
            if (!success) {
                return@withContext Result.failure(Exception("Failed to delete account"))
            }
            
            // If deleting current account, logout
            if (currentDecryptedAccount?.publicKey == publicKey) {
                withContext(Dispatchers.Main) {
                    logout()
                }
            }
            
            loadAccounts()
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    /**
     * Get current decrypted account (if authenticated)
     */
    fun getCurrentAccount(): DecryptedAccountData? = currentDecryptedAccount
}

@Composable
fun rememberAuthState(context: Context): AuthStateManager {
    val scope = rememberCoroutineScope()
    return remember(context) {
        AuthStateManager(context, scope)
    }
}

@Composable
fun ProvideAuthState(
    authState: AuthStateManager,
    content: @Composable (AuthStateData) -> Unit
) {
    val state by authState.state.collectAsState()
    content(state)
}
