import Foundation
import SynorIbc

/// Synor IBC SDK Examples for Swift
///
/// Demonstrates Inter-Blockchain Communication operations:
/// - Cross-chain transfers and messages
/// - Channel lifecycle management
/// - Packet handling and acknowledgments
/// - Relayer operations
/// - Connection monitoring

@main
struct IbcExample {
    static func main() async throws {
        // Initialize client
        let config = IbcConfig(
            apiKey: ProcessInfo.processInfo.environment["SYNOR_API_KEY"] ?? "your-api-key",
            endpoint: "https://ibc.synor.io/v1",
            timeout: 30,
            retries: 3,
            debug: false,
            defaultChain: "synor-mainnet-1",
            confirmations: 1
        )

        let ibc = SynorIbc(config: config)

        do {
            // Check service health
            let healthy = try await ibc.healthCheck()
            print("Service healthy: \(healthy)\n")

            // Run examples
            try await chainsExample(ibc: ibc)
            try await channelsExample(ibc: ibc)
            try await transferExample(ibc: ibc)
            try await packetExample(ibc: ibc)
            try await relayerExample(ibc: ibc)
            try await monitoringExample(ibc: ibc)
        } catch {
            print("Error: \(error)")
        }

        await ibc.close()
    }

    static func chainsExample(ibc: SynorIbc) async throws {
        print("=== Chain Discovery ===")

        // Get all connected chains
        let chains = try await ibc.chains.list()
        print("Connected chains: \(chains.count)")

        for chain in chains.prefix(5) {
            print("\n  \(chain.chainId):")
            print("    Name: \(chain.name)")
            print("    Type: \(chain.chainType)")
            print("    Status: \(chain.status)")
            print("    Block height: \(chain.latestHeight)")
            print("    Light client: \(chain.lightClient)")
        }

        // Get specific chain info
        let synorChain = try await ibc.chains.get(chainId: "synor-mainnet-1")
        print("\nSynor chain details:")
        print("  Bech32 prefix: \(synorChain.bech32Prefix)")
        print("  Gas price: \(synorChain.gasPrice)")
        print("  Supported features: \(synorChain.features.joined(separator: ", "))")

        // Get chain connections
        let connections = try await ibc.chains.getConnections(chainId: "synor-mainnet-1")
        print("\nChain connections: \(connections.count)")
        for conn in connections.prefix(3) {
            print("  \(conn.connectionId) -> \(conn.counterpartyChainId)")
        }

        print()
    }

    static func channelsExample(ibc: SynorIbc) async throws {
        print("=== Channel Management ===")

        // List existing channels
        let channels = try await ibc.channels.list()
        print("Active channels: \(channels.count)")

        for channel in channels.prefix(3) {
            print("\n  Channel \(channel.channelId):")
            print("    Port: \(channel.portId)")
            print("    State: \(channel.state)")
            print("    Order: \(channel.ordering)")
            print("    Counterparty: \(channel.counterpartyChannelId) on \(channel.counterpartyChainId)")
            print("    Version: \(channel.version)")
        }

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

        // Step 1: ChanOpenInit
        let initResult = try await ibc.channels.openInit(
            ChannelInitRequest(
                portId: "transfer",
                counterpartyChainId: "cosmos-hub-4",
                counterpartyPortId: "transfer",
                version: "ics20-1",
                ordering: .unordered
            )
        )

        print("Channel init:")
        print("  Channel ID: \(initResult.channelId)")
        print("  State: \(initResult.state)")
        print("  TX hash: \(initResult.txHash)")

        // Step 2: Wait for ChanOpenTry (counterparty)
        print("\nWaiting for counterparty ChanOpenTry...")
        let tryState = try await ibc.channels.waitForState(
            channelId: initResult.channelId,
            state: .tryopen,
            timeout: 300 // 5 minutes
        )
        print("Channel state: \(tryState)")

        // Step 3: ChanOpenAck
        print("\nSending ChanOpenAck...")
        let ackResult = try await ibc.channels.openAck(
            ChannelAckRequest(
                channelId: initResult.channelId,
                counterpartyChannelId: "channel-0",
                counterpartyVersion: "ics20-1"
            )
        )
        print("Ack TX: \(ackResult.txHash)")

        // Step 4: Wait for ChanOpenConfirm (counterparty)
        print("\nWaiting for channel to open...")
        let openState = try await ibc.channels.waitForState(
            channelId: initResult.channelId,
            state: .open,
            timeout: 300
        )
        print("Channel is now: \(openState)")

        // Get channel details
        let channel = try await ibc.channels.get(channelId: initResult.channelId)
        print("\nChannel details:")
        print("  Sequences - Send: \(channel.nextSequenceSend), Recv: \(channel.nextSequenceRecv), Ack: \(channel.nextSequenceAck)")

        print()
    }

    static func transferExample(ibc: SynorIbc) async throws {
        print("=== Cross-Chain Transfers ===")

        // Get supported tokens for transfer
        let tokens = try await ibc.transfers.getSupportedTokens(targetChain: "cosmos-hub-4")
        print("Transferable tokens to Cosmos Hub:")
        for token in tokens.prefix(5) {
            print("  \(token.symbol) (\(token.denom))")
        }

        // Initiate a cross-chain transfer
        print("\nInitiating transfer...")
        let transfer = try await ibc.transfers.send(
            TransferRequest(
                sourceChannel: "channel-0",
                denom: "usynor",
                amount: "1000000",
                receiver: "cosmos1...",
                timeoutHeight: 0, // Use timestamp instead
                timeoutTimestamp: UInt64(Date().timeIntervalSince1970 + 600) * 1_000_000_000,
                memo: "IBC transfer from Synor"
            )
        )

        print("Transfer initiated:")
        print("  TX hash: \(transfer.txHash)")
        print("  Sequence: \(transfer.sequence)")
        print("  Packet ID: \(transfer.packetId)")
        print("  Status: \(transfer.status)")

        // Track transfer progress
        print("\nTracking transfer...")
        let status = try await ibc.transfers.track(packetId: transfer.packetId)
        print("Current status: \(status.state)")
        print("Source TX: \(status.sourceTxHash)")
        if let destTx = status.destTxHash {
            print("Dest TX: \(destTx)")
        }

        // Wait for completion
        print("\nWaiting for transfer completion...")
        let finalStatus = try await ibc.transfers.waitForCompletion(
            packetId: transfer.packetId,
            timeout: 600 // 10 minutes
        )

        print("Transfer completed:")
        print("  Final status: \(finalStatus.state)")
        print("  Acknowledgment: \(finalStatus.acknowledgment)")

        // Get transfer history
        let history = try await ibc.transfers.getHistory(limit: 10)
        print("\nRecent transfers: \(history.count)")
        for t in history.prefix(3) {
            print("  \(t.amount) \(t.denom) -> \(t.destChain) (\(t.status))")
        }

        print()
    }

    static func packetExample(ibc: SynorIbc) async throws {
        print("=== Packet Operations ===")

        // List pending packets
        let pending = try await ibc.packets.listPending(channelId: "channel-0")
        print("Pending packets on channel-0: \(pending.count)")

        for packet in pending.prefix(3) {
            print("\n  Packet #\(packet.sequence):")
            print("    Source: \(packet.sourcePort)/\(packet.sourceChannel)")
            print("    Dest: \(packet.destPort)/\(packet.destChannel)")
            print("    State: \(packet.state)")
            print("    Timeout: \(packet.timeoutTimestamp)")
        }

        // Get specific packet
        let packet = try await ibc.packets.get(channelId: "channel-0", sequence: 1)
        print("\nPacket details:")
        print("  Data (hex): \(packet.dataHex.prefix(40))...")
        print("  Created: \(packet.createdAt)")

        // Get packet commitment proof
        let proof = try await ibc.packets.getCommitmentProof(channelId: "channel-0", sequence: 1)
        print("\nCommitment proof:")
        print("  Height: \(proof.proofHeight)")
        print("  Proof size: \(proof.proof.count) bytes")

        // Get acknowledgment
        let ack = try await ibc.packets.getAcknowledgment(channelId: "channel-0", sequence: 1)
        print("\nAcknowledgment:")
        print("  Result: \(ack.success ? "Success" : "Error: \(ack.error ?? "unknown")")")
        print("  TX hash: \(ack.txHash)")

        // List timed-out packets
        let timedOut = try await ibc.packets.listTimedOut()
        print("\nTimed-out packets: \(timedOut.count)")

        // Timeout a packet manually
        if let toTimeout = timedOut.first {
            print("\nProcessing timeout for packet #\(toTimeout.sequence)")
            let timeout = try await ibc.packets.timeout(
                channelId: toTimeout.sourceChannel,
                sequence: toTimeout.sequence
            )
            print("Timeout TX: \(timeout.txHash)")
        }

        print()
    }

    static func relayerExample(ibc: SynorIbc) async throws {
        print("=== Relayer Operations ===")

        // Get relayer status
        let status = try await ibc.relayer.getStatus()
        print("Relayer status:")
        print("  Running: \(status.running)")
        print("  Uptime: \(status.uptime)")
        print("  Packets relayed: \(status.packetsRelayed)")
        print("  Errors: \(status.errorCount)")

        // List active paths
        let paths = try await ibc.relayer.listPaths()
        print("\nActive relay paths: \(paths.count)")

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

        // Configure a new path
        print("\nConfiguring new relay path...")
        let newPath = try await ibc.relayer.addPath(
            PathConfig(
                sourceChain: "synor-mainnet-1",
                destChain: "osmosis-1",
                sourceChannel: "channel-1",
                destChannel: "channel-100",
                filterDenoms: ["usynor", "uosmo"],
                minRelayAmount: "1000",
                maxRelayAmount: "1000000000"
            )
        )

        print("Path created: \(newPath.pathId)")

        // Start relaying on path
        print("\nStarting relayer on path...")
        try await ibc.relayer.startPath(pathId: newPath.pathId)
        print("Relayer started")

        // Manually relay pending packets
        print("\nRelaying pending packets...")
        let relayResult = try await ibc.relayer.relayPending(pathId: newPath.pathId)
        print("Relayed \(relayResult.packetCount) packets")
        print("TX hashes: \(relayResult.txHashes.count)")

        // Get relay history
        let history = try await ibc.relayer.getHistory(pathId: newPath.pathId, limit: 10)
        print("\nRelay history:")
        for event in history.prefix(3) {
            print("  \(event.timestamp): \(event.eventType) - \(event.packetCount) packets")
        }

        print()
    }

    static func monitoringExample(ibc: SynorIbc) async throws {
        print("=== IBC Monitoring ===")

        // Get IBC metrics
        let metrics = try await ibc.monitoring.getMetrics()
        print("IBC metrics:")
        print("  Total channels: \(metrics.totalChannels)")
        print("  Active channels: \(metrics.activeChannels)")
        print("  Total packets: \(metrics.totalPackets)")
        print("  Pending packets: \(metrics.pendingPackets)")
        print("  Failed packets: \(metrics.failedPackets)")
        print("  Avg relay time: \(metrics.avgRelayTime)ms")

        // Get chain health
        let chainHealth = try await ibc.monitoring.getChainHealth()
        print("\nChain health:")
        for health in chainHealth.prefix(3) {
            print("  \(health.chainId):")
            print("    Status: \(health.status)")
            print("    Block lag: \(health.blockLag)")
            print("    Last update: \(health.lastUpdate)")
        }

        // Get channel statistics
        let stats = try await ibc.monitoring.getChannelStats(channelId: "channel-0")
        print("\nChannel-0 statistics:")
        print("  Packets sent: \(stats.packetsSent)")
        print("  Packets received: \(stats.packetsReceived)")
        print("  Success rate: \(stats.successRate)%")
        print("  Avg confirmation time: \(stats.avgConfirmationTime)ms")

        // Subscribe to IBC events
        print("\nSubscribing to IBC events...")
        try await ibc.monitoring.subscribe { event in
            print("Event: \(event.type) on \(event.channelId)")
        }

        // Get alerts
        let alerts = try await ibc.monitoring.getAlerts()
        print("\nActive alerts: \(alerts.count)")
        for alert in alerts {
            print("  [\(alert.severity)] \(alert.message)")
        }

        print()
    }
}
