/*
 * Copyright (c) 2026 QPQ IaaS AG <info@qpq.swiss>. All rights reserved.
 * Project: Gajumaru Core Java Libraries <gajumaru.io>
 *
 * This program is dual-licensed:
 * 1) Under the GNU Affero General Public License as published by the Free
 *    Software Foundation, either version 3 of the License, or (at your option)
 *    any later version (AGPL-3.0-or-later).
 *
 * 2) Under a commercial/proprietary license available directly from QPQ IaaS AG.
 *    If you wish to use this software outside the strict constraints of the
 *    AGPLv3 (e.g. within a closed-source or proprietary product), you must
 *    purchase a commercial license from QPQ IaaS AG.
 *
 * Authors:
 *   - Craig Everett <craigeverett@qpq.swiss>
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-QPQ-Commercial
 */

package swiss.qpq.gajumobile

import android.os.Bundle
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ProcessLifecycleOwner
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import swiss.qpq.gajumobile.data.AppStateManager
import swiss.qpq.gajumobile.data.WalletRepository
import swiss.qpq.gajumobile.data.models.Account
import swiss.qpq.gajumobile.data.models.GridsSignRequest
import swiss.qpq.gajumobile.data.models.GridsSignType
import swiss.qpq.gajumobile.data.models.Wallet
import swiss.qpq.gajumobile.data.models.toGridsSignRequest
import swiss.qpq.gajumobile.security.AppLifecycleObserver
import swiss.qpq.gajumobile.security.BiometricHelper
import swiss.qpq.gajumobile.security.SecurityManager
import swiss.qpq.gajumobile.security.KeyManager
import swiss.qpq.gajumobile.security.AccountAirlock
import swiss.qpq.gajumobile.security.GajuNative
import swiss.qpq.gajumobile.ui.screens.AccountNamingScreen
import kotlin.time.Duration.Companion.seconds
import swiss.qpq.gajumobile.ui.screens.CreateWalletScreen
import swiss.qpq.gajumobile.ui.screens.DashboardScreen
import swiss.qpq.gajumobile.ui.screens.DeleteConfirmationScreen
import swiss.qpq.gajumobile.ui.screens.EnvironmentInfoScreen
import swiss.qpq.gajumobile.ui.screens.FetchingScreen
import swiss.qpq.gajumobile.ui.screens.LockScreen
import swiss.qpq.gajumobile.ui.screens.ManageAccountScreen
import swiss.qpq.gajumobile.ui.screens.ManageWalletsScreen
import swiss.qpq.gajumobile.ui.screens.ManualGridsEntryScreen
import swiss.qpq.gajumobile.ui.screens.MnemonicRecoveryScreen
import swiss.qpq.gajumobile.ui.screens.MnemonicVerifyScreen
import swiss.qpq.gajumobile.ui.screens.QRReceiveScreen
import swiss.qpq.gajumobile.ui.screens.QRScannerScreen
import swiss.qpq.gajumobile.ui.screens.SendFormScreen
import swiss.qpq.gajumobile.ui.screens.SendReviewScreen
import swiss.qpq.gajumobile.ui.screens.SettingsScreen
import swiss.qpq.gajumobile.ui.screens.SetupChoiceScreen
import swiss.qpq.gajumobile.ui.screens.SignRequestScreen
import swiss.qpq.gajumobile.ui.screens.TransactionSuccessScreen
import swiss.qpq.gajumobile.ui.screens.ViewMnemonicScreen
import swiss.qpq.gajumobile.ui.theme.MyApplicationTheme
import swiss.qpq.gajumaru.core.formatting.GajuFormat
import swiss.qpq.gajumaru.core.tools.Grids
import swiss.qpq.gajumaru.core.tools.Http
import swiss.qpq.gajumaru.core.tools.NodeClient
import swiss.qpq.gajumaru.core.tools.TransactionService
import java.math.BigInteger
import java.util.UUID

class MainActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        window.isNavigationBarContrastEnforced = false
        
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver(this))

        setContent {
            val context = LocalContext.current
            val isLocked by SecurityManager.isLocked.collectAsState()
            var lockErrorMessage by remember { mutableStateOf<String?>(null) }
            
            val walletRepo = remember { WalletRepository(context) }
            val appStateManager = remember { AppStateManager(context) }

            MyApplicationTheme {
                if (isLocked) {
                    LockScreen(errorMessage = lockErrorMessage) {
                        BiometricHelper.showBiometricPrompt(
                            activity = this,
                            onSuccess = { 
                                lockErrorMessage = null
                                SecurityManager.unlock() 
                            },
                        ) { err -> 
                            lockErrorMessage = err
                        }
                    }
                } else {
                    val routerError: (String) -> Unit = { message ->
                        Toast.makeText(this@MainActivity, message, Toast.LENGTH_LONG).show()
                    }
                    GajuRouter(this@MainActivity, walletRepo, appStateManager, routerError)
                }
            }
        }
    }
}

enum class Screen {
    CREATE_WALLET,
    SETUP_CHOICE,
    MNEMONIC_RECOVERY,
    ACCOUNT_NAMING,
    DASHBOARD,
    QR_SCANNER,
    QR_RECEIVE,
    SETTINGS,
    MANAGE_ACCOUNT,
    MANAGE_WALLETS,
    DELETE_CONFIRM,
    VIEW_MNEMONIC,
    VERIFY_MNEMONIC,
    ENVIRONMENT_INFO,
    SEND_FORM,
    SEND_REVIEW,
    SIGN_REQUEST,
    FETCHING_SIGN_REQUEST,
    MANUAL_GRIDS_ENTRY,
    TRANSACTION_SUCCESS
}

enum class ScannerMode { SEND, GRIDS }

@Composable
fun GajuRouter(
    context: android.content.Context,
    walletRepo: WalletRepository,
    appStateManager: AppStateManager,
    onError: (String) -> Unit,
) {
    val appState by appStateManager.state.collectAsState()
    val scope = rememberCoroutineScope()
    
    val initialScreen = remember {
        val wallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
        if (wallets.isEmpty()) Screen.CREATE_WALLET else Screen.DASHBOARD
    }
    
    val screenStack = remember { mutableStateListOf(initialScreen) }
    val currentScreen = screenStack.last()

    fun navigateTo(screen: Screen) {
        screenStack.add(screen)
    }

    fun navigateBack() {
        if (screenStack.size > 1) {
            screenStack.removeAt(screenStack.size - 1)
        }
    }

    BackHandler(enabled = screenStack.size > 1) {
        navigateBack()
    }
    
    var pendingWalletName by rememberSaveable { mutableStateOf("") }
    var pendingNetworkId by rememberSaveable { mutableStateOf("groot.mainnet") }
    var pendingAccount by remember { mutableStateOf<Account?>(null) }
    var setupRecovered by rememberSaveable { mutableStateOf(value = false) }

    var selectedWalletId by rememberSaveable { mutableStateOf("") }
    var selectedAccountId by rememberSaveable { mutableStateOf("") }
    
    var deleteType by rememberSaveable { mutableStateOf("") }
    var deleteName by rememberSaveable { mutableStateOf("") }

    var viewMnemonicAccount by remember { mutableStateOf<Account?>(null) }
    var viewMnemonicPhrase by remember { mutableStateOf<Array<ByteArray>?>(null) }

    var verifyMnemonicAccount by remember { mutableStateOf<Account?>(null) }
    var verifyMnemonicPhrase by remember { mutableStateOf<Array<ByteArray>?>(null) }

    var activeWalletIndex by rememberSaveable { mutableIntStateOf(0) }
    var activeAccountIndex by rememberSaveable { mutableIntStateOf(0) }

    var currentScannerMode by rememberSaveable { mutableStateOf(ScannerMode.SEND) }

    var nodeStatusResult by remember { mutableStateOf<Map<String, Any?>?>(null) }
    var nodeStatusError by remember { mutableStateOf<String?>(null) }

    var pendingScanResult by remember { mutableStateOf<Grids.ParseResult?>(null) }
    
    var pendingRecipient by rememberSaveable { mutableStateOf("") }
    var pendingAmountStr by rememberSaveable { mutableStateOf("") }
    var pendingPayload by rememberSaveable { mutableStateOf("") }
    var pendingGas by rememberSaveable { mutableStateOf("") }
    var pendingGasPrice by rememberSaveable { mutableStateOf("") }
    var pendingTTL by rememberSaveable { mutableStateOf("") }

    var lastTxHash by rememberSaveable { mutableStateOf("") }
    var lastTxAmount by rememberSaveable { mutableStateOf("") }
    var lastTxRecipient by rememberSaveable { mutableStateOf("") }
    var lastTxPayload by rememberSaveable { mutableStateOf("") }
    var lastTxNetworkId by rememberSaveable { mutableStateOf("") }
    var lastTxSenderLabel by rememberSaveable { mutableStateOf("") }
    var lastTxWalletName by rememberSaveable { mutableStateOf("") }

    var pendingSignRequest by remember { mutableStateOf<GridsSignRequest?>(null) }
    var pendingSignUrl by rememberSaveable { mutableStateOf("") }
    var activeFetchJob by remember { mutableStateOf<Job?>(null) }

    fun processGridsUrl(url: String) {
        try {
            val result = Grids.parse(url)
            if (result.verb == Grids.Verb.SIGN) {
                pendingSignUrl = result.url
                navigateTo(Screen.FETCHING_SIGN_REQUEST)
                
                activeFetchJob = scope.launch {
                    try {
                        android.util.Log.i("GajuRouter", "Fetching GRIDS request from: ${result.url}")
                        @Suppress("UNCHECKED_CAST")
                        val response = withContext(Dispatchers.IO) {
                            Http.get(result.url) as? Map<String, Any?>
                        }
                        if (response != null) {
                            pendingSignRequest = response.toGridsSignRequest()
                            // Remove fetching screen and add request screen
                            screenStack.removeAt(screenStack.size - 1)
                            navigateTo(Screen.SIGN_REQUEST)
                        } else {
                            onError(context.getString(R.string.err_fetch_sig_failed))
                            navigateBack()
                        }
                    } catch (e: Exception) {
                        if (activeFetchJob?.isCancelled == false) {
                            onError(context.getString(R.string.err_fetch_failed, e.message ?: ""))
                            navigateBack()
                        }
                    } finally {
                        activeFetchJob = null
                    }
                }
            } else {
                pendingScanResult = result
                pendingRecipient = result.recipient ?: ""
                pendingAmountStr = if (result.amount != null) {
                    val type = GajuFormat.Type.valueOf(appState.balanceFormat)
                    val unit = GajuFormat.Unit.valueOf(appState.balanceUnit)
                    GajuFormat.amount(GajuFormat.standardSpec(type, unit), result.amount)
                } else ""
                pendingPayload = if (result.payload != null) String(result.payload, Charsets.UTF_8) else ""

                navigateTo(Screen.SEND_FORM)
            }
        } catch (e: Exception) {
            onError(context.getString(R.string.err_invalid_grids_url, e.message ?: ""))
        }
    }

    when (currentScreen) {
        Screen.CREATE_WALLET -> {
            CreateWalletScreen(
                onWalletCreated = { name, networkId ->
                    pendingWalletName = name
                    pendingNetworkId = networkId
                    selectedWalletId = ""
                    navigateTo(Screen.SETUP_CHOICE)
                },
                onBack = if (screenStack.size > 1) { { navigateBack() } } else null,
            )
        }
        Screen.SETUP_CHOICE -> {
            SetupChoiceScreen(
                onCreate = { 
                    try {
                        val masterKey = KeyManager.getMasterKey(context)
                        val phrase = AccountAirlock.generateMnemonic()
                        val account = AccountAirlock.createAccount("New Account", phrase, masterKey)
                        AccountAirlock.wipePhrase(phrase)
                        
                        pendingAccount = account
                        setupRecovered = false
                        navigateTo(Screen.ACCOUNT_NAMING)
                    } catch (e: Throwable) {
                        val msg = e.message ?: e.toString()
                        onError(context.getString(R.string.err_generate_account, msg))
                        android.util.Log.e("GajuRouter", "Account generation failed", e)
                    }
                },
                onRecover = { 
                    setupRecovered = true
                    navigateTo(Screen.MNEMONIC_RECOVERY)
                },
                onBack = { navigateBack() },
            )
        }
        Screen.MNEMONIC_RECOVERY -> {
            MnemonicRecoveryScreen(
                onMnemonicConfirmed = { phrase ->
                    try {
                        val masterKey = KeyManager.getMasterKey(context)
                        val account = AccountAirlock.createAccount("Recovered Account", phrase, masterKey)
                        AccountAirlock.wipePhrase(phrase)
                        
                        pendingAccount = account
                        navigateTo(Screen.ACCOUNT_NAMING)
                    } catch (e: Exception) {
                        onError(context.getString(R.string.err_recover_account, e.message ?: ""))
                    }
                },
                onBack = { navigateBack() },
            )
        }
        Screen.ACCOUNT_NAMING -> {
            val account = pendingAccount ?: return
            val akId = account.gajuId
            
            AccountNamingScreen(
                publicKey = akId,
                onAccountNamed = { name ->
                    val finalAccount = account.copy(label = name)
                    
                    if (selectedWalletId.isNotEmpty()) {
                        val wallet = walletRepo.loadWallet(selectedWalletId)
                        wallet?.let {
                            walletRepo.saveWallet(it.copy(accounts = it.accounts + finalAccount))
                        }
                    } else {
                        val endpoints = if (pendingNetworkId == "groot.mainnet") Wallet.DEFAULT_MAINNET else Wallet.DEFAULT_TESTNET
                        val wallet = Wallet(
                            id = UUID.randomUUID().toString(),
                            name = pendingWalletName.ifBlank { "My Wallet" },
                            networkId = pendingNetworkId,
                            endpoints = endpoints,
                            accounts = listOf(finalAccount),
                        )
                        walletRepo.saveWallet(wallet)
                    }
                    
                    if (!setupRecovered) {
                        appStateManager.markUnverified(finalAccount.id)
                    }
                    
                    selectedWalletId = ""
                    appStateManager.notifyWalletsChanged()
                    screenStack.clear()
                    screenStack.add(Screen.DASHBOARD)
                },
                onBack = { navigateBack() },
            )
        }
        Screen.VIEW_MNEMONIC -> {
            val acc = viewMnemonicAccount ?: return
            val phrase = viewMnemonicPhrase ?: return
            ViewMnemonicScreen(label = acc.label, phrase = phrase) {
                AccountAirlock.wipePhrase(phrase)
                viewMnemonicAccount = null
                viewMnemonicPhrase = null
                navigateBack()
            }
        }
        Screen.VERIFY_MNEMONIC -> {
            val acc = verifyMnemonicAccount ?: return
            val phrase = verifyMnemonicPhrase ?: return
            MnemonicVerifyScreen(
                label = acc.label,
                expectedPhrase = phrase,
                onVerified = {
                    appStateManager.clearVerified(acc.id)
                    AccountAirlock.wipePhrase(phrase)
                    verifyMnemonicAccount = null
                    verifyMnemonicPhrase = null
                    navigateBack()
                },
                onBack = {
                    AccountAirlock.wipePhrase(phrase)
                    verifyMnemonicAccount = null
                    verifyMnemonicPhrase = null
                    navigateBack()
                }
            )
        }
        Screen.DELETE_CONFIRM -> {
            DeleteConfirmationScreen(
                type = deleteType,
                name = deleteName,
                onConfirm = {
                    if (deleteType == "WALLET") {
                        walletRepo.deleteWallet(selectedWalletId)
                    } else {
                        walletRepo.deleteAccount(selectedWalletId, selectedAccountId)
                    }
                    appStateManager.notifyWalletsChanged()
                    // Pop back to Dashboard by removing the confirmation and the management screen
                    repeat(2) {
                        if (screenStack.size > 1) screenStack.removeAt(screenStack.size - 1)
                    }
                },
                onCancel = { navigateBack() }
            )
        }
        Screen.DASHBOARD -> {
            val wallets = remember(appState.walletsVersion) { 
                walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) } 
            }
            
            DashboardScreen(
                wallets = wallets,
                selectedWalletIndex = activeWalletIndex,
                onWalletIndexChange = { activeWalletIndex = it },
                selectedAccountIndex = activeAccountIndex,
                onAccountIndexChange = { activeAccountIndex = it },
                balanceFormat = GajuFormat.Type.valueOf(appState.balanceFormat),
                balanceUnit = GajuFormat.Unit.valueOf(appState.balanceUnit),
                onSend = { 
                    currentScannerMode = ScannerMode.SEND
                    pendingRecipient = ""
                    pendingAmountStr = ""
                    pendingPayload = ""
                    pendingGas = "20000"
                    pendingGasPrice = "1000000000"
                    pendingTTL = "500"
                    pendingScanResult = null
                    navigateTo(Screen.QR_SCANNER) 
                },
                onGrids = { 
                    currentScannerMode = ScannerMode.GRIDS
                    pendingRecipient = ""
                    pendingAmountStr = ""
                    pendingPayload = ""
                    pendingGas = "20000"
                    pendingGasPrice = "1000000000"
                    pendingTTL = "500"
                    pendingScanResult = null
                    navigateTo(Screen.QR_SCANNER) 
                },
                onReceive = { navigateTo(Screen.QR_RECEIVE) },
                onAddAccount = { walletId ->
                    selectedWalletId = walletId
                    navigateTo(Screen.SETUP_CHOICE)
                },
                onAccountSettings = { walletId, accountId ->
                    selectedWalletId = walletId
                    selectedAccountId = accountId
                    navigateTo(Screen.MANAGE_ACCOUNT)
                },
                onRefresh = { walletId, accountId ->
                    scope.launch {
                        val updated = walletRepo.refreshAccount(walletId, accountId)
                        if (updated != null) {
                            appStateManager.notifyWalletsChanged()
                        } else {
                            onError(context.getString(R.string.err_refresh_failed))
                        }
                    }
                },
                onNavigate = { dest ->
                    val screen = when (dest) {
                        "settings" -> Screen.SETTINGS
                        "environment_info" -> Screen.ENVIRONMENT_INFO
                        "manage_wallets" -> Screen.MANAGE_WALLETS
                        "CREATE_WALLET" -> Screen.CREATE_WALLET
                        "WIPE_DATA" -> {
                            walletRepo.listWallets().forEach { walletRepo.deleteWallet(it) }
                            appStateManager.notifyWalletsChanged()
                            navigateTo(Screen.CREATE_WALLET)
                            null
                        }
                        else -> null
                    }
                    screen?.let { navigateTo(it) }
                },
            )
        }
        Screen.MANAGE_WALLETS -> {
            val wallets = remember(appState.walletsVersion) {
                walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
            }
            ManageWalletsScreen(
                wallets = wallets,
                onAddWallet = {
                    selectedWalletId = ""
                    navigateTo(Screen.CREATE_WALLET)
                },
                onRenameWallet = { id, newName ->
                    val wallet = walletRepo.loadWallet(id)
                    wallet?.let { 
                        walletRepo.saveWallet(it.copy(name = newName))
                        appStateManager.notifyWalletsChanged()
                    }
                },
                onDeleteWallet = { id ->
                    val wallet = walletRepo.loadWallet(id)
                    wallet?.let {
                        selectedWalletId = id
                        deleteType = "WALLET"
                        deleteName = it.name
                        navigateTo(Screen.DELETE_CONFIRM)
                    }
                },
                onUpdateEndpoints = { id, endpoints ->
                    val wallet = walletRepo.loadWallet(id)
                    wallet?.let {
                        walletRepo.saveWallet(it.copy(endpoints = endpoints))
                        appStateManager.notifyWalletsChanged()
                    }
                },
                onAddEndpoint = { walletId, host, port, useTls ->
                    scope.launch {
                        try {
                            val wallet = walletRepo.loadWallet(walletId) ?: return@launch
                            val endpoint = swiss.qpq.gajumobile.data.models.Endpoint(host, port, useTls)
                            val client = NodeClient(listOf(NodeClient.Endpoint(host, port, useTls)))
                            
                            val status = withContext(Dispatchers.IO) { client.status() }
                            val reportedNetworkId = status["network_id"] as? String
                            
                            if ((reportedNetworkId != null) && (reportedNetworkId != wallet.networkId)) {
                                val allWallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
                                val targetWallet = allWallets.find { it.networkId == reportedNetworkId }
                                
                                if (targetWallet != null) {
                                    val targetEndpoints = targetWallet.endpoints + endpoint
                                    walletRepo.saveWallet(targetWallet.copy(endpoints = targetEndpoints))
                                    appStateManager.notifyWalletsChanged()
                                    onError(context.getString(R.string.err_network_mismatch_added, reportedNetworkId, targetWallet.name))
                                } else {
                                    nodeStatusError = context.getString(R.string.err_critical_mismatch_create, reportedNetworkId, wallet.networkId)
                                }
                            } else {
                                val newList = wallet.endpoints + endpoint
                                walletRepo.saveWallet(wallet.copy(endpoints = newList))
                                appStateManager.notifyWalletsChanged()
                                onError(context.getString(R.string.msg_endpoint_verified))
                            }
                        } catch (e: Exception) {
                            // Even if status check fails, we might still want to add it?
                            // User said: "We should test the endpoints and fall back to HTTP"
                            // If it fails completely, maybe don't add it or warn the user.
                            nodeStatusError = "Failed to verify endpoint: ${e.message}. It has not been added."
                        }
                    }
                },
                onCheckStatus = { walletId, index ->
                    scope.launch {
                        try {
                            val wallet = walletRepo.loadWallet(walletId) ?: return@launch
                            val endpoint = wallet.endpoints.getOrNull(index) ?: return@launch
                            val client = NodeClient(listOf(NodeClient.Endpoint(endpoint.host, endpoint.port, endpoint.useTls)))
                            
                            val status = withContext(Dispatchers.IO) { client.status() }
                            val reportedNetworkId = status["network_id"] as? String
                            
                            if ((reportedNetworkId != null) && (reportedNetworkId != wallet.networkId)) {
                                // Mismatch logic
                                val allWallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
                                val targetWallet = allWallets.find { it.networkId == reportedNetworkId }
                                
                                if (targetWallet != null) {
                                    // Move endpoint
                                    val sourceEndpoints = wallet.endpoints.toMutableList().apply { removeAt(index) }
                                    val targetEndpoints = targetWallet.endpoints + endpoint
                                    
                                    walletRepo.saveWallet(wallet.copy(endpoints = sourceEndpoints))
                                    walletRepo.saveWallet(targetWallet.copy(endpoints = targetEndpoints))
                                    
                                    appStateManager.notifyWalletsChanged()
                                    onError(context.getString(R.string.err_network_mismatch_moved, reportedNetworkId, targetWallet.name))
                                } else {
                                    // Warn for discard
                                    nodeStatusError = context.getString(R.string.err_critical_mismatch_fix, reportedNetworkId, wallet.networkId)
                                }
                            } else {
                                nodeStatusResult = status
                            }
                        } catch (e: Exception) {
                            nodeStatusError = e.message ?: "Unknown error"
                        }
                    }
                },
                statusResult = nodeStatusResult,
                statusError = nodeStatusError,
                onClearStatus = {
                    nodeStatusResult = null
                    nodeStatusError = null
                },
                onBack = { navigateBack() },
            )
        }
        Screen.MANAGE_ACCOUNT -> {
            val wallet = remember(selectedWalletId, appState.walletsVersion) {
                walletRepo.loadWallet(selectedWalletId)
            } ?: return
            val account = wallet.accounts.find { it.id == selectedAccountId } ?: return
            val isUnverified = appState.unverifiedAccountIds.contains(account.id)

            ManageAccountScreen(
                account = account,
                walletName = wallet.name,
                balanceFormat = GajuFormat.Type.valueOf(appState.balanceFormat),
                balanceUnit = GajuFormat.Unit.valueOf(appState.balanceUnit),
                isUnverified = isUnverified,
                onViewMnemonic = {
                    try {
                        val masterKey = KeyManager.getMasterKey(context)
                        val phrase = AccountAirlock.deriveMnemonic(account.privateKeyEnvelope, masterKey)
                        viewMnemonicAccount = account
                        viewMnemonicPhrase = phrase
                        navigateTo(Screen.VIEW_MNEMONIC)
                    } catch (e: Exception) {
                        onError(context.getString(R.string.err_auth_failed, e.message ?: ""))
                    }
                },
                onVerifyMnemonic = {
                    try {
                        val masterKey = KeyManager.getMasterKey(context)
                        val phrase = AccountAirlock.deriveMnemonic(account.privateKeyEnvelope, masterKey)
                        verifyMnemonicAccount = account
                        verifyMnemonicPhrase = phrase
                        navigateTo(Screen.VERIFY_MNEMONIC)
                    } catch (e: Exception) {
                        onError(context.getString(R.string.err_auth_failed, e.message ?: ""))
                    }
                },
                onRename = { newName ->
                    val updatedAccounts = wallet.accounts.map {
                        if (it.id == account.id) it.copy(label = newName) else it
                    }
                    walletRepo.saveWallet(wallet.copy(accounts = updatedAccounts))
                    appStateManager.notifyWalletsChanged()
                },
                onDelete = {
                    deleteType = "ACCOUNT"
                    deleteName = account.label
                    navigateTo(Screen.DELETE_CONFIRM)
                }
            )
        }
        Screen.SETTINGS -> {
            SettingsScreen(
                balanceFormat = GajuFormat.Type.valueOf(appState.balanceFormat),
                onFormatChange = { type ->
                    appStateManager.setBalanceFormat(type.name)
                },
                onNavigate = { dest ->
                    if (dest == "WIPE_DATA") {
                        walletRepo.listWallets().forEach { walletRepo.deleteWallet(it) }
                        appStateManager.notifyWalletsChanged()
                        screenStack.clear()
                        screenStack.add(Screen.CREATE_WALLET)
                    } else {
                        val screen = when (dest) {
                            "dashboard" -> Screen.DASHBOARD
                            "environment_info" -> Screen.ENVIRONMENT_INFO
                            "manage_wallets" -> Screen.MANAGE_WALLETS
                            else -> null
                        }
                        if (screen != null) {
                            screenStack.clear()
                            screenStack.add(screen)
                        }
                    }
                }
            )
        }
        Screen.ENVIRONMENT_INFO -> {
            EnvironmentInfoScreen(onBack = { navigateBack() })
        }
        Screen.FETCHING_SIGN_REQUEST -> {
            FetchingScreen(
                url = pendingSignUrl,
                onCancel = {
                    activeFetchJob?.cancel()
                    navigateBack()
                }
            )
        }
        Screen.QR_SCANNER -> {
            QRScannerScreen(
                onScanResult = { processGridsUrl(it) },
                onManualEntry = { 
                    if (currentScannerMode == ScannerMode.GRIDS) {
                        navigateTo(Screen.MANUAL_GRIDS_ENTRY)
                    } else {
                        pendingScanResult = null
                        navigateTo(Screen.SEND_FORM) 
                    }
                },
                onBack = { navigateBack() },
            )
        }
        Screen.MANUAL_GRIDS_ENTRY -> {
            ManualGridsEntryScreen(
                onConfirm = { url ->
                    // Remove manual entry screen and process result
                    screenStack.removeAt(screenStack.size - 1)
                    processGridsUrl(url)
                },
                onBack = { navigateBack() }
            )
        }
        Screen.SEND_FORM -> {
            val wallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
            val activeWallet = wallets.getOrNull(activeWalletIndex)
            val activeAccount = activeWallet?.accounts?.getOrNull(activeAccountIndex)
            
            if (activeAccount == null) {
                onError(context.getString(R.string.err_no_active_account))
                navigateBack()
                return
            }
            
            SendFormScreen(
                fromAccount = activeAccount,
                balanceFormat = GajuFormat.Type.valueOf(appState.balanceFormat),
                balanceUnit = GajuFormat.Unit.valueOf(appState.balanceUnit),
                initialRecipient = pendingRecipient,
                initialAmount = pendingAmountStr,
                initialPayload = pendingPayload,
                initialGas = pendingGas,
                initialGasPrice = pendingGasPrice,
                initialTTL = pendingTTL,
                onReview = { recipient, amountStr, payload, gas, gasPrice, ttl ->
                    pendingRecipient = recipient
                    pendingAmountStr = amountStr
                    pendingPayload = payload
                    pendingGas = gas
                    pendingGasPrice = gasPrice
                    pendingTTL = ttl
                    navigateTo(Screen.SEND_REVIEW)
                },
                onBack = { navigateBack() },
            )
        }
        Screen.SEND_REVIEW -> {
            val wallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
            val activeWallet = wallets.getOrNull(activeWalletIndex)
            val activeAccount = activeWallet?.accounts?.getOrNull(activeAccountIndex)

            if (activeAccount == null) {
                onError(context.getString(R.string.err_no_active_account))
                navigateBack()
                return
            }

            SendReviewScreen(
                senderLabel = activeAccount.label,
                walletName = activeWallet.name,
                networkId = activeWallet.networkId,
                recipientLabel = "Recipient", 
                recipientId = pendingRecipient,
                amountStr = pendingAmountStr,
                payload = pendingPayload,
                onSend = {
                    scope.launch {
                        try {
                            // 1. Get next nonce and current height
                            val (nonce, height) = withContext(Dispatchers.IO) {
                                val client = NodeClient(activeWallet.endpoints.map { NodeClient.Endpoint(it.host, it.port) })
                                client.nextNonce(activeAccount.gajuId) to client.topHeight()
                            }
                            
                            // 2. Derive seed from airlock
                            val masterKey = KeyManager.getMasterKey(context)
                            val seed = AccountAirlock.derivePrivateKey(activeAccount.privateKeyEnvelope, masterKey)
                            
                            // 3. Build and sign tx
                            val amountPucks = BigInteger(GajuFormat.read(pendingAmountStr))
                            val signedTx = TransactionService.buildSpendTx(
                                activeWallet.networkId,
                                activeAccount.gajuId,
                                pendingRecipient,
                                amountPucks,
                                BigInteger(pendingGasPrice),
                                BigInteger(pendingGas),
                                height + pendingTTL.toLong(),
                                nonce,
                                pendingPayload,
                                seed
                            )
                            
                            // 4. Wipe seed
                            GajuNative.memzero(seed)
                            
                            // 5. Post tx
                            val result = withContext(Dispatchers.IO) {
                                val client = NodeClient(activeWallet.endpoints.map { NodeClient.Endpoint(it.host, it.port) })
                                client.postTx(signedTx)
                            }
                            
                            val hash = (result["tx_hash"] ?: result["hash"]) as? String ?: "Unknown"
                            
                            lastTxHash = hash
                            lastTxAmount = GajuFormat.amount(
                                GajuFormat.standardSpec(
                                    GajuFormat.Type.valueOf(appState.balanceFormat),
                                    GajuFormat.Unit.valueOf(appState.balanceUnit)
                                ),
                                amountPucks
                            )
                            lastTxRecipient = pendingRecipient
                            lastTxPayload = pendingPayload
                            lastTxNetworkId = activeWallet.networkId
                            lastTxSenderLabel = activeAccount.label
                            lastTxWalletName = activeWallet.name
                            
                            // 6. Schedule a balance refresh in 3 seconds
                            scope.launch {
                                kotlinx.coroutines.delay(3.seconds)
                                walletRepo.refreshAccount(activeWallet.id, activeAccount.id)
                                appStateManager.notifyWalletsChanged()
                            }

                            // Success! Go to success screen
                            navigateTo(Screen.TRANSACTION_SUCCESS)
                        } catch (e: Throwable) {
                            val msg = e.message ?: e.toString()
                            onError(context.getString(R.string.err_send_failed, msg))
                            android.util.Log.e("GajuRouter", "Send failed", e)
                        }
                    }
                },
                onBack = { navigateBack() }
            )
        }
        Screen.SIGN_REQUEST -> {
            val wallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
            val activeWallet = wallets.getOrNull(activeWalletIndex)
            val activeAccount = activeWallet?.accounts?.getOrNull(activeAccountIndex)
            
            val request = pendingSignRequest ?: return
            
            // If request specifies an ID, try to find that account
            val signAccount = if (request.publicId != null && request.publicId != "false") {
                wallets.asSequence().flatMap { it.accounts }.find { it.gajuId == request.publicId }
            } else {
                activeAccount
            }
            
            if (signAccount == null) {
                onError(context.getString(R.string.err_no_matching_account))
                navigateBack()
                return
            }

            SignRequestScreen(
                request = request,
                originUrl = pendingSignUrl,
                accountLabel = signAccount.label,
                accountId = signAccount.gajuId,
                onSign = {
                    scope.launch {
                        try {
                            val masterKey = KeyManager.getMasterKey(context)
                            val signedRequest = mutableMapOf<String, Any?>()
                            
                            // Essential keys present in all GRIDS responses
                            signedRequest["grids"] = request.grids
                            signedRequest["chain"] = request.chain
                            signedRequest["network_id"] = request.networkId
                            signedRequest["type"] = request.type.toProtocolString()
                            signedRequest["public_id"] = signAccount.gajuId // Must be the actual ID string, never 'false'
                            
                            when (request.type) {
                                GridsSignType.MESSAGE -> {
                                    val sig = AccountAirlock.signMessage(signAccount.privateKeyEnvelope, masterKey, request.payload)
                                    signedRequest["payload"] = request.payload
                                    signedRequest["signature"] = android.util.Base64.encodeToString(sig, android.util.Base64.NO_WRAP)
                                }
                                GridsSignType.BINARY -> {
                                    val binary = swiss.qpq.gajumaru.core.encoding.ApiEncoder.decode(request.payload).payload()
                                    val sig = AccountAirlock.signBinary(signAccount.privateKeyEnvelope, masterKey, binary)
                                    signedRequest["payload"] = request.payload
                                    signedRequest["signature"] = android.util.Base64.encodeToString(sig, android.util.Base64.NO_WRAP)
                                }
                                GridsSignType.TX -> {
                                    val nid = request.networkId ?: activeWallet?.networkId ?: "groot.mainnet"
                                    val txBinary = swiss.qpq.gajumaru.core.encoding.ApiEncoder.decode(request.payload).payload()
                                    signedRequest["payload"] = AccountAirlock.signTx(signAccount.privateKeyEnvelope, masterKey, txBinary, nid)
                                    signedRequest["signed"] = true
                                }
                                else -> {
                                    throw Exception("Cannot sign unknown request type")
                                }
                            }
                            
                            android.util.Log.i("GajuRouter", "POSTing GRIDS response to $pendingSignUrl")
                            withContext(Dispatchers.IO) {
                                Http.post(pendingSignUrl, signedRequest)
                            }
                            
                            onError(context.getString(R.string.msg_sig_submitted))
                            screenStack.clear()
                            screenStack.add(Screen.DASHBOARD)
                        } catch (e: Throwable) {
                            val msg = e.message ?: e.toString()
                            onError(context.getString(R.string.err_sig_failed, msg))
                            android.util.Log.e("GajuRouter", "Signing failed", e)
                        }
                    }
                },
                onCancel = { navigateBack() }
            )
        }
        Screen.QR_RECEIVE -> {
            val wallets = walletRepo.listWallets().mapNotNull { walletRepo.loadWallet(it) }
            val activeWallet = wallets.getOrNull(activeWalletIndex)
            val activeAccount = activeWallet?.accounts?.getOrNull(activeAccountIndex)

            if (activeAccount == null) {
                onError(context.getString(R.string.err_no_active_account))
                navigateBack()
                return
            }

            QRReceiveScreen(
                account = activeAccount,
                networkId = activeWallet.networkId,
                balanceFormat = GajuFormat.Type.valueOf(appState.balanceFormat),
                balanceUnit = GajuFormat.Unit.valueOf(appState.balanceUnit),
                onDone = { navigateBack() },
            )
        }
        Screen.TRANSACTION_SUCCESS -> {
            TransactionSuccessScreen(
                amount = lastTxAmount,
                recipient = lastTxRecipient,
                payload = lastTxPayload,
                txHash = lastTxHash,
                senderLabel = lastTxSenderLabel,
                walletName = lastTxWalletName,
                networkId = lastTxNetworkId,
                onExit = {
                    screenStack.clear()
                    screenStack.add(Screen.DASHBOARD)
                }
            )
        }
    }
}
