package io.novafoundation.nova

import android.content.Context
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.address.AccountIdKey
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.deriveSeed32
import io.novafoundation.nova.feature_account_api.data.signer.CallExecutionType
import io.novafoundation.nova.feature_account_api.data.signer.NovaSigner
import io.novafoundation.nova.feature_account_api.data.signer.SigningContext
import io.novafoundation.nova.feature_account_api.data.signer.SigningMode
import io.novafoundation.nova.feature_account_api.data.signer.SubmissionHierarchy
import io.novafoundation.nova.feature_account_api.data.signer.setSignerData
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.TransferMode
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.nativeTransfer
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.runtime.ext.Geneses
import io.novafoundation.nova.runtime.ext.requireGenesisHash
import io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory
import io.novasama.substrate_sdk_android.extensions.fromHex
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
import io.novafoundation.nova.runtime.network.rpc.RpcCalls
import io.novasama.substrate_sdk_android.encrypt.EncryptionType
import io.novasama.substrate_sdk_android.encrypt.MultiChainEncryption
import io.novasama.substrate_sdk_android.encrypt.SignatureWrapper
import io.novasama.substrate_sdk_android.encrypt.keypair.Keypair
import io.novasama.substrate_sdk_android.encrypt.keypair.substrate.SubstrateKeypairFactory
import io.novasama.substrate_sdk_android.encrypt.seed.substrate.SubstrateSeedFactory
import io.novasama.substrate_sdk_android.runtime.AccountId
import io.novasama.substrate_sdk_android.runtime.extrinsic.BatchMode
import io.novasama.substrate_sdk_android.runtime.extrinsic.Nonce
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.KeyPairSigner
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedRaw
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadRaw
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.InheritedImplication
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.CheckNonce.Companion.setNonce
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.GeneralTransactionSigner
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.VerifySignature.Companion.setVerifySignature
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.signingPayload
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
import io.novafoundation.nova.sr25519.BizinikiwSr25519
import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
import org.junit.Test
import java.math.BigInteger

/**
 * LIVE TRANSFER TEST - Transfers real HEZ tokens on Pezkuwi mainnet!
 *
 * Sender: 5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w
 * Recipient: 5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV
 * Amount: 5 HEZ
 *
 * Run with: ./gradlew :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.novafoundation.nova.PezkuwiLiveTransferTest
 */
class PezkuwiLiveTransferTest : BaseIntegrationTest() {

    companion object {
        // Test wallet mnemonic
        private const val TEST_MNEMONIC = "crucial surge north silly divert throw habit fury zebra fabric tank output"

        // Sender address (derived from mnemonic)
        private const val SENDER_ADDRESS = "5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w"

        // Recipient address
        private const val RECIPIENT_ADDRESS = "5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV"

        // Amount: 5 HEZ (with 12 decimals)
        private val TRANSFER_AMOUNT = BigInteger("5000000000000") // 5 * 10^12
    }

    private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
        ApplicationProvider.getApplicationContext<Context>(),
        WalletFeatureApi::class.java
    )

    private val extrinsicBuilderFactory = runtimeApi.provideExtrinsicBuilderFactory()
    private val rpcCalls = runtimeApi.rpcCalls()

    /**
     * LIVE TEST: Build and submit a real transfer on Pezkuwi mainnet
     */
    @Test(timeout = 120000) // 2 minute timeout
    fun testLiveTransfer5HEZ() = runTest {
        Log.d("LiveTransferTest", "=== STARTING LIVE TRANSFER TEST ===")
        Log.d("LiveTransferTest", "Sender: $SENDER_ADDRESS")
        Log.d("LiveTransferTest", "Recipient: $RECIPIENT_ADDRESS")
        Log.d("LiveTransferTest", "Amount: 5 HEZ")

        // Request full sync for Pezkuwi chain specifically
        Log.d("LiveTransferTest", "Requesting full sync for Pezkuwi chain...")
        chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)

        val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
        Log.d("LiveTransferTest", "Chain: ${chain.name}")

        // Create keypair from mnemonic
        val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
        Log.d("LiveTransferTest", "Keypair created, public key: ${keypair.publicKey.toHexString()}")

        // Create signer
        val signer = RealSigner(keypair, chain)
        Log.d("LiveTransferTest", "Signer created")

        // Get recipient account ID
        val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()
        Log.d("LiveTransferTest", "Recipient AccountId: ${recipientAccountId.toHexString()}")

        // Get current nonce using sender's SS58 address
        val nonce = try {
            rpcCalls.getNonce(chain.id, SENDER_ADDRESS)
        } catch (e: Exception) {
            Log.e("LiveTransferTest", "Failed to get nonce, using 0", e)
            BigInteger.ZERO
        }
        Log.d("LiveTransferTest", "Current nonce: $nonce")

        // Create extrinsic builder
        val builder = extrinsicBuilderFactory.create(
            chain = chain,
            options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
        )
        Log.d("LiveTransferTest", "ExtrinsicBuilder created")

        // Use default MORTAL era (same as @pezkuwi/api)
        Log.d("LiveTransferTest", "Using MORTAL era (default, same as @pezkuwi/api)")

        // Add transfer call with KEEP_ALIVE mode (same as @pezkuwi/api uses)
        builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT, mode = TransferMode.KEEP_ALIVE)
        Log.d("LiveTransferTest", "Transfer call added")

        // Set signer data for SUBMISSION (this is where TypeReference errors occur!)
        try {
            with(builder) {
                signer.setSignerData(RealSigningContext(chain, nonce), SigningMode.SUBMISSION)
            }
            Log.d("LiveTransferTest", "Signer data set successfully")
        } catch (e: Exception) {
            Log.e("LiveTransferTest", "FAILED to set signer data!", e)
            fail("Failed to set signer data: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
            return@runTest
        }

        // Build the extrinsic
        val extrinsic = try {
            builder.buildExtrinsic()
        } catch (e: Exception) {
            Log.e("LiveTransferTest", "FAILED to build extrinsic!", e)
            fail("Failed to build extrinsic: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
            return@runTest
        }

        assertNotNull("Extrinsic should not be null", extrinsic)
        Log.d("LiveTransferTest", "Extrinsic built: ${extrinsic.extrinsicHex}")

        // Submit the extrinsic
        Log.d("LiveTransferTest", "Submitting extrinsic to network...")
        try {
            val hash = rpcCalls.submitExtrinsic(chain.id, extrinsic)
            Log.d("LiveTransferTest", "=== TRANSFER SUBMITTED SUCCESSFULLY ===")
            Log.d("LiveTransferTest", "Transaction hash: $hash")
            println("LIVE TRANSFER SUCCESS! TX Hash: $hash")
        } catch (e: Exception) {
            Log.e("LiveTransferTest", "FAILED to submit extrinsic!", e)
            fail("Failed to submit extrinsic: ${e.message}")
        }
    }

    /**
     * Test to check type resolution in the runtime
     */
    @Test(timeout = 120000)
    fun testTypeResolution() = runTest {
        Log.d("LiveTransferTest", "=== TESTING TYPE RESOLUTION ===")

        // Request full sync for Pezkuwi chain
        chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
        val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
        val runtime = chainRegistry.getRuntime(chain.id)

        // Check critical types for extrinsic encoding
        val typesToCheck = listOf(
            "Address",
            "MultiAddress",
            "GenericMultiAddress",
            "ExtrinsicSignature",
            "MultiSignature",
            "pezsp_runtime::multiaddress::MultiAddress",
            "pezsp_runtime::MultiSignature",
            "pezsp_runtime.multiaddress.MultiAddress",
            "pezsp_runtime.MultiSignature",
            "GenericExtrinsic",
            "Extrinsic"
        )

        val results = mutableListOf<String>()
        for (typeName in typesToCheck) {
            val type = runtime.typeRegistry[typeName]
            val resolved = type?.let {
                try {
                    // Try to get the actual type, not just alias
                    it.toString()
                } catch (e: Exception) {
                    "ERROR: ${e.message}"
                }
            }
            val status = if (type != null) "FOUND: $resolved" else "MISSING"
            results.add("  $typeName: $status")
            Log.d("LiveTransferTest", "$typeName: $status")
        }

        // Check if extrinsic signature type is defined in metadata
        val extrinsicMeta = runtime.metadata.extrinsic
        Log.d("LiveTransferTest", "Extrinsic version: ${extrinsicMeta.version}")
        Log.d("LiveTransferTest", "Signed extensions: ${extrinsicMeta.signedExtensions.map { it.id }}")

        // Log signed extension IDs
        for (ext in extrinsicMeta.signedExtensions) {
            Log.d("LiveTransferTest", "Extension: ${ext.id}")
        }

        // Just log the extension names - type access might be restricted
        Log.d("LiveTransferTest", "Signed extensions count: ${extrinsicMeta.signedExtensions.size}")

        println("Type resolution results:\n${results.joinToString("\n")}")
    }

    /**
     * Test fee calculation (doesn't submit, just builds for fee estimation)
     */
    @Test(timeout = 120000)
    fun testFeeCalculation() = runTest {
        Log.d("LiveTransferTest", "=== TESTING FEE CALCULATION ===")

        // Request full sync for Pezkuwi chain
        chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
        val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)

        // First, log type registry state
        val runtime = chainRegistry.getRuntime(chain.id)
        Log.d("LiveTransferTest", "TypeRegistry has ExtrinsicSignature: ${runtime.typeRegistry["ExtrinsicSignature"] != null}")
        Log.d("LiveTransferTest", "TypeRegistry has MultiSignature: ${runtime.typeRegistry["MultiSignature"] != null}")
        Log.d("LiveTransferTest", "TypeRegistry has Address: ${runtime.typeRegistry["Address"] != null}")
        Log.d("LiveTransferTest", "TypeRegistry has MultiAddress: ${runtime.typeRegistry["MultiAddress"] != null}")

        val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
        val signer = RealSigner(keypair, chain)
        val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()

        val builder = extrinsicBuilderFactory.create(
            chain = chain,
            options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
        )

        builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT)

        // Set signer data for FEE mode
        try {
            with(builder) {
                signer.setSignerData(RealSigningContext(chain, BigInteger.ZERO), SigningMode.FEE)
            }
            Log.d("LiveTransferTest", "Signer data set, building extrinsic...")
            val extrinsic = builder.buildExtrinsic()
            assertNotNull("Fee extrinsic should not be null", extrinsic)
            Log.d("LiveTransferTest", "Extrinsic built, getting hex...")

            // The error happens when accessing extrinsicHex
            try {
                val hex = extrinsic.extrinsicHex
                Log.d("LiveTransferTest", "Fee extrinsic built: $hex")
                println("Fee calculation test PASSED!")
            } catch (e: Exception) {
                Log.e("LiveTransferTest", "FAILED accessing extrinsicHex!", e)
                fail("Failed to get extrinsic hex: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
            }
        } catch (e: Exception) {
            Log.e("LiveTransferTest", "Fee calculation FAILED!", e)
            fail("Fee calculation failed: ${e.message}\nCause: ${e.cause?.message}")
        }
    }

    // Helper to create keypair from mnemonic
    private fun createKeypairFromMnemonic(mnemonic: String): Keypair {
        val seedResult = SubstrateSeedFactory.deriveSeed32(mnemonic, password = null)
        return SubstrateKeypairFactory.generate(EncryptionType.SR25519, seedResult.seed)
    }

    // Real signer using actual keypair with bizinikiwi context
    private inner class RealSigner(
        private val keypair: Keypair,
        private val chain: Chain
    ) : NovaSigner, GeneralTransactionSigner {

        val accountId: ByteArray = keypair.publicKey

        // Generate proper 96-byte keypair using BizinikiwSr25519 native library
        // This gives us the correct 64-byte secret key format for signing
        private val bizinikiwKeypair: ByteArray by lazy {
            val seedResult = SubstrateSeedFactory.deriveSeed32(TEST_MNEMONIC, password = null)
            BizinikiwSr25519.keypairFromSeed(seedResult.seed)
        }

        // Extract 64-byte secret key (32-byte scalar + 32-byte nonce)
        private val bizinikiwSecretKey: ByteArray by lazy {
            BizinikiwSr25519.secretKeyFromKeypair(bizinikiwKeypair)
        }

        // Extract 32-byte public key
        private val bizinikiwPublicKey: ByteArray by lazy {
            BizinikiwSr25519.publicKeyFromKeypair(bizinikiwKeypair)
        }

        private val keyPairSigner = KeyPairSigner(
            keypair,
            MultiChainEncryption.Substrate(EncryptionType.SR25519)
        )

        override suspend fun callExecutionType(): CallExecutionType {
            return CallExecutionType.IMMEDIATE
        }

        override val metaAccount: MetaAccount = DefaultMetaAccount(
            id = 0,
            globallyUniqueId = "test-wallet",
            substrateAccountId = accountId,
            substrateCryptoType = null,
            substratePublicKey = keypair.publicKey,
            ethereumAddress = null,
            ethereumPublicKey = null,
            isSelected = true,
            name = "Test Wallet",
            type = LightMetaAccount.Type.SECRETS,
            chainAccounts = emptyMap(),
            status = LightMetaAccount.Status.ACTIVE,
            parentMetaId = null
        )

        override suspend fun getSigningHierarchy(): SubmissionHierarchy {
            return SubmissionHierarchy(metaAccount, CallExecutionType.IMMEDIATE)
        }

        override suspend fun signRaw(payload: SignerPayloadRaw): SignedRaw {
            return keyPairSigner.signRaw(payload)
        }

        context(ExtrinsicBuilder)
        override suspend fun setSignerDataForSubmission(context: SigningContext) {
            val nonce = context.getNonce(AccountIdKey(accountId))
            setNonce(nonce)
            setVerifySignature(this@RealSigner, accountId)
        }

        context(ExtrinsicBuilder)
        override suspend fun setSignerDataForFee(context: SigningContext) {
            setSignerDataForSubmission(context)
        }

        override suspend fun submissionSignerAccountId(chain: Chain): AccountId {
            return accountId
        }

        override suspend fun maxCallsPerTransaction(): Int? {
            return null
        }

        override suspend fun signInheritedImplication(
            inheritedImplication: InheritedImplication,
            accountId: AccountId
        ): SignatureWrapper {
            // Get the SDK's signing payload (SCALE format - same as @pezkuwi/api)
            val sdkPayloadBytes = inheritedImplication.signingPayload()

            Log.d("LiveTransferTest", "=== SIGNING PAYLOAD (SDK - SCALE) ===")
            Log.d("LiveTransferTest", "SDK Payload hex: ${sdkPayloadBytes.toHexString()}")
            Log.d("LiveTransferTest", "SDK Payload length: ${sdkPayloadBytes.size} bytes")

            // Debug: show first bytes to verify format
            if (sdkPayloadBytes.size >= 42) {
                val callData = sdkPayloadBytes.copyOfRange(0, 42)
                val extensions = sdkPayloadBytes.copyOfRange(42, sdkPayloadBytes.size)
                Log.d("LiveTransferTest", "Call data (42 bytes): ${callData.toHexString()}")
                Log.d("LiveTransferTest", "Extensions (${extensions.size} bytes): ${extensions.toHexString()}")
            }

            // Use BizinikiwSr25519 native library with "bizinikiwi" signing context
            Log.d("LiveTransferTest", "=== USING BIZINIKIWI CONTEXT ===")
            Log.d("LiveTransferTest", "Bizinikiwi public key: ${bizinikiwPublicKey.toHexString()}")
            Log.d("LiveTransferTest", "Bizinikiwi secret key size: ${bizinikiwSecretKey.size} bytes")

            val signatureBytes = BizinikiwSr25519.sign(
                publicKey = bizinikiwPublicKey,
                secretKey = bizinikiwSecretKey,
                message = sdkPayloadBytes
            )

            Log.d("LiveTransferTest", "=== SIGNATURE PRODUCED ===")
            Log.d("LiveTransferTest", "Signature bytes: ${signatureBytes.toHexString()}")
            Log.d("LiveTransferTest", "Signature length: ${signatureBytes.size} bytes")

            // Verify the signature locally before sending
            val verifyResult = BizinikiwSr25519.verify(signatureBytes, sdkPayloadBytes, bizinikiwPublicKey)
            Log.d("LiveTransferTest", "Local verification: $verifyResult")

            return SignatureWrapper.Sr25519(signatureBytes)
        }
    }

    private class RealSigningContext(
        override val chain: Chain,
        private val nonceValue: BigInteger
    ) : SigningContext {
        override suspend fun getNonce(accountId: AccountIdKey): Nonce {
            return Nonce.ZERO + nonceValue
        }
    }

    private fun ByteArray.toHexString(): String {
        return joinToString("") { "%02x".format(it) }
    }
}
