/**
 * Synor IBC SDK Examples for JavaScript/TypeScript
 *
 * Demonstrates Inter-Blockchain Communication operations:
 * - Cross-chain token transfers
 * - Channel management
 * - Packet handling
 * - Relayer operations
 */

import {
  SynorIbc,
  IbcConfig,
  Network,
  ChannelState,
  PacketState,
} from '../src/ibc';

async function main() {
  // Initialize client
  const config: IbcConfig = {
    apiKey: process.env.SYNOR_API_KEY || 'your-api-key',
    endpoint: 'https://ibc.synor.io/v1',
    timeout: 30000,
    retries: 3,
    debug: false,
    defaultNetwork: Network.Mainnet,
  };

  const ibc = new SynorIbc(config);

  try {
    // Check service health
    const healthy = await ibc.healthCheck();
    console.log(`Service healthy: ${healthy}\n`);

    // Example 1: Connected chains
    await chainsExample(ibc);

    // Example 2: Channel management
    await channelsExample(ibc);

    // Example 3: Token transfers
    await transferExample(ibc);

    // Example 4: Packet tracking
    await packetExample(ibc);

    // Example 5: Relayer operations
    await relayerExample(ibc);

    // Example 6: Connection info
    await connectionExample(ibc);
  } finally {
    ibc.close();
  }
}

/**
 * Connected chains information
 */
async function chainsExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Connected Chains ===');

  // Get all connected chains
  const chains = await ibc.chains.list();
  console.log(`Connected chains: ${chains.length}`);
  for (const chain of chains) {
    console.log(`  ${chain.chainId}:`);
    console.log(`    Name: ${chain.name}`);
    console.log(`    Status: ${chain.status}`);
    console.log(`    Block height: ${chain.latestHeight}`);
    console.log(`    Channels: ${chain.channelCount}`);
  }

  // Get specific chain info
  const cosmos = await ibc.chains.get('cosmoshub-4');
  console.log(`\nCosmos Hub details:`);
  console.log(`  RPC: ${cosmos.rpcEndpoint}`);
  console.log(`  Rest: ${cosmos.restEndpoint}`);
  console.log(`  Native denom: ${cosmos.nativeDenom}`);
  console.log(`  Prefix: ${cosmos.bech32Prefix}`);

  // Get supported assets on a chain
  const assets = await ibc.chains.getAssets('cosmoshub-4');
  console.log(`\nSupported assets on Cosmos Hub:`);
  for (const asset of assets.slice(0, 5)) {
    console.log(`  ${asset.symbol}: ${asset.denom}`);
    console.log(`    Origin: ${asset.originChain}`);
    console.log(`    Decimals: ${asset.decimals}`);
  }

  // Get chain paths (routes)
  const paths = await ibc.chains.getPaths('synor-1', 'cosmoshub-4');
  console.log(`\nPaths from Synor to Cosmos Hub:`);
  for (const path of paths) {
    console.log(`  ${path.sourceChannel} -> ${path.destChannel}`);
    console.log(`    Hops: ${path.hops}`);
    console.log(`    Avg time: ${path.avgTransferTime}s`);
  }

  console.log('');
}

/**
 * Channel management
 */
async function channelsExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Channel Management ===');

  // List all channels
  const channels = await ibc.channels.list();
  console.log(`Total channels: ${channels.length}`);

  // Filter by state
  const openChannels = channels.filter(c => c.state === ChannelState.Open);
  console.log(`Open channels: ${openChannels.length}`);

  for (const channel of openChannels.slice(0, 3)) {
    console.log(`\n  Channel ${channel.channelId}:`);
    console.log(`    Port: ${channel.portId}`);
    console.log(`    Counterparty: ${channel.counterpartyChannelId} on ${channel.counterpartyChainId}`);
    console.log(`    Ordering: ${channel.ordering}`);
    console.log(`    Version: ${channel.version}`);
    console.log(`    State: ${channel.state}`);
  }

  // Get specific channel
  const channel = await ibc.channels.get('channel-0');
  console.log(`\nChannel-0 details:`);
  console.log(`  Connection: ${channel.connectionId}`);
  console.log(`  Counterparty port: ${channel.counterpartyPortId}`);

  // Get channel statistics
  const stats = await ibc.channels.getStats('channel-0');
  console.log(`\nChannel-0 statistics:`);
  console.log(`  Total packets sent: ${stats.packetsSent}`);
  console.log(`  Total packets received: ${stats.packetsReceived}`);
  console.log(`  Pending packets: ${stats.pendingPackets}`);
  console.log(`  Success rate: ${stats.successRate}%`);
  console.log(`  Avg relay time: ${stats.avgRelayTime}s`);

  // Get channel capacity
  const capacity = await ibc.channels.getCapacity('channel-0');
  console.log(`\nChannel-0 capacity:`);
  console.log(`  Max throughput: ${capacity.maxPacketsPerBlock} packets/block`);
  console.log(`  Current utilization: ${capacity.utilization}%`);

  console.log('');
}

/**
 * Cross-chain token transfers
 */
async function transferExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Cross-Chain Transfers ===');

  // Estimate transfer fee
  const estimate = await ibc.transfers.estimateFee({
    sourceChain: 'synor-1',
    destChain: 'cosmoshub-4',
    denom: 'usyn',
    amount: '1000000', // 1 SYN (6 decimals)
  });
  console.log('Transfer fee estimate:');
  console.log(`  Gas: ${estimate.gas}`);
  console.log(`  Fee: ${estimate.fee} ${estimate.feeDenom}`);
  console.log(`  Timeout: ${estimate.timeout}s`);

  // Initiate a transfer
  console.log('\nInitiating transfer...');
  const transfer = await ibc.transfers.send({
    sourceChain: 'synor-1',
    destChain: 'cosmoshub-4',
    channel: 'channel-0',
    sender: 'synor1abc...', // Your address
    receiver: 'cosmos1xyz...', // Recipient address
    denom: 'usyn',
    amount: '1000000', // 1 SYN
    memo: 'Cross-chain transfer example',
    timeoutHeight: 0, // Use timestamp instead
    timeoutTimestamp: Date.now() + 600000, // 10 minutes
  });

  console.log(`Transfer initiated:`);
  console.log(`  TX Hash: ${transfer.txHash}`);
  console.log(`  Sequence: ${transfer.sequence}`);
  console.log(`  Status: ${transfer.status}`);

  // Track transfer status
  console.log('\nTracking transfer...');
  const status = await ibc.transfers.getStatus(transfer.txHash);
  console.log(`Current status: ${status.state}`);
  console.log(`  Source confirmed: ${status.sourceConfirmed}`);
  console.log(`  Relayed: ${status.relayed}`);
  console.log(`  Dest confirmed: ${status.destConfirmed}`);

  // Get transfer history
  const history = await ibc.transfers.getHistory({
    address: 'synor1abc...',
    limit: 5,
  });
  console.log(`\nTransfer history (last 5):`);
  for (const tx of history) {
    const direction = tx.sender === 'synor1abc...' ? 'OUT' : 'IN';
    console.log(`  ${direction} ${tx.amount} ${tx.denom} (${tx.status})`);
  }

  // Get pending transfers
  const pending = await ibc.transfers.getPending('synor1abc...');
  console.log(`\nPending transfers: ${pending.length}`);

  console.log('');
}

/**
 * Packet handling
 */
async function packetExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Packet Handling ===');

  // Get pending packets
  const packets = await ibc.packets.getPending('channel-0');
  console.log(`Pending packets on channel-0: ${packets.length}`);

  for (const packet of packets.slice(0, 3)) {
    console.log(`\n  Packet ${packet.sequence}:`);
    console.log(`    Source: ${packet.sourcePort}/${packet.sourceChannel}`);
    console.log(`    Dest: ${packet.destPort}/${packet.destChannel}`);
    console.log(`    State: ${packet.state}`);
    console.log(`    Data size: ${packet.data.length} bytes`);
    console.log(`    Timeout height: ${packet.timeoutHeight}`);
    console.log(`    Timeout timestamp: ${new Date(packet.timeoutTimestamp).toISOString()}`);
  }

  // Get packet by sequence
  const packet = await ibc.packets.get('channel-0', 1);
  console.log(`\nPacket details:`);
  console.log(`  Commitment: ${packet.commitment}`);
  console.log(`  Receipt: ${packet.receipt}`);
  console.log(`  Acknowledgement: ${packet.acknowledgement}`);

  // Get packet receipts
  const receipts = await ibc.packets.getReceipts('channel-0', [1, 2, 3]);
  console.log(`\nPacket receipts:`);
  for (const [seq, receipt] of Object.entries(receipts)) {
    console.log(`  Sequence ${seq}: ${receipt ? 'received' : 'not received'}`);
  }

  // Get timed out packets
  const timedOut = await ibc.packets.getTimedOut('channel-0');
  console.log(`\nTimed out packets: ${timedOut.length}`);
  for (const p of timedOut) {
    console.log(`  Sequence ${p.sequence}: timeout at ${new Date(p.timeoutTimestamp).toISOString()}`);
  }

  // Get unreceived packets
  const unreceived = await ibc.packets.getUnreceived('channel-0');
  console.log(`\nUnreceived packet sequences: ${unreceived.join(', ') || 'none'}`);

  // Get unacknowledged packets
  const unacked = await ibc.packets.getUnacknowledged('channel-0');
  console.log(`Unacknowledged packet sequences: ${unacked.join(', ') || 'none'}`);

  console.log('');
}

/**
 * Relayer operations
 */
async function relayerExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Relayer Operations ===');

  // Get active relayers
  const relayers = await ibc.relayers.list();
  console.log(`Active relayers: ${relayers.length}`);
  for (const relayer of relayers.slice(0, 3)) {
    console.log(`\n  ${relayer.address}:`);
    console.log(`    Chains: ${relayer.chains.join(', ')}`);
    console.log(`    Packets relayed: ${relayer.packetsRelayed}`);
    console.log(`    Success rate: ${relayer.successRate}%`);
    console.log(`    Avg latency: ${relayer.avgLatency}ms`);
    console.log(`    Fee rate: ${relayer.feeRate}%`);
  }

  // Get relayer statistics
  const stats = await ibc.relayers.getStats();
  console.log('\nGlobal relayer statistics:');
  console.log(`  Total relayers: ${stats.totalRelayers}`);
  console.log(`  Active relayers: ${stats.activeRelayers}`);
  console.log(`  Packets relayed (24h): ${stats.packetsRelayed24h}`);
  console.log(`  Total fees earned: ${stats.totalFeesEarned}`);

  // Register as a relayer
  console.log('\nRegistering as relayer...');
  const registration = await ibc.relayers.register({
    chains: ['synor-1', 'cosmoshub-4'],
    feeRate: 0.1, // 0.1% fee
    minPacketSize: 0,
    maxPacketSize: 1000000,
  });
  console.log(`Registered with address: ${registration.relayerAddress}`);

  // Start relaying (in background)
  console.log('\nStarting relay service...');
  const relaySession = await ibc.relayers.startRelay({
    channels: ['channel-0'],
    autoAck: true,
    batchSize: 10,
    pollInterval: 5000,
  });
  console.log(`Relay session started: ${relaySession.sessionId}`);

  // Get relay queue
  const queue = await ibc.relayers.getQueue('channel-0');
  console.log(`\nRelay queue for channel-0: ${queue.length} packets`);

  // Manually relay a packet
  if (queue.length > 0) {
    console.log('\nRelaying packet...');
    const relayResult = await ibc.relayers.relayPacket(queue[0].sequence, 'channel-0');
    console.log(`Relay result: ${relayResult.status}`);
    console.log(`  TX Hash: ${relayResult.txHash}`);
    console.log(`  Fee earned: ${relayResult.feeEarned}`);
  }

  // Stop relay session
  await ibc.relayers.stopRelay(relaySession.sessionId);
  console.log('\nRelay session stopped');

  console.log('');
}

/**
 * Connection information
 */
async function connectionExample(ibc: SynorIbc): Promise<void> {
  console.log('=== Connection Information ===');

  // List connections
  const connections = await ibc.connections.list();
  console.log(`Total connections: ${connections.length}`);

  for (const conn of connections.slice(0, 3)) {
    console.log(`\n  ${conn.connectionId}:`);
    console.log(`    Client: ${conn.clientId}`);
    console.log(`    Counterparty: ${conn.counterpartyConnectionId}`);
    console.log(`    State: ${conn.state}`);
    console.log(`    Versions: ${conn.versions.join(', ')}`);
  }

  // Get connection details
  const connection = await ibc.connections.get('connection-0');
  console.log(`\nConnection-0 details:`);
  console.log(`  Delay period: ${connection.delayPeriod}ns`);
  console.log(`  Counterparty client: ${connection.counterpartyClientId}`);
  console.log(`  Counterparty prefix: ${connection.counterpartyPrefix}`);

  // Get client state
  const client = await ibc.clients.get(connection.clientId);
  console.log(`\nClient state:`);
  console.log(`  Chain ID: ${client.chainId}`);
  console.log(`  Trust level: ${client.trustLevel}`);
  console.log(`  Trusting period: ${client.trustingPeriod}`);
  console.log(`  Unbonding period: ${client.unbondingPeriod}`);
  console.log(`  Latest height: ${client.latestHeight}`);
  console.log(`  Frozen: ${client.frozen}`);

  // Get consensus state
  const consensus = await ibc.clients.getConsensusState(connection.clientId, client.latestHeight);
  console.log(`\nConsensus state at height ${client.latestHeight}:`);
  console.log(`  Timestamp: ${new Date(consensus.timestamp).toISOString()}`);
  console.log(`  Root: ${consensus.root.slice(0, 20)}...`);
  console.log(`  Next validators hash: ${consensus.nextValidatorsHash.slice(0, 20)}...`);

  console.log('');
}

// Run examples
main().catch(console.error);
