package io.novafoundation.nova.feature_wallet_api.domain.model.xcm.dynamic

import io.novafoundation.nova.common.utils.graph.Edge
import io.novafoundation.nova.common.utils.graph.SimpleEdge
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.dynamic.DynamicCrossChainTransfersConfiguration.AssetTransfers
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.dynamic.DynamicCrossChainTransfersConfiguration.CustomTeleportEntry
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.dynamic.DynamicCrossChainTransfersConfiguration.TransferDestination
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.dynamic.reserve.XcmTransferType
import io.novafoundation.nova.feature_xcm_api.chain.XcmChain
import io.novafoundation.nova.runtime.ext.fullId
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
import io.novafoundation.nova.runtime.multiNetwork.chain.model.FullChainAssetId

fun DynamicCrossChainTransfersConfiguration.availableOutDestinations(origin: Chain.Asset): List<FullChainAssetId> {
    val assetTransfers = outComingAssetTransfers(origin.fullId) ?: return emptyList()
    return assetTransfers.destinations.map { it.fullChainAssetId }
}

fun DynamicCrossChainTransfersConfiguration.availableInDestinations(destination: Chain.Asset): List<FullChainAssetId> {
    val requiredDestinationId = destination.fullId

    return chains.flatMap { (originChainId, chainTransfers) ->
        chainTransfers.mapNotNull { originAssetTransfers ->
            val hasDestination = originAssetTransfers.destinations.any { it.fullChainAssetId == requiredDestinationId }

            if (hasDestination) {
                FullChainAssetId(originChainId, originAssetTransfers.assetId)
            } else {
                null
            }
        }
    }
}

fun DynamicCrossChainTransfersConfiguration.availableInDestinations(): List<Edge<FullChainAssetId>> {
    return chains.flatMap { (originChainId, chainTransfers) ->
        chainTransfers.flatMap { originAssetTransfers ->
            originAssetTransfers.destinations.map {
                val from = FullChainAssetId(originChainId, originAssetTransfers.assetId)
                val to = it.fullChainAssetId

                SimpleEdge(from, to)
            }
        }
    }
}

fun DynamicCrossChainTransfersConfiguration.transferFeatures(
    originAsset: FullChainAssetId,
    destinationChainId: ChainId
): DynamicCrossChainTransferFeatures? {
    return outComingAssetTransfers(originAsset)?.getDestination(destinationChainId)?.getTransferFeatures()
}

suspend fun DynamicCrossChainTransfersConfiguration.transferConfiguration(
    originXcmChain: XcmChain,
    originAsset: Chain.Asset,
    destinationXcmChain: XcmChain,
): DynamicCrossChainTransferConfiguration? {
    val destinationChain = destinationXcmChain.chain

    val assetTransfers = outComingAssetTransfers(originAsset.fullId) ?: return null
    val targetTransfer = assetTransfers.getDestination(destinationChain.id) ?: return null

    val reserve = reserveRegistry.getReserve(originAsset)

    return DynamicCrossChainTransferConfiguration(
        originChain = originXcmChain,
        destinationChain = destinationXcmChain,
        originChainAsset = originAsset,
        transferType = XcmTransferType.determineTransferType(
            usesTeleports = canUseTeleport(originXcmChain, originAsset, destinationXcmChain),
            originChain = originXcmChain,
            destinationChain = destinationXcmChain,
            reserve = reserve
        ),
        features = targetTransfer.getTransferFeatures(),
    )
}

private fun DynamicCrossChainTransfersConfiguration.canUseTeleport(
    originXcmChain: XcmChain,
    originAsset: Chain.Asset,
    destinationXcmChain: XcmChain,
): Boolean {
    val customTeleportEntry = CustomTeleportEntry(originAsset.fullId, destinationXcmChain.chain.id)
    if (customTeleportEntry in customTeleports) return true

    return XcmTransferType.isSystemTeleport(originXcmChain, destinationXcmChain)
}

private fun AssetTransfers.getDestination(destinationChainId: ChainId): TransferDestination? {
    return destinations.find { it.fullChainAssetId.chainId == destinationChainId }
}

private fun TransferDestination.getTransferFeatures(): DynamicCrossChainTransferFeatures {
    return DynamicCrossChainTransferFeatures(
        hasDeliveryFee = hasDeliveryFee,
        supportsXcmExecute = supportsXcmExecute,
    )
}

/**
 * @return null if transfer is unknown, true if delivery fee has to be paid, false otherwise
 */
fun DynamicCrossChainTransfersConfiguration.hasDeliveryFee(
    origin: FullChainAssetId,
    destination: FullChainAssetId
): Boolean? {
    val transfers = outComingAssetTransfers(origin) ?: return null
    val destinationConfig = transfers.destinations.find { it.fullChainAssetId == destination } ?: return null

    return destinationConfig.hasDeliveryFee
}

private fun DynamicCrossChainTransfersConfiguration.outComingAssetTransfers(origin: FullChainAssetId): AssetTransfers? {
    return chains[origin.chainId]?.find { it.assetId == origin.assetId }
}
