package io.novafoundation.nova;

import android.util.Log
import io.novafoundation.nova.common.address.AccountIdKey
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.nativeTransfer
import io.novafoundation.nova.metadata_shortener.MetadataShortener
import io.novafoundation.nova.runtime.ext.Geneses
import io.novafoundation.nova.runtime.ext.utilityAsset
import io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory
import io.novafoundation.nova.runtime.extrinsic.systemRemark
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novasama.substrate_sdk_android.encrypt.SignatureWrapper
import io.novasama.substrate_sdk_android.extensions.fromHex
import io.novasama.substrate_sdk_android.extensions.toHexString
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.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 org.junit.Test
import java.math.BigInteger

class MetadataShortenerTest : BaseIntegrationTest() {

    val extrinsicBuilderFactory = runtimeApi.provideExtrinsicBuilderFactory()

    val runtimeMetadata =
        "[encoded data, 608242 characters]"

    @Test
    fun shouldGenerateProofForExtrinsic() = runTest {
        val polkadot = chainRegistry.getChain(Chain.Geneses.POLKADOT)
        val runtimeMetadataBytes = runtimeMetadata.fromHex()

        val dot = polkadot.utilityAsset

        val specVersion = 1_002_000
        val specName = "polkadot"

        val signer = TestSigner { payload ->
            val signedExtras = payload.encodedExplicits()
            val additionalSigned = payload.encodedImplicits()

            Log.d("MetadataShortenerTest", "Signed extras: ${signedExtras.toHexString()}")
            Log.d("MetadataShortenerTest", "Additional signed: ${additionalSigned.toHexString()}")
            Log.d("MetadataShortenerTest", "Call: ${payload.encodedCall().toHexString()}")

            val proof = MetadataShortener.generate_extrinsic_proof(
                payload.encodedCall(),
                signedExtras,
                additionalSigned,
                runtimeMetadataBytes,
                specVersion,
                specName,
                polkadot.addressPrefix,
                dot.precision.value,
                dot.symbol.value
            )

            Log.d("MetadataShortenerTest", "Generated proof of size ${proof.size / 1024.0} KB: ${proof.toHexString()}")
        }

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

        extrinsicBuilder.nativeTransfer(accountId = signer.accountId, amount = BigInteger.ONE)
        extrinsicBuilder.systemRemark(remark = byteArrayOf(1, 2, 3))

        with(extrinsicBuilder) {
            signer.setSignerData(TestSigningContext(polkadot), SigningMode.SUBMISSION)
        }

        extrinsicBuilder.buildExtrinsic()
    }

    @Test
    fun shouldGenerateMetadataDigest() = runTest {
        val polkadot = chainRegistry.getChain(Chain.Geneses.POLKADOT)
        val dot = polkadot.utilityAsset

        val specVersion = 1_002_000
        val specName = "polkadot"

        val digest = MetadataShortener.generate_metadata_digest(
            runtimeMetadata.fromHex(),
            specVersion,
            specName,
            polkadot.addressPrefix,
            dot.precision.value,
            dot.symbol.value
        )

        Log.d("MetadataShortenerTest", "Metadata digest: ${digest.toHexString()}")
    }

    private inner class TestSigner(
        private val testPayload: (InheritedImplication) -> Unit
    ) : NovaSigner, GeneralTransactionSigner {

        val accountId = ByteArray(32) { 1 }

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

        override val metaAccount: MetaAccount = DefaultMetaAccount(
            id = 0,
            globallyUniqueId = "0",
            substrateAccountId = accountId,
            substrateCryptoType = null,
            substratePublicKey = null,
            ethereumAddress = null,
            ethereumPublicKey = null,
            isSelected = true,
            name = "test",
            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 {
            error("Not implemented")
        }

        context(ExtrinsicBuilder)
        override suspend fun setSignerDataForSubmission(context: SigningContext) {
            setNonce(BigInteger.ZERO)
            setVerifySignature(this, 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 {
            testPayload(inheritedImplication)

            return SignatureWrapper.Sr25519(ByteArray(64))
        }
    }

    private class TestSigningContext(override val chain: Chain) : SigningContext {
        override suspend fun getNonce(accountId: AccountIdKey): Nonce {
            return Nonce.ZERO
        }
    }
}


