package io.synor.examples

import io.synor.ibc.*
import io.synor.ibc.types.*
import kotlinx.coroutines.runBlocking
import java.time.Duration
import java.time.Instant

/**
 * Synor IBC SDK Examples for Kotlin
 *
 * Demonstrates Inter-Blockchain Communication operations:
 * - Cross-chain transfers and messages
 * - Channel lifecycle management
 * - Packet handling and acknowledgments
 * - Relayer operations
 * - Connection monitoring
 */
fun main() = runBlocking {
    // Initialize client
    val config = IbcConfig(
        apiKey = System.getenv("SYNOR_API_KEY") ?: "your-api-key",
        endpoint = "https://ibc.synor.io/v1",
        timeout = 30_000,
        retries = 3,
        debug = false,
        defaultChain = "synor-mainnet-1",
        confirmations = 1
    )

    val ibc = SynorIbc(config)

    try {
        // Check service health
        val healthy = ibc.healthCheck()
        println("Service healthy: $healthy\n")

        // Run examples
        chainsExample(ibc)
        channelsExample(ibc)
        transferExample(ibc)
        packetExample(ibc)
        relayerExample(ibc)
        monitoringExample(ibc)
    } finally {
        ibc.close()
    }
}

suspend fun chainsExample(ibc: SynorIbc) {
    println("=== Chain Discovery ===")

    // Get all connected chains
    val chains = ibc.chains.list()
    println("Connected chains: ${chains.size}")

    chains.take(5).forEach { chain ->
        println("\n  ${chain.chainId}:")
        println("    Name: ${chain.name}")
        println("    Type: ${chain.chainType}")
        println("    Status: ${chain.status}")
        println("    Block height: ${chain.latestHeight}")
        println("    Light client: ${chain.lightClient}")
    }

    // Get specific chain info
    val synorChain = ibc.chains.get("synor-mainnet-1")
    println("\nSynor chain details:")
    println("  Bech32 prefix: ${synorChain.bech32Prefix}")
    println("  Gas price: ${synorChain.gasPrice}")
    println("  Supported features: ${synorChain.features.joinToString(", ")}")

    // Get chain connections
    val connections = ibc.chains.getConnections("synor-mainnet-1")
    println("\nChain connections: ${connections.size}")
    connections.take(3).forEach { conn ->
        println("  ${conn.connectionId} -> ${conn.counterpartyChainId}")
    }

    println()
}

suspend fun channelsExample(ibc: SynorIbc) {
    println("=== Channel Management ===")

    // List existing channels
    val channels = ibc.channels.list()
    println("Active channels: ${channels.size}")

    channels.take(3).forEach { channel ->
        println("\n  Channel ${channel.channelId}:")
        println("    Port: ${channel.portId}")
        println("    State: ${channel.state}")
        println("    Order: ${channel.ordering}")
        println("    Counterparty: ${channel.counterpartyChannelId} on ${channel.counterpartyChainId}")
        println("    Version: ${channel.version}")
    }

    // Create a new channel (4-step handshake)
    println("\nInitiating channel creation...")

    // Step 1: ChanOpenInit
    val initResult = ibc.channels.openInit(
        ChannelInitRequest(
            portId = "transfer",
            counterpartyChainId = "cosmos-hub-4",
            counterpartyPortId = "transfer",
            version = "ics20-1",
            ordering = ChannelOrdering.UNORDERED
        )
    )

    println("Channel init:")
    println("  Channel ID: ${initResult.channelId}")
    println("  State: ${initResult.state}")
    println("  TX hash: ${initResult.txHash}")

    // Step 2: Wait for ChanOpenTry (counterparty)
    println("\nWaiting for counterparty ChanOpenTry...")
    val tryState = ibc.channels.waitForState(
        initResult.channelId,
        ChannelState.TRYOPEN,
        Duration.ofMinutes(5)
    )
    println("Channel state: $tryState")

    // Step 3: ChanOpenAck
    println("\nSending ChanOpenAck...")
    val ackResult = ibc.channels.openAck(
        ChannelAckRequest(
            channelId = initResult.channelId,
            counterpartyChannelId = "channel-0",
            counterpartyVersion = "ics20-1"
        )
    )
    println("Ack TX: ${ackResult.txHash}")

    // Step 4: Wait for ChanOpenConfirm (counterparty)
    println("\nWaiting for channel to open...")
    val openState = ibc.channels.waitForState(
        initResult.channelId,
        ChannelState.OPEN,
        Duration.ofMinutes(5)
    )
    println("Channel is now: $openState")

    // Get channel details
    val channel = ibc.channels.get(initResult.channelId)
    println("\nChannel details:")
    println("  Sequences - Send: ${channel.nextSequenceSend}, Recv: ${channel.nextSequenceRecv}, Ack: ${channel.nextSequenceAck}")

    println()
}

suspend fun transferExample(ibc: SynorIbc) {
    println("=== Cross-Chain Transfers ===")

    // Get supported tokens for transfer
    val tokens = ibc.transfers.getSupportedTokens("cosmos-hub-4")
    println("Transferable tokens to Cosmos Hub:")
    tokens.take(5).forEach { token ->
        println("  ${token.symbol} (${token.denom})")
    }

    // Initiate a cross-chain transfer
    println("\nInitiating transfer...")
    val transfer = ibc.transfers.send(
        TransferRequest(
            sourceChannel = "channel-0",
            denom = "usynor",
            amount = "1000000",
            receiver = "cosmos1...",
            timeoutHeight = 0, // Use timestamp instead
            timeoutTimestamp = Instant.now().plusSeconds(600).toEpochMilli() * 1_000_000L,
            memo = "IBC transfer from Synor"
        )
    )

    println("Transfer initiated:")
    println("  TX hash: ${transfer.txHash}")
    println("  Sequence: ${transfer.sequence}")
    println("  Packet ID: ${transfer.packetId}")
    println("  Status: ${transfer.status}")

    // Track transfer progress
    println("\nTracking transfer...")
    val status = ibc.transfers.track(transfer.packetId)
    println("Current status: ${status.state}")
    println("Source TX: ${status.sourceTxHash}")
    status.destTxHash?.let { println("Dest TX: $it") }

    // Wait for completion
    println("\nWaiting for transfer completion...")
    val finalStatus = ibc.transfers.waitForCompletion(
        transfer.packetId,
        Duration.ofMinutes(10)
    )

    println("Transfer completed:")
    println("  Final status: ${finalStatus.state}")
    println("  Acknowledgment: ${finalStatus.acknowledgment}")

    // Get transfer history
    val history = ibc.transfers.getHistory(limit = 10)
    println("\nRecent transfers: ${history.size}")
    history.take(3).forEach { t ->
        println("  ${t.amount} ${t.denom} -> ${t.destChain} (${t.status})")
    }

    println()
}

suspend fun packetExample(ibc: SynorIbc) {
    println("=== Packet Operations ===")

    // List pending packets
    val pending = ibc.packets.listPending("channel-0")
    println("Pending packets on channel-0: ${pending.size}")

    pending.take(3).forEach { packet ->
        println("\n  Packet #${packet.sequence}:")
        println("    Source: ${packet.sourcePort}/${packet.sourceChannel}")
        println("    Dest: ${packet.destPort}/${packet.destChannel}")
        println("    State: ${packet.state}")
        println("    Timeout: ${packet.timeoutTimestamp}")
    }

    // Get specific packet
    val packet = ibc.packets.get("channel-0", 1)
    println("\nPacket details:")
    println("  Data (hex): ${packet.dataHex.take(40)}...")
    println("  Created: ${packet.createdAt}")

    // Get packet commitment proof
    val proof = ibc.packets.getCommitmentProof("channel-0", 1)
    println("\nCommitment proof:")
    println("  Height: ${proof.proofHeight}")
    println("  Proof size: ${proof.proof.size} bytes")

    // Get acknowledgment
    val ack = ibc.packets.getAcknowledgment("channel-0", 1)
    println("\nAcknowledgment:")
    println("  Result: ${if (ack.success) "Success" else "Error: ${ack.error}"}")
    println("  TX hash: ${ack.txHash}")

    // List timed-out packets
    val timedOut = ibc.packets.listTimedOut()
    println("\nTimed-out packets: ${timedOut.size}")

    // Timeout a packet manually
    if (timedOut.isNotEmpty()) {
        val toTimeout = timedOut.first()
        println("\nProcessing timeout for packet #${toTimeout.sequence}")
        val timeout = ibc.packets.timeout(toTimeout.sourceChannel, toTimeout.sequence)
        println("Timeout TX: ${timeout.txHash}")
    }

    println()
}

suspend fun relayerExample(ibc: SynorIbc) {
    println("=== Relayer Operations ===")

    // Get relayer status
    val status = ibc.relayer.getStatus()
    println("Relayer status:")
    println("  Running: ${status.running}")
    println("  Uptime: ${status.uptime}")
    println("  Packets relayed: ${status.packetsRelayed}")
    println("  Errors: ${status.errorCount}")

    // List active paths
    val paths = ibc.relayer.listPaths()
    println("\nActive relay paths: ${paths.size}")

    paths.forEach { path ->
        println("\n  ${path.pathId}:")
        println("    ${path.sourceChain} <-> ${path.destChain}")
        println("    Channel: ${path.sourceChannel} <-> ${path.destChannel}")
        println("    Status: ${path.status}")
        println("    Pending packets: ${path.pendingPackets}")
    }

    // Configure a new path
    println("\nConfiguring new relay path...")
    val newPath = ibc.relayer.addPath(
        PathConfig(
            sourceChain = "synor-mainnet-1",
            destChain = "osmosis-1",
            sourceChannel = "channel-1",
            destChannel = "channel-100",
            filterDenoms = listOf("usynor", "uosmo"),
            minRelayAmount = "1000",
            maxRelayAmount = "1000000000"
        )
    )

    println("Path created: ${newPath.pathId}")

    // Start relaying on path
    println("\nStarting relayer on path...")
    ibc.relayer.startPath(newPath.pathId)
    println("Relayer started")

    // Manually relay pending packets
    println("\nRelaying pending packets...")
    val relayResult = ibc.relayer.relayPending(newPath.pathId)
    println("Relayed ${relayResult.packetCount} packets")
    println("TX hashes: ${relayResult.txHashes.size}")

    // Get relay history
    val history = ibc.relayer.getHistory(newPath.pathId, limit = 10)
    println("\nRelay history:")
    history.take(3).forEach { event ->
        println("  ${event.timestamp}: ${event.eventType} - ${event.packetCount} packets")
    }

    println()
}

suspend fun monitoringExample(ibc: SynorIbc) {
    println("=== IBC Monitoring ===")

    // Get IBC metrics
    val metrics = ibc.monitoring.getMetrics()
    println("IBC metrics:")
    println("  Total channels: ${metrics.totalChannels}")
    println("  Active channels: ${metrics.activeChannels}")
    println("  Total packets: ${metrics.totalPackets}")
    println("  Pending packets: ${metrics.pendingPackets}")
    println("  Failed packets: ${metrics.failedPackets}")
    println("  Avg relay time: ${metrics.avgRelayTime}ms")

    // Get chain health
    val chainHealth = ibc.monitoring.getChainHealth()
    println("\nChain health:")
    chainHealth.take(3).forEach { health ->
        println("  ${health.chainId}:")
        println("    Status: ${health.status}")
        println("    Block lag: ${health.blockLag}")
        println("    Last update: ${health.lastUpdate}")
    }

    // Get channel statistics
    val stats = ibc.monitoring.getChannelStats("channel-0")
    println("\nChannel-0 statistics:")
    println("  Packets sent: ${stats.packetsSent}")
    println("  Packets received: ${stats.packetsReceived}")
    println("  Success rate: ${stats.successRate}%")
    println("  Avg confirmation time: ${stats.avgConfirmationTime}ms")

    // Subscribe to IBC events
    println("\nSubscribing to IBC events...")
    ibc.monitoring.subscribe { event ->
        println("Event: ${event.type} on ${event.channelId}")
    }

    // Get alerts
    val alerts = ibc.monitoring.getAlerts()
    println("\nActive alerts: ${alerts.size}")
    alerts.forEach { alert ->
        println("  [${alert.severity}] ${alert.message}")
    }

    println()
}
