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

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <chrono>
#include <synor/ibc.hpp>

using namespace synor::ibc;

void chains_example(SynorIbc& ibc) {
    std::cout << "=== Chain Discovery ===" << std::endl;

    // Get all connected chains
    auto chains = ibc.chains().list();
    std::cout << "Connected chains: " << chains.size() << std::endl;

    for (size_t i = 0; i < std::min(chains.size(), size_t(5)); ++i) {
        const auto& chain = chains[i];
        std::cout << "\n  " << chain.chain_id() << ":" << std::endl;
        std::cout << "    Name: " << chain.name() << std::endl;
        std::cout << "    Type: " << chain.chain_type() << std::endl;
        std::cout << "    Status: " << chain.status() << std::endl;
        std::cout << "    Block height: " << chain.latest_height() << std::endl;
        std::cout << "    Light client: " << chain.light_client() << std::endl;
    }

    // Get specific chain info
    auto synor_chain = ibc.chains().get("synor-mainnet-1");
    std::cout << "\nSynor chain details:" << std::endl;
    std::cout << "  Bech32 prefix: " << synor_chain.bech32_prefix() << std::endl;
    std::cout << "  Gas price: " << synor_chain.gas_price() << std::endl;

    auto features = synor_chain.features();
    std::cout << "  Supported features: ";
    for (size_t i = 0; i < features.size(); ++i) {
        std::cout << features[i] << (i < features.size() - 1 ? ", " : "");
    }
    std::cout << std::endl;

    // Get chain connections
    auto connections = ibc.chains().get_connections("synor-mainnet-1");
    std::cout << "\nChain connections: " << connections.size() << std::endl;
    for (size_t i = 0; i < std::min(connections.size(), size_t(3)); ++i) {
        const auto& conn = connections[i];
        std::cout << "  " << conn.connection_id() << " -> " << conn.counterparty_chain_id() << std::endl;
    }

    std::cout << std::endl;
}

void channels_example(SynorIbc& ibc) {
    std::cout << "=== Channel Management ===" << std::endl;

    // List existing channels
    auto channels = ibc.channels().list();
    std::cout << "Active channels: " << channels.size() << std::endl;

    for (size_t i = 0; i < std::min(channels.size(), size_t(3)); ++i) {
        const auto& channel = channels[i];
        std::cout << "\n  Channel " << channel.channel_id() << ":" << std::endl;
        std::cout << "    Port: " << channel.port_id() << std::endl;
        std::cout << "    State: " << channel.state() << std::endl;
        std::cout << "    Order: " << channel.ordering() << std::endl;
        std::cout << "    Counterparty: " << channel.counterparty_channel_id()
                  << " on " << channel.counterparty_chain_id() << std::endl;
        std::cout << "    Version: " << channel.version() << std::endl;
    }

    // Create a new channel (4-step handshake)
    std::cout << "\nInitiating channel creation..." << std::endl;

    // Step 1: ChanOpenInit
    auto init_result = ibc.channels().open_init({
        .port_id = "transfer",
        .counterparty_chain_id = "cosmos-hub-4",
        .counterparty_port_id = "transfer",
        .version = "ics20-1",
        .ordering = ChannelOrdering::Unordered
    });

    std::cout << "Channel init:" << std::endl;
    std::cout << "  Channel ID: " << init_result.channel_id() << std::endl;
    std::cout << "  State: " << init_result.state() << std::endl;
    std::cout << "  TX hash: " << init_result.tx_hash() << std::endl;

    // Step 2: Wait for ChanOpenTry (counterparty)
    std::cout << "\nWaiting for counterparty ChanOpenTry..." << std::endl;
    auto try_state = ibc.channels().wait_for_state(
        init_result.channel_id(),
        ChannelState::TryOpen,
        std::chrono::minutes(5)
    );
    std::cout << "Channel state: " << try_state << std::endl;

    // Step 3: ChanOpenAck
    std::cout << "\nSending ChanOpenAck..." << std::endl;
    auto ack_result = ibc.channels().open_ack({
        .channel_id = init_result.channel_id(),
        .counterparty_channel_id = "channel-0",
        .counterparty_version = "ics20-1"
    });
    std::cout << "Ack TX: " << ack_result.tx_hash() << std::endl;

    // Step 4: Wait for ChanOpenConfirm (counterparty)
    std::cout << "\nWaiting for channel to open..." << std::endl;
    auto open_state = ibc.channels().wait_for_state(
        init_result.channel_id(),
        ChannelState::Open,
        std::chrono::minutes(5)
    );
    std::cout << "Channel is now: " << open_state << std::endl;

    // Get channel details
    auto channel = ibc.channels().get(init_result.channel_id());
    std::cout << "\nChannel details:" << std::endl;
    std::cout << "  Sequences - Send: " << channel.next_sequence_send()
              << ", Recv: " << channel.next_sequence_recv()
              << ", Ack: " << channel.next_sequence_ack() << std::endl;

    std::cout << std::endl;
}

void transfer_example(SynorIbc& ibc) {
    std::cout << "=== Cross-Chain Transfers ===" << std::endl;

    // Get supported tokens for transfer
    auto tokens = ibc.transfers().get_supported_tokens("cosmos-hub-4");
    std::cout << "Transferable tokens to Cosmos Hub:" << std::endl;
    for (size_t i = 0; i < std::min(tokens.size(), size_t(5)); ++i) {
        const auto& token = tokens[i];
        std::cout << "  " << token.symbol() << " (" << token.denom() << ")" << std::endl;
    }

    // Initiate a cross-chain transfer
    std::cout << "\nInitiating transfer..." << std::endl;
    auto now = std::chrono::system_clock::now();
    auto timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
        (now + std::chrono::seconds(600)).time_since_epoch()
    ).count();

    auto transfer = ibc.transfers().send({
        .source_channel = "channel-0",
        .denom = "usynor",
        .amount = "1000000",
        .receiver = "cosmos1...",
        .timeout_height = 0,  // Use timestamp instead
        .timeout_timestamp = static_cast<uint64_t>(timeout),
        .memo = "IBC transfer from Synor"
    });

    std::cout << "Transfer initiated:" << std::endl;
    std::cout << "  TX hash: " << transfer.tx_hash() << std::endl;
    std::cout << "  Sequence: " << transfer.sequence() << std::endl;
    std::cout << "  Packet ID: " << transfer.packet_id() << std::endl;
    std::cout << "  Status: " << transfer.status() << std::endl;

    // Track transfer progress
    std::cout << "\nTracking transfer..." << std::endl;
    auto status = ibc.transfers().track(transfer.packet_id());
    std::cout << "Current status: " << status.state() << std::endl;
    std::cout << "Source TX: " << status.source_tx_hash() << std::endl;
    if (!status.dest_tx_hash().empty()) {
        std::cout << "Dest TX: " << status.dest_tx_hash() << std::endl;
    }

    // Wait for completion
    std::cout << "\nWaiting for transfer completion..." << std::endl;
    auto final_status = ibc.transfers().wait_for_completion(
        transfer.packet_id(),
        std::chrono::minutes(10)
    );

    std::cout << "Transfer completed:" << std::endl;
    std::cout << "  Final status: " << final_status.state() << std::endl;
    std::cout << "  Acknowledgment: " << final_status.acknowledgment() << std::endl;

    // Get transfer history
    auto history = ibc.transfers().get_history(10);
    std::cout << "\nRecent transfers: " << history.size() << std::endl;
    for (size_t i = 0; i < std::min(history.size(), size_t(3)); ++i) {
        const auto& t = history[i];
        std::cout << "  " << t.amount() << " " << t.denom()
                  << " -> " << t.dest_chain() << " (" << t.status() << ")" << std::endl;
    }

    std::cout << std::endl;
}

void packet_example(SynorIbc& ibc) {
    std::cout << "=== Packet Operations ===" << std::endl;

    // List pending packets
    auto pending = ibc.packets().list_pending("channel-0");
    std::cout << "Pending packets on channel-0: " << pending.size() << std::endl;

    for (size_t i = 0; i < std::min(pending.size(), size_t(3)); ++i) {
        const auto& packet = pending[i];
        std::cout << "\n  Packet #" << packet.sequence() << ":" << std::endl;
        std::cout << "    Source: " << packet.source_port() << "/" << packet.source_channel() << std::endl;
        std::cout << "    Dest: " << packet.dest_port() << "/" << packet.dest_channel() << std::endl;
        std::cout << "    State: " << packet.state() << std::endl;
        std::cout << "    Timeout: " << packet.timeout_timestamp() << std::endl;
    }

    // Get specific packet
    auto packet = ibc.packets().get("channel-0", 1);
    std::cout << "\nPacket details:" << std::endl;
    std::cout << "  Data (hex): " << packet.data_hex().substr(0, 40) << "..." << std::endl;
    std::cout << "  Created: " << packet.created_at() << std::endl;

    // Get packet commitment proof
    auto proof = ibc.packets().get_commitment_proof("channel-0", 1);
    std::cout << "\nCommitment proof:" << std::endl;
    std::cout << "  Height: " << proof.proof_height() << std::endl;
    std::cout << "  Proof size: " << proof.proof().size() << " bytes" << std::endl;

    // Get acknowledgment
    auto ack = ibc.packets().get_acknowledgment("channel-0", 1);
    std::cout << "\nAcknowledgment:" << std::endl;
    std::cout << "  Result: " << (ack.success() ? "Success" : std::string("Error: ") + ack.error()) << std::endl;
    std::cout << "  TX hash: " << ack.tx_hash() << std::endl;

    // List timed-out packets
    auto timed_out = ibc.packets().list_timed_out();
    std::cout << "\nTimed-out packets: " << timed_out.size() << std::endl;

    // Timeout a packet manually
    if (!timed_out.empty()) {
        const auto& to_timeout = timed_out.front();
        std::cout << "\nProcessing timeout for packet #" << to_timeout.sequence() << std::endl;
        auto timeout = ibc.packets().timeout(to_timeout.source_channel(), to_timeout.sequence());
        std::cout << "Timeout TX: " << timeout.tx_hash() << std::endl;
    }

    std::cout << std::endl;
}

void relayer_example(SynorIbc& ibc) {
    std::cout << "=== Relayer Operations ===" << std::endl;

    // Get relayer status
    auto status = ibc.relayer().get_status();
    std::cout << "Relayer status:" << std::endl;
    std::cout << "  Running: " << (status.running() ? "true" : "false") << std::endl;
    std::cout << "  Uptime: " << status.uptime() << std::endl;
    std::cout << "  Packets relayed: " << status.packets_relayed() << std::endl;
    std::cout << "  Errors: " << status.error_count() << std::endl;

    // List active paths
    auto paths = ibc.relayer().list_paths();
    std::cout << "\nActive relay paths: " << paths.size() << std::endl;

    for (const auto& path : paths) {
        std::cout << "\n  " << path.path_id() << ":" << std::endl;
        std::cout << "    " << path.source_chain() << " <-> " << path.dest_chain() << std::endl;
        std::cout << "    Channel: " << path.source_channel() << " <-> " << path.dest_channel() << std::endl;
        std::cout << "    Status: " << path.status() << std::endl;
        std::cout << "    Pending packets: " << path.pending_packets() << std::endl;
    }

    // Configure a new path
    std::cout << "\nConfiguring new relay path..." << std::endl;
    auto new_path = ibc.relayer().add_path({
        .source_chain = "synor-mainnet-1",
        .dest_chain = "osmosis-1",
        .source_channel = "channel-1",
        .dest_channel = "channel-100",
        .filter_denoms = {"usynor", "uosmo"},
        .min_relay_amount = "1000",
        .max_relay_amount = "1000000000"
    });

    std::cout << "Path created: " << new_path.path_id() << std::endl;

    // Start relaying on path
    std::cout << "\nStarting relayer on path..." << std::endl;
    ibc.relayer().start_path(new_path.path_id());
    std::cout << "Relayer started" << std::endl;

    // Manually relay pending packets
    std::cout << "\nRelaying pending packets..." << std::endl;
    auto relay_result = ibc.relayer().relay_pending(new_path.path_id());
    std::cout << "Relayed " << relay_result.packet_count() << " packets" << std::endl;
    std::cout << "TX hashes: " << relay_result.tx_hashes().size() << std::endl;

    // Get relay history
    auto history = ibc.relayer().get_history(new_path.path_id(), 10);
    std::cout << "\nRelay history:" << std::endl;
    for (size_t i = 0; i < std::min(history.size(), size_t(3)); ++i) {
        const auto& event = history[i];
        std::cout << "  " << event.timestamp() << ": " << event.event_type()
                  << " - " << event.packet_count() << " packets" << std::endl;
    }

    std::cout << std::endl;
}

void monitoring_example(SynorIbc& ibc) {
    std::cout << "=== IBC Monitoring ===" << std::endl;

    // Get IBC metrics
    auto metrics = ibc.monitoring().get_metrics();
    std::cout << "IBC metrics:" << std::endl;
    std::cout << "  Total channels: " << metrics.total_channels() << std::endl;
    std::cout << "  Active channels: " << metrics.active_channels() << std::endl;
    std::cout << "  Total packets: " << metrics.total_packets() << std::endl;
    std::cout << "  Pending packets: " << metrics.pending_packets() << std::endl;
    std::cout << "  Failed packets: " << metrics.failed_packets() << std::endl;
    std::cout << "  Avg relay time: " << metrics.avg_relay_time() << "ms" << std::endl;

    // Get chain health
    auto chain_health = ibc.monitoring().get_chain_health();
    std::cout << "\nChain health:" << std::endl;
    for (size_t i = 0; i < std::min(chain_health.size(), size_t(3)); ++i) {
        const auto& health = chain_health[i];
        std::cout << "  " << health.chain_id() << ":" << std::endl;
        std::cout << "    Status: " << health.status() << std::endl;
        std::cout << "    Block lag: " << health.block_lag() << std::endl;
        std::cout << "    Last update: " << health.last_update() << std::endl;
    }

    // Get channel statistics
    auto stats = ibc.monitoring().get_channel_stats("channel-0");
    std::cout << "\nChannel-0 statistics:" << std::endl;
    std::cout << "  Packets sent: " << stats.packets_sent() << std::endl;
    std::cout << "  Packets received: " << stats.packets_received() << std::endl;
    std::cout << "  Success rate: " << stats.success_rate() << "%" << std::endl;
    std::cout << "  Avg confirmation time: " << stats.avg_confirmation_time() << "ms" << std::endl;

    // Subscribe to IBC events
    std::cout << "\nSubscribing to IBC events..." << std::endl;
    ibc.monitoring().subscribe([](const IbcEvent& event) {
        std::cout << "Event: " << event.type() << " on " << event.channel_id() << std::endl;
    });

    // Get alerts
    auto alerts = ibc.monitoring().get_alerts();
    std::cout << "\nActive alerts: " << alerts.size() << std::endl;
    for (const auto& alert : alerts) {
        std::cout << "  [" << alert.severity() << "] " << alert.message() << std::endl;
    }

    std::cout << std::endl;
}

int main(int argc, char** argv) {
    // Initialize client
    const char* api_key = std::getenv("SYNOR_API_KEY");
    IbcConfig config{
        .api_key = api_key ? api_key : "your-api-key",
        .endpoint = "https://ibc.synor.io/v1",
        .timeout = std::chrono::seconds(30),
        .retries = 3,
        .debug = false,
        .default_chain = "synor-mainnet-1",
        .confirmations = 1
    };

    SynorIbc ibc(config);

    try {
        // Check service health
        bool healthy = ibc.health_check();
        std::cout << "Service healthy: " << (healthy ? "true" : "false") << std::endl << std::endl;

        // Run examples
        chains_example(ibc);
        channels_example(ibc);
        transfer_example(ibc);
        packet_example(ibc);
        relayer_example(ibc);
        monitoring_example(ibc);
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
