/**
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <synor/ibc.h>

void chains_example(synor_ibc_t* ibc) {
    printf("=== Chain Discovery ===\n");

    synor_error_t err;

    // Get all connected chains
    synor_chain_list_t* chains = NULL;
    err = synor_chains_list(ibc, &chains);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to get chains\n");
        return;
    }

    printf("Connected chains: %zu\n", synor_chain_list_count(chains));

    for (size_t i = 0; i < 5 && i < synor_chain_list_count(chains); i++) {
        synor_chain_t* chain = synor_chain_list_get(chains, i);
        printf("\n  %s:\n", synor_chain_get_id(chain));
        printf("    Name: %s\n", synor_chain_get_name(chain));
        printf("    Type: %s\n", synor_chain_get_type(chain));
        printf("    Status: %s\n", synor_chain_get_status(chain));
        printf("    Block height: %lu\n", synor_chain_get_latest_height(chain));
        printf("    Light client: %s\n", synor_chain_get_light_client(chain));
    }
    synor_chain_list_free(chains);

    // Get specific chain info
    synor_chain_t* synor_chain = NULL;
    err = synor_chains_get(ibc, "synor-mainnet-1", &synor_chain);
    if (err == SYNOR_OK) {
        printf("\nSynor chain details:\n");
        printf("  Bech32 prefix: %s\n", synor_chain_get_bech32_prefix(synor_chain));
        printf("  Gas price: %s\n", synor_chain_get_gas_price(synor_chain));

        char** features;
        size_t feature_count;
        synor_chain_get_features(synor_chain, &features, &feature_count);
        printf("  Supported features: ");
        for (size_t i = 0; i < feature_count; i++) {
            printf("%s%s", features[i], i < feature_count - 1 ? ", " : "");
        }
        printf("\n");
        synor_string_array_free(features, feature_count);

        synor_chain_free(synor_chain);
    }

    // Get chain connections
    synor_connection_list_t* connections = NULL;
    err = synor_chains_get_connections(ibc, "synor-mainnet-1", &connections);
    if (err == SYNOR_OK) {
        printf("\nChain connections: %zu\n", synor_connection_list_count(connections));
        for (size_t i = 0; i < 3 && i < synor_connection_list_count(connections); i++) {
            synor_connection_t* conn = synor_connection_list_get(connections, i);
            printf("  %s -> %s\n",
                   synor_connection_get_id(conn),
                   synor_connection_get_counterparty_chain_id(conn));
        }
        synor_connection_list_free(connections);
    }

    printf("\n");
}

void channels_example(synor_ibc_t* ibc) {
    printf("=== Channel Management ===\n");

    synor_error_t err;

    // List existing channels
    synor_channel_list_t* channels = NULL;
    err = synor_channels_list(ibc, &channels);
    if (err == SYNOR_OK) {
        printf("Active channels: %zu\n", synor_channel_list_count(channels));

        for (size_t i = 0; i < 3 && i < synor_channel_list_count(channels); i++) {
            synor_channel_t* channel = synor_channel_list_get(channels, i);
            printf("\n  Channel %s:\n", synor_channel_get_id(channel));
            printf("    Port: %s\n", synor_channel_get_port_id(channel));
            printf("    State: %s\n", synor_channel_get_state(channel));
            printf("    Order: %s\n", synor_channel_get_ordering(channel));
            printf("    Counterparty: %s on %s\n",
                   synor_channel_get_counterparty_channel_id(channel),
                   synor_channel_get_counterparty_chain_id(channel));
            printf("    Version: %s\n", synor_channel_get_version(channel));
        }
        synor_channel_list_free(channels);
    }

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

    // Step 1: ChanOpenInit
    synor_channel_init_request_t init_req = {
        .port_id = "transfer",
        .counterparty_chain_id = "cosmos-hub-4",
        .counterparty_port_id = "transfer",
        .version = "ics20-1",
        .ordering = SYNOR_CHANNEL_ORDERING_UNORDERED
    };

    synor_channel_init_result_t* init_result = NULL;
    err = synor_channels_open_init(ibc, &init_req, &init_result);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to init channel\n");
        return;
    }

    printf("Channel init:\n");
    printf("  Channel ID: %s\n", synor_channel_init_result_get_id(init_result));
    printf("  State: %s\n", synor_channel_init_result_get_state(init_result));
    printf("  TX hash: %s\n", synor_channel_init_result_get_tx_hash(init_result));

    // Step 2: Wait for ChanOpenTry (counterparty)
    printf("\nWaiting for counterparty ChanOpenTry...\n");
    synor_channel_state_t try_state;
    err = synor_channels_wait_for_state(ibc, synor_channel_init_result_get_id(init_result),
                                         SYNOR_CHANNEL_STATE_TRYOPEN, 300, &try_state);
    printf("Channel state: %d\n", try_state);

    // Step 3: ChanOpenAck
    printf("\nSending ChanOpenAck...\n");
    synor_channel_ack_request_t ack_req = {
        .channel_id = synor_channel_init_result_get_id(init_result),
        .counterparty_channel_id = "channel-0",
        .counterparty_version = "ics20-1"
    };

    synor_channel_ack_result_t* ack_result = NULL;
    err = synor_channels_open_ack(ibc, &ack_req, &ack_result);
    if (err == SYNOR_OK) {
        printf("Ack TX: %s\n", synor_channel_ack_result_get_tx_hash(ack_result));
        synor_channel_ack_result_free(ack_result);
    }

    // Step 4: Wait for ChanOpenConfirm (counterparty)
    printf("\nWaiting for channel to open...\n");
    synor_channel_state_t open_state;
    err = synor_channels_wait_for_state(ibc, synor_channel_init_result_get_id(init_result),
                                         SYNOR_CHANNEL_STATE_OPEN, 300, &open_state);
    printf("Channel is now: %d\n", open_state);

    // Get channel details
    synor_channel_t* channel = NULL;
    err = synor_channels_get(ibc, synor_channel_init_result_get_id(init_result), &channel);
    if (err == SYNOR_OK) {
        printf("\nChannel details:\n");
        printf("  Sequences - Send: %lu, Recv: %lu, Ack: %lu\n",
               synor_channel_get_next_sequence_send(channel),
               synor_channel_get_next_sequence_recv(channel),
               synor_channel_get_next_sequence_ack(channel));
        synor_channel_free(channel);
    }

    synor_channel_init_result_free(init_result);
    printf("\n");
}

void transfer_example(synor_ibc_t* ibc) {
    printf("=== Cross-Chain Transfers ===\n");

    synor_error_t err;

    // Get supported tokens for transfer
    synor_transferable_token_list_t* tokens = NULL;
    err = synor_transfers_get_supported_tokens(ibc, "cosmos-hub-4", &tokens);
    if (err == SYNOR_OK) {
        printf("Transferable tokens to Cosmos Hub:\n");
        for (size_t i = 0; i < 5 && i < synor_transferable_token_list_count(tokens); i++) {
            synor_transferable_token_t* token = synor_transferable_token_list_get(tokens, i);
            printf("  %s (%s)\n",
                   synor_transferable_token_get_symbol(token),
                   synor_transferable_token_get_denom(token));
        }
        synor_transferable_token_list_free(tokens);
    }

    // Initiate a cross-chain transfer
    printf("\nInitiating transfer...\n");
    synor_transfer_request_t transfer_req = {
        .source_channel = "channel-0",
        .denom = "usynor",
        .amount = "1000000",
        .receiver = "cosmos1...",
        .timeout_height = 0,  // Use timestamp instead
        .timeout_timestamp = (uint64_t)(time(NULL) + 600) * 1000000000ULL,
        .memo = "IBC transfer from Synor"
    };

    synor_transfer_result_t* transfer = NULL;
    err = synor_transfers_send(ibc, &transfer_req, &transfer);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to send transfer\n");
        return;
    }

    printf("Transfer initiated:\n");
    printf("  TX hash: %s\n", synor_transfer_result_get_tx_hash(transfer));
    printf("  Sequence: %lu\n", synor_transfer_result_get_sequence(transfer));
    printf("  Packet ID: %s\n", synor_transfer_result_get_packet_id(transfer));
    printf("  Status: %s\n", synor_transfer_result_get_status(transfer));

    // Track transfer progress
    printf("\nTracking transfer...\n");
    synor_transfer_status_t* status = NULL;
    err = synor_transfers_track(ibc, synor_transfer_result_get_packet_id(transfer), &status);
    if (err == SYNOR_OK) {
        printf("Current status: %s\n", synor_transfer_status_get_state(status));
        printf("Source TX: %s\n", synor_transfer_status_get_source_tx_hash(status));
        const char* dest_tx = synor_transfer_status_get_dest_tx_hash(status);
        if (dest_tx) {
            printf("Dest TX: %s\n", dest_tx);
        }
        synor_transfer_status_free(status);
    }

    // Wait for completion
    printf("\nWaiting for transfer completion...\n");
    synor_transfer_status_t* final_status = NULL;
    err = synor_transfers_wait_for_completion(ibc, synor_transfer_result_get_packet_id(transfer),
                                               600, &final_status);
    if (err == SYNOR_OK) {
        printf("Transfer completed:\n");
        printf("  Final status: %s\n", synor_transfer_status_get_state(final_status));
        printf("  Acknowledgment: %s\n", synor_transfer_status_get_acknowledgment(final_status));
        synor_transfer_status_free(final_status);
    }

    // Get transfer history
    synor_transfer_list_t* history = NULL;
    err = synor_transfers_get_history(ibc, 10, &history);
    if (err == SYNOR_OK) {
        printf("\nRecent transfers: %zu\n", synor_transfer_list_count(history));
        for (size_t i = 0; i < 3 && i < synor_transfer_list_count(history); i++) {
            synor_transfer_t* t = synor_transfer_list_get(history, i);
            printf("  %s %s -> %s (%s)\n",
                   synor_transfer_get_amount(t),
                   synor_transfer_get_denom(t),
                   synor_transfer_get_dest_chain(t),
                   synor_transfer_get_status(t));
        }
        synor_transfer_list_free(history);
    }

    synor_transfer_result_free(transfer);
    printf("\n");
}

void packet_example(synor_ibc_t* ibc) {
    printf("=== Packet Operations ===\n");

    synor_error_t err;

    // List pending packets
    synor_packet_list_t* pending = NULL;
    err = synor_packets_list_pending(ibc, "channel-0", &pending);
    if (err == SYNOR_OK) {
        printf("Pending packets on channel-0: %zu\n", synor_packet_list_count(pending));

        for (size_t i = 0; i < 3 && i < synor_packet_list_count(pending); i++) {
            synor_packet_t* packet = synor_packet_list_get(pending, i);
            printf("\n  Packet #%lu:\n", synor_packet_get_sequence(packet));
            printf("    Source: %s/%s\n",
                   synor_packet_get_source_port(packet),
                   synor_packet_get_source_channel(packet));
            printf("    Dest: %s/%s\n",
                   synor_packet_get_dest_port(packet),
                   synor_packet_get_dest_channel(packet));
            printf("    State: %s\n", synor_packet_get_state(packet));
            printf("    Timeout: %lu\n", synor_packet_get_timeout_timestamp(packet));
        }
        synor_packet_list_free(pending);
    }

    // Get specific packet
    synor_packet_t* packet = NULL;
    err = synor_packets_get(ibc, "channel-0", 1, &packet);
    if (err == SYNOR_OK) {
        printf("\nPacket details:\n");
        const char* data_hex = synor_packet_get_data_hex(packet);
        printf("  Data (hex): %.40s...\n", data_hex);
        printf("  Created: %s\n", synor_packet_get_created_at(packet));
        synor_packet_free(packet);
    }

    // Get packet commitment proof
    synor_packet_proof_t* proof = NULL;
    err = synor_packets_get_commitment_proof(ibc, "channel-0", 1, &proof);
    if (err == SYNOR_OK) {
        printf("\nCommitment proof:\n");
        printf("  Height: %lu\n", synor_packet_proof_get_height(proof));
        printf("  Proof size: %zu bytes\n", synor_packet_proof_get_size(proof));
        synor_packet_proof_free(proof);
    }

    // Get acknowledgment
    synor_packet_ack_t* ack = NULL;
    err = synor_packets_get_acknowledgment(ibc, "channel-0", 1, &ack);
    if (err == SYNOR_OK) {
        printf("\nAcknowledgment:\n");
        printf("  Result: %s\n", synor_packet_ack_is_success(ack) ? "Success" :
                                 synor_packet_ack_get_error(ack));
        printf("  TX hash: %s\n", synor_packet_ack_get_tx_hash(ack));
        synor_packet_ack_free(ack);
    }

    // List timed-out packets
    synor_packet_list_t* timed_out = NULL;
    err = synor_packets_list_timed_out(ibc, &timed_out);
    if (err == SYNOR_OK) {
        printf("\nTimed-out packets: %zu\n", synor_packet_list_count(timed_out));

        // Timeout a packet manually
        if (synor_packet_list_count(timed_out) > 0) {
            synor_packet_t* to_timeout = synor_packet_list_get(timed_out, 0);
            printf("\nProcessing timeout for packet #%lu\n", synor_packet_get_sequence(to_timeout));

            synor_timeout_result_t* timeout = NULL;
            err = synor_packets_timeout(ibc, synor_packet_get_source_channel(to_timeout),
                                         synor_packet_get_sequence(to_timeout), &timeout);
            if (err == SYNOR_OK) {
                printf("Timeout TX: %s\n", synor_timeout_result_get_tx_hash(timeout));
                synor_timeout_result_free(timeout);
            }
        }
        synor_packet_list_free(timed_out);
    }

    printf("\n");
}

void relayer_example(synor_ibc_t* ibc) {
    printf("=== Relayer Operations ===\n");

    synor_error_t err;

    // Get relayer status
    synor_relayer_status_t* status = NULL;
    err = synor_relayer_get_status(ibc, &status);
    if (err == SYNOR_OK) {
        printf("Relayer status:\n");
        printf("  Running: %s\n", synor_relayer_status_is_running(status) ? "true" : "false");
        printf("  Uptime: %s\n", synor_relayer_status_get_uptime(status));
        printf("  Packets relayed: %lu\n", synor_relayer_status_get_packets_relayed(status));
        printf("  Errors: %lu\n", synor_relayer_status_get_error_count(status));
        synor_relayer_status_free(status);
    }

    // List active paths
    synor_relay_path_list_t* paths = NULL;
    err = synor_relayer_list_paths(ibc, &paths);
    if (err == SYNOR_OK) {
        printf("\nActive relay paths: %zu\n", synor_relay_path_list_count(paths));

        for (size_t i = 0; i < synor_relay_path_list_count(paths); i++) {
            synor_relay_path_t* path = synor_relay_path_list_get(paths, i);
            printf("\n  %s:\n", synor_relay_path_get_id(path));
            printf("    %s <-> %s\n",
                   synor_relay_path_get_source_chain(path),
                   synor_relay_path_get_dest_chain(path));
            printf("    Channel: %s <-> %s\n",
                   synor_relay_path_get_source_channel(path),
                   synor_relay_path_get_dest_channel(path));
            printf("    Status: %s\n", synor_relay_path_get_status(path));
            printf("    Pending packets: %zu\n", synor_relay_path_get_pending_packets(path));
        }
        synor_relay_path_list_free(paths);
    }

    // Configure a new path
    printf("\nConfiguring new relay path...\n");
    const char* filter_denoms[] = {"usynor", "uosmo"};
    synor_path_config_t path_config = {
        .source_chain = "synor-mainnet-1",
        .dest_chain = "osmosis-1",
        .source_channel = "channel-1",
        .dest_channel = "channel-100",
        .filter_denoms = filter_denoms,
        .filter_denoms_count = 2,
        .min_relay_amount = "1000",
        .max_relay_amount = "1000000000"
    };

    synor_relay_path_t* new_path = NULL;
    err = synor_relayer_add_path(ibc, &path_config, &new_path);
    if (err == SYNOR_OK) {
        printf("Path created: %s\n", synor_relay_path_get_id(new_path));

        // Start relaying on path
        printf("\nStarting relayer on path...\n");
        err = synor_relayer_start_path(ibc, synor_relay_path_get_id(new_path));
        if (err == SYNOR_OK) {
            printf("Relayer started\n");
        }

        // Manually relay pending packets
        printf("\nRelaying pending packets...\n");
        synor_relay_result_t* relay_result = NULL;
        err = synor_relayer_relay_pending(ibc, synor_relay_path_get_id(new_path), &relay_result);
        if (err == SYNOR_OK) {
            printf("Relayed %zu packets\n", synor_relay_result_get_packet_count(relay_result));
            printf("TX hashes: %zu\n", synor_relay_result_get_tx_hash_count(relay_result));
            synor_relay_result_free(relay_result);
        }

        // Get relay history
        synor_relay_event_list_t* history = NULL;
        err = synor_relayer_get_history(ibc, synor_relay_path_get_id(new_path), 10, &history);
        if (err == SYNOR_OK) {
            printf("\nRelay history:\n");
            for (size_t i = 0; i < 3 && i < synor_relay_event_list_count(history); i++) {
                synor_relay_event_t* event = synor_relay_event_list_get(history, i);
                printf("  %s: %s - %zu packets\n",
                       synor_relay_event_get_timestamp(event),
                       synor_relay_event_get_type(event),
                       synor_relay_event_get_packet_count(event));
            }
            synor_relay_event_list_free(history);
        }

        synor_relay_path_free(new_path);
    }

    printf("\n");
}

void monitoring_example(synor_ibc_t* ibc) {
    printf("=== IBC Monitoring ===\n");

    synor_error_t err;

    // Get IBC metrics
    synor_ibc_metrics_t* metrics = NULL;
    err = synor_monitoring_get_metrics(ibc, &metrics);
    if (err == SYNOR_OK) {
        printf("IBC metrics:\n");
        printf("  Total channels: %lu\n", synor_ibc_metrics_get_total_channels(metrics));
        printf("  Active channels: %lu\n", synor_ibc_metrics_get_active_channels(metrics));
        printf("  Total packets: %lu\n", synor_ibc_metrics_get_total_packets(metrics));
        printf("  Pending packets: %lu\n", synor_ibc_metrics_get_pending_packets(metrics));
        printf("  Failed packets: %lu\n", synor_ibc_metrics_get_failed_packets(metrics));
        printf("  Avg relay time: %lums\n", synor_ibc_metrics_get_avg_relay_time(metrics));
        synor_ibc_metrics_free(metrics);
    }

    // Get chain health
    synor_chain_health_list_t* chain_health = NULL;
    err = synor_monitoring_get_chain_health(ibc, &chain_health);
    if (err == SYNOR_OK) {
        printf("\nChain health:\n");
        for (size_t i = 0; i < 3 && i < synor_chain_health_list_count(chain_health); i++) {
            synor_chain_health_t* health = synor_chain_health_list_get(chain_health, i);
            printf("  %s:\n", synor_chain_health_get_chain_id(health));
            printf("    Status: %s\n", synor_chain_health_get_status(health));
            printf("    Block lag: %lu\n", synor_chain_health_get_block_lag(health));
            printf("    Last update: %s\n", synor_chain_health_get_last_update(health));
        }
        synor_chain_health_list_free(chain_health);
    }

    // Get channel statistics
    synor_channel_stats_t* stats = NULL;
    err = synor_monitoring_get_channel_stats(ibc, "channel-0", &stats);
    if (err == SYNOR_OK) {
        printf("\nChannel-0 statistics:\n");
        printf("  Packets sent: %lu\n", synor_channel_stats_get_packets_sent(stats));
        printf("  Packets received: %lu\n", synor_channel_stats_get_packets_received(stats));
        printf("  Success rate: %s%%\n", synor_channel_stats_get_success_rate(stats));
        printf("  Avg confirmation time: %lums\n", synor_channel_stats_get_avg_confirmation_time(stats));
        synor_channel_stats_free(stats);
    }

    // Get alerts
    synor_ibc_alert_list_t* alerts = NULL;
    err = synor_monitoring_get_alerts(ibc, &alerts);
    if (err == SYNOR_OK) {
        printf("\nActive alerts: %zu\n", synor_ibc_alert_list_count(alerts));
        for (size_t i = 0; i < synor_ibc_alert_list_count(alerts); i++) {
            synor_ibc_alert_t* alert = synor_ibc_alert_list_get(alerts, i);
            printf("  [%s] %s\n",
                   synor_ibc_alert_get_severity(alert),
                   synor_ibc_alert_get_message(alert));
        }
        synor_ibc_alert_list_free(alerts);
    }

    printf("\n");
}

int main(int argc, char** argv) {
    synor_error_t err;

    // Initialize client
    synor_ibc_config_t config = {
        .api_key = getenv("SYNOR_API_KEY") ? getenv("SYNOR_API_KEY") : "your-api-key",
        .endpoint = "https://ibc.synor.io/v1",
        .timeout_ms = 30000,
        .retries = 3,
        .debug = false,
        .default_chain = "synor-mainnet-1",
        .confirmations = 1
    };

    synor_ibc_t* ibc = NULL;
    err = synor_ibc_init(&config, &ibc);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to initialize IBC client: %s\n", synor_error_string(err));
        return 1;
    }

    // Check service health
    bool healthy;
    err = synor_ibc_health_check(ibc, &healthy);
    printf("Service healthy: %s\n\n", healthy ? "true" : "false");

    // Run examples
    chains_example(ibc);
    channels_example(ibc);
    transfer_example(ibc);
    packet_example(ibc);
    relayer_example(ibc);
    monitoring_example(ibc);

    // Cleanup
    synor_ibc_free(ibc);

    return 0;
}
