/**
 * Kava 区块链客户端
 *
 * 功能:
 * 1. 查询账户余额
 * 2. 查询账户信息 (用于获取 sequence 和 account_number)
 * 3. 构建交易
 * 4. 广播交易
 *
 * 使用 Kava 官方 LCD REST API:
 * - 主网: https://api.kava.io
 * - 备用: https://api.kava-rpc.com
 */

import * as crypto from 'crypto';

// =============================================================================
// 配置
// =============================================================================

export interface KavaClientConfig {
  lcdEndpoint: string;        // LCD REST API 端点
  chainId: string;            // 链 ID (kava_2222-10 for mainnet)
  gasPrice: string;           // Gas 价格 (如 "0.025ukava")
  defaultGasLimit: number;    // 默认 Gas 限制
}

export const KAVA_MAINNET_CONFIG: KavaClientConfig = {
  lcdEndpoint: 'https://api.kava.io',
  chainId: 'kava_2222-10',
  gasPrice: '0.025ukava',
  defaultGasLimit: 200000,
};

export const KAVA_TESTNET_CONFIG: KavaClientConfig = {
  lcdEndpoint: 'https://api.testnet.kava.io',
  chainId: 'kava_2221-16000',
  gasPrice: '0.025ukava',
  defaultGasLimit: 200000,
};

// 备用端点列表
export const KAVA_LCD_ENDPOINTS = [
  'https://api.kava.io',
  'https://api.kava-rpc.com',
  'https://api.kava.chainstacklabs.com',
];

// =============================================================================
// 类型定义
// =============================================================================

export interface Coin {
  denom: string;
  amount: string;
}

export interface AccountInfo {
  address: string;
  accountNumber: string;
  sequence: string;
  pubKey?: {
    type: string;
    value: string;
  };
}

export interface BalanceResponse {
  balances: Coin[];
  pagination: {
    next_key: string | null;
    total: string;
  };
}

export interface AccountResponse {
  account: {
    '@type': string;
    address: string;
    pub_key?: {
      '@type': string;
      key: string;
    };
    account_number: string;
    sequence: string;
  };
}

export interface TxResponse {
  height: string;
  txhash: string;
  codespace: string;
  code: number;
  data: string;
  raw_log: string;
  logs: unknown[];
  info: string;
  gas_wanted: string;
  gas_used: string;
  tx: unknown;
  timestamp: string;
  events: unknown[];
}

export interface BroadcastTxResponse {
  tx_response: TxResponse;
}

export interface SimulateTxResponse {
  gas_info: {
    gas_wanted: string;
    gas_used: string;
  };
  result: {
    data: string;
    log: string;
    events: unknown[];
  };
}

// 交易消息类型
export interface MsgSend {
  '@type': '/cosmos.bank.v1beta1.MsgSend';
  from_address: string;
  to_address: string;
  amount: Coin[];
}

export interface Fee {
  amount: Coin[];
  gas_limit: string;
  payer?: string;
  granter?: string;
}

export interface SignerInfo {
  public_key: {
    '@type': string;
    key: string;
  };
  mode_info: {
    single: {
      mode: string;
    };
  };
  sequence: string;
}

export interface AuthInfo {
  signer_infos: SignerInfo[];
  fee: Fee;
}

export interface TxBody {
  messages: MsgSend[];
  memo: string;
  timeout_height: string;
  extension_options: unknown[];
  non_critical_extension_options: unknown[];
}

export interface TxRaw {
  body_bytes: string;
  auth_info_bytes: string;
  signatures: string[];
}

// =============================================================================
// Kava 客户端类
// =============================================================================

export class KavaClient {
  private config: KavaClientConfig;
  private currentEndpointIndex = 0;

  constructor(config: KavaClientConfig = KAVA_MAINNET_CONFIG) {
    this.config = config;
  }

  /**
   * 获取当前 LCD 端点
   */
  private getLcdEndpoint(): string {
    return this.config.lcdEndpoint;
  }

  /**
   * 切换到备用端点
   */
  private switchToBackupEndpoint(): void {
    this.currentEndpointIndex = (this.currentEndpointIndex + 1) % KAVA_LCD_ENDPOINTS.length;
    this.config.lcdEndpoint = KAVA_LCD_ENDPOINTS[this.currentEndpointIndex];
    console.log(`Switched to backup endpoint: ${this.config.lcdEndpoint}`);
  }

  /**
   * 发送 HTTP 请求
   */
  private async request<T>(
    path: string,
    method: 'GET' | 'POST' = 'GET',
    body?: unknown
  ): Promise<T> {
    const url = `${this.getLcdEndpoint()}${path}`;

    const options: RequestInit = {
      method,
      headers: {
        'Content-Type': 'application/json',
      },
    };

    if (body) {
      options.body = JSON.stringify(body);
    }

    try {
      const response = await fetch(url, options);

      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`HTTP ${response.status}: ${errorText}`);
      }

      return await response.json() as T;
    } catch (error) {
      // 如果请求失败，尝试切换端点
      console.error(`Request failed for ${url}:`, error);
      this.switchToBackupEndpoint();
      throw error;
    }
  }

  // ===========================================================================
  // 查询功能
  // ===========================================================================

  /**
   * 查询账户余额
   *
   * @param address - Kava 地址 (bech32 格式，以 "kava" 开头)
   * @returns 余额列表
   */
  async getBalances(address: string): Promise<Coin[]> {
    const response = await this.request<BalanceResponse>(
      `/cosmos/bank/v1beta1/balances/${address}`
    );
    return response.balances;
  }

  /**
   * 查询指定代币余额
   *
   * @param address - Kava 地址
   * @param denom - 代币单位 (如 "ukava")
   * @returns 余额
   */
  async getBalance(address: string, denom: string = 'ukava'): Promise<Coin> {
    const response = await this.request<{ balance: Coin }>(
      `/cosmos/bank/v1beta1/balances/${address}/by_denom?denom=${denom}`
    );
    return response.balance;
  }

  /**
   * 查询账户信息 (用于构建交易)
   *
   * @param address - Kava 地址
   * @returns 账户信息 (包含 account_number 和 sequence)
   */
  async getAccountInfo(address: string): Promise<AccountInfo> {
    const response = await this.request<AccountResponse>(
      `/cosmos/auth/v1beta1/accounts/${address}`
    );

    const account = response.account;
    return {
      address: account.address,
      accountNumber: account.account_number,
      sequence: account.sequence,
      pubKey: account.pub_key ? {
        type: account.pub_key['@type'],
        value: account.pub_key.key,
      } : undefined,
    };
  }

  /**
   * 查询交易详情
   *
   * @param txHash - 交易哈希
   * @returns 交易详情
   */
  async getTx(txHash: string): Promise<TxResponse> {
    const response = await this.request<{ tx_response: TxResponse }>(
      `/cosmos/tx/v1beta1/txs/${txHash}`
    );
    return response.tx_response;
  }

  /**
   * 查询最新区块高度
   */
  async getLatestBlockHeight(): Promise<number> {
    const response = await this.request<{ block: { header: { height: string } } }>(
      `/cosmos/base/tendermint/v1beta1/blocks/latest`
    );
    return parseInt(response.block.header.height, 10);
  }

  // ===========================================================================
  // 交易构建
  // ===========================================================================

  /**
   * 构建转账交易消息
   *
   * @param fromAddress - 发送方地址
   * @param toAddress - 接收方地址
   * @param amount - 金额
   * @param denom - 代币单位
   * @returns MsgSend 消息
   */
  buildMsgSend(
    fromAddress: string,
    toAddress: string,
    amount: string,
    denom: string = 'ukava'
  ): MsgSend {
    return {
      '@type': '/cosmos.bank.v1beta1.MsgSend',
      from_address: fromAddress,
      to_address: toAddress,
      amount: [{ denom, amount }],
    };
  }

  /**
   * 构建交易体
   *
   * @param messages - 交易消息列表
   * @param memo - 备注
   * @returns TxBody
   */
  buildTxBody(messages: MsgSend[], memo: string = ''): TxBody {
    return {
      messages,
      memo,
      timeout_height: '0',
      extension_options: [],
      non_critical_extension_options: [],
    };
  }

  /**
   * 构建 AuthInfo
   *
   * @param publicKeyBase64 - 压缩公钥 (Base64)
   * @param sequence - 账户序列号
   * @param gasLimit - Gas 限制
   * @param feeAmount - 手续费金额
   * @returns AuthInfo
   */
  buildAuthInfo(
    publicKeyBase64: string,
    sequence: string,
    gasLimit: number = this.config.defaultGasLimit,
    feeAmount?: Coin[]
  ): AuthInfo {
    // 计算手续费 (如果未提供)
    if (!feeAmount) {
      const gasPrice = parseFloat(this.config.gasPrice.replace('ukava', ''));
      const fee = Math.ceil(gasLimit * gasPrice);
      feeAmount = [{ denom: 'ukava', amount: fee.toString() }];
    }

    return {
      signer_infos: [{
        public_key: {
          '@type': '/cosmos.crypto.secp256k1.PubKey',
          key: publicKeyBase64,
        },
        mode_info: {
          single: {
            mode: 'SIGN_MODE_DIRECT',
          },
        },
        sequence,
      }],
      fee: {
        amount: feeAmount,
        gas_limit: gasLimit.toString(),
      },
    };
  }

  /**
   * 构建待签名的交易数据
   *
   * @param txBody - 交易体
   * @param authInfo - 认证信息
   * @param accountNumber - 账户编号
   * @returns 签名数据 (用于 TSS 签名)
   */
  buildSignDoc(
    txBody: TxBody,
    authInfo: AuthInfo,
    accountNumber: string
  ): {
    bodyBytes: Buffer;
    authInfoBytes: Buffer;
    chainId: string;
    accountNumber: string;
    signBytes: Buffer;
  } {
    // 注意：这里需要使用 protobuf 编码
    // 简化版本：使用 JSON 编码后进行 SHA256 哈希
    // 生产环境应使用 @cosmjs/proto-signing

    const bodyBytes = Buffer.from(JSON.stringify(txBody));
    const authInfoBytes = Buffer.from(JSON.stringify(authInfo));

    // 构建 SignDoc
    const signDoc = {
      body_bytes: bodyBytes.toString('base64'),
      auth_info_bytes: authInfoBytes.toString('base64'),
      chain_id: this.config.chainId,
      account_number: accountNumber,
    };

    // 计算签名哈希 (SHA256)
    const signBytes = crypto.createHash('sha256')
      .update(JSON.stringify(signDoc))
      .digest();

    return {
      bodyBytes,
      authInfoBytes,
      chainId: this.config.chainId,
      accountNumber,
      signBytes,
    };
  }

  // ===========================================================================
  // 交易广播
  // ===========================================================================

  /**
   * 模拟交易 (估算 Gas)
   *
   * @param txBytes - 交易字节 (Base64)
   * @returns 模拟结果
   */
  async simulateTx(txBytes: string): Promise<SimulateTxResponse> {
    return this.request<SimulateTxResponse>(
      '/cosmos/tx/v1beta1/simulate',
      'POST',
      { tx_bytes: txBytes }
    );
  }

  /**
   * 广播交易
   *
   * @param txBytes - 已签名的交易字节 (Base64)
   * @param mode - 广播模式 (BROADCAST_MODE_SYNC | BROADCAST_MODE_ASYNC | BROADCAST_MODE_BLOCK)
   * @returns 广播结果
   */
  async broadcastTx(
    txBytes: string,
    mode: 'BROADCAST_MODE_SYNC' | 'BROADCAST_MODE_ASYNC' | 'BROADCAST_MODE_BLOCK' = 'BROADCAST_MODE_SYNC'
  ): Promise<BroadcastTxResponse> {
    return this.request<BroadcastTxResponse>(
      '/cosmos/tx/v1beta1/txs',
      'POST',
      {
        tx_bytes: txBytes,
        mode,
      }
    );
  }

  /**
   * 构建并编码完整的已签名交易
   *
   * @param bodyBytes - 交易体字节
   * @param authInfoBytes - 认证信息字节
   * @param signature - 签名 (Buffer)
   * @returns Base64 编码的交易字节
   */
  encodeSignedTx(
    bodyBytes: Buffer,
    authInfoBytes: Buffer,
    signature: Buffer
  ): string {
    // 简化版本：使用 JSON 编码
    // 生产环境应使用 protobuf 编码
    const txRaw = {
      body_bytes: bodyBytes.toString('base64'),
      auth_info_bytes: authInfoBytes.toString('base64'),
      signatures: [signature.toString('base64')],
    };

    return Buffer.from(JSON.stringify(txRaw)).toString('base64');
  }

  // ===========================================================================
  // 便捷方法
  // ===========================================================================

  /**
   * 格式化 KAVA 金额 (ukava -> KAVA)
   *
   * @param amount - 金额 (ukava)
   * @returns 格式化的金额字符串
   */
  formatKava(amount: string): string {
    const ukava = BigInt(amount);
    const kava = Number(ukava) / 1_000_000;
    return kava.toFixed(6);
  }

  /**
   * 转换 KAVA 到 ukava
   *
   * @param kava - KAVA 金额
   * @returns ukava 金额字符串
   */
  toUkava(kava: number | string): string {
    const ukava = Math.floor(Number(kava) * 1_000_000);
    return ukava.toString();
  }

  /**
   * 检查地址格式
   *
   * @param address - 地址
   * @returns 是否有效
   */
  isValidAddress(address: string): boolean {
    return address.startsWith('kava') && address.length === 43;
  }

  /**
   * 获取配置
   */
  getConfig(): KavaClientConfig {
    return { ...this.config };
  }

  /**
   * 更新配置
   */
  updateConfig(config: Partial<KavaClientConfig>): void {
    this.config = { ...this.config, ...config };
  }
}

// 导出默认客户端实例
export const kavaClient = new KavaClient();
