/**
 * Kava 交易服务
 *
 * 使用 Kava LCD REST API 构建和广播交易
 * 支持 TSS 多方签名
 *
 * API 文档参考:
 * - https://docs.kava.io/docs/using-kava-endpoints/endpoints/
 * - https://docs.cosmos.network/main/learn/advanced/grpc_rest
 */

import * as crypto from 'crypto';

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

export interface KavaTxConfig {
  lcdEndpoint: string;
  rpcEndpoint: string;
  chainId: string;
  prefix: string;
  denom: string;
  gasPrice: number;  // ukava per gas unit
}

export const KAVA_MAINNET_TX_CONFIG: KavaTxConfig = {
  lcdEndpoint: 'https://api.kava.io',
  rpcEndpoint: 'https://rpc.kava.io',
  chainId: 'kava_2222-10',
  prefix: 'kava',
  denom: 'ukava',
  gasPrice: 0.025,
};

export const KAVA_TESTNET_TX_CONFIG: KavaTxConfig = {
  lcdEndpoint: 'https://api.testnet.kava.io',
  rpcEndpoint: 'https://rpc.testnet.kava.io',
  chainId: 'kava_2221-16000',
  prefix: 'kava',
  denom: 'ukava',
  gasPrice: 0.025,
};

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

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

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

export interface AccountBalance {
  denom: string;
  amount: string;
  formatted: string;  // 人类可读格式 (KAVA)
}

export interface AccountInfo {
  address: string;
  accountNumber: number;
  sequence: number;
  balances: AccountBalance[];
}

export interface UnsignedTxData {
  // 用于 TSS 签名的数据
  signBytes: Uint8Array;      // 待签名的哈希
  signBytesHex: string;       // 十六进制格式

  // 交易元数据
  txBodyBytes: Uint8Array;
  authInfoBytes: Uint8Array;
  accountNumber: number;
  sequence: number;
  chainId: string;

  // 可读信息
  from: string;
  to: string;
  amount: string;
  denom: string;
  memo: string;
  fee: string;
  gasLimit: number;
}

export interface SignedTxData {
  txBytes: Uint8Array;        // 完整的已签名交易
  txBytesBase64: string;      // Base64 格式
  txHash: string;             // 交易哈希
}

export interface TxBroadcastResult {
  success: boolean;
  txHash?: string;
  code?: number;
  rawLog?: string;
  gasUsed?: string;
  gasWanted?: string;
  height?: string;
}

export interface TxStatus {
  found: boolean;
  status: 'pending' | 'success' | 'failed';
  code?: number;
  rawLog?: string;
  height?: string;
  gasUsed?: string;
  timestamp?: string;
}

// =============================================================================
// Kava 交易服务类
// =============================================================================

export class KavaTxService {
  private config: KavaTxConfig;
  private backupEndpointIndex = 0;

  constructor(config: KavaTxConfig = KAVA_MAINNET_TX_CONFIG) {
    this.config = { ...config };
  }

  // ===========================================================================
  // 配置管理
  // ===========================================================================

  getConfig(): KavaTxConfig {
    return { ...this.config };
  }

  updateConfig(config: Partial<KavaTxConfig>): void {
    this.config = { ...this.config, ...config };
  }

  /**
   * 切换到测试网
   */
  switchToTestnet(): void {
    this.config = { ...KAVA_TESTNET_TX_CONFIG };
  }

  /**
   * 切换到主网
   */
  switchToMainnet(): void {
    this.config = { ...KAVA_MAINNET_TX_CONFIG };
  }

  /**
   * 检查是否是测试网
   */
  isTestnet(): boolean {
    return this.config.chainId === KAVA_TESTNET_TX_CONFIG.chainId;
  }

  // ===========================================================================
  // HTTP 请求
  // ===========================================================================

  private async request<T>(
    path: string,
    method: 'GET' | 'POST' = 'GET',
    body?: unknown,
    timeout: number = 10000
  ): Promise<T> {
    const url = `${this.config.lcdEndpoint}${path}`;

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

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

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

      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) {
      // 如果是主网且请求失败，尝试备用端点
      if (!this.isTestnet()) {
        this.backupEndpointIndex = (this.backupEndpointIndex + 1) % BACKUP_ENDPOINTS.length;
        console.log(`Switched to backup endpoint: ${BACKUP_ENDPOINTS[this.backupEndpointIndex]}`);
      }
      throw error;
    } finally {
      clearTimeout(timeoutId);
    }
  }

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

  /**
   * 健康检查 - 查询最新区块
   */
  async healthCheck(): Promise<{ ok: boolean; latency?: number; blockHeight?: number; error?: string }> {
    const start = Date.now();
    try {
      const response = await this.request<{ block: { header: { height: string } } }>(
        '/cosmos/base/tendermint/v1beta1/blocks/latest',
        'GET',
        undefined,
        5000
      );
      const latency = Date.now() - start;
      return {
        ok: true,
        latency,
        blockHeight: parseInt(response.block.header.height, 10),
      };
    } catch (error) {
      return {
        ok: false,
        error: (error as Error).message,
      };
    }
  }

  /**
   * 查询 KAVA 余额
   */
  async getKavaBalance(address: string): Promise<string> {
    try {
      const response = await this.request<{ balance: Coin }>(
        `/cosmos/bank/v1beta1/balances/${address}/by_denom?denom=ukava`
      );
      return response.balance?.amount || '0';
    } catch {
      return '0';
    }
  }

  /**
   * 查询所有余额
   */
  async getAllBalances(address: string): Promise<AccountBalance[]> {
    const response = await this.request<{ balances: Coin[] }>(
      `/cosmos/bank/v1beta1/balances/${address}`
    );

    return (response.balances || []).map(coin => ({
      denom: coin.denom,
      amount: coin.amount,
      formatted: this.formatAmount(coin.amount, coin.denom),
    }));
  }

  /**
   * 查询账户信息
   */
  async getAccountInfo(address: string): Promise<AccountInfo | null> {
    try {
      const [accountResp, balances] = await Promise.all([
        this.request<{
          account: {
            '@type': string;
            address: string;
            account_number: string;
            sequence: string;
          };
        }>(`/cosmos/auth/v1beta1/accounts/${address}`),
        this.getAllBalances(address),
      ]);

      return {
        address: accountResp.account.address,
        accountNumber: parseInt(accountResp.account.account_number, 10),
        sequence: parseInt(accountResp.account.sequence, 10),
        balances,
      };
    } catch {
      return null;
    }
  }

  /**
   * 查询交易状态
   */
  async getTxStatus(txHash: string): Promise<TxStatus> {
    try {
      const response = await this.request<{
        tx_response: {
          code: number;
          raw_log: string;
          height: string;
          gas_used: string;
          timestamp: string;
        };
      }>(`/cosmos/tx/v1beta1/txs/${txHash}`);

      return {
        found: true,
        status: response.tx_response.code === 0 ? 'success' : 'failed',
        code: response.tx_response.code,
        rawLog: response.tx_response.raw_log,
        height: response.tx_response.height,
        gasUsed: response.tx_response.gas_used,
        timestamp: response.tx_response.timestamp,
      };
    } catch {
      return { found: false, status: 'pending' };
    }
  }

  // ===========================================================================
  // 交易构建 (使用 Amino JSON)
  // ===========================================================================

  /**
   * 构建转账交易 (返回待签名数据)
   */
  async buildSendTx(
    fromAddress: string,
    toAddress: string,
    amount: string,
    publicKeyHex: string,
    memo: string = ''
  ): Promise<UnsignedTxData> {
    // 获取账户信息
    const accountInfo = await this.getAccountInfo(fromAddress);
    if (!accountInfo) {
      throw new Error('Account not found or has no transactions');
    }

    const gasLimit = 100000;
    const feeAmount = Math.ceil(gasLimit * this.config.gasPrice);

    // 构建 Amino JSON 签名文档
    const signDoc = {
      chain_id: this.config.chainId,
      account_number: accountInfo.accountNumber.toString(),
      sequence: accountInfo.sequence.toString(),
      fee: {
        amount: [{ denom: 'ukava', amount: feeAmount.toString() }],
        gas: gasLimit.toString(),
      },
      msgs: [{
        type: 'cosmos-sdk/MsgSend',
        value: {
          from_address: fromAddress,
          to_address: toAddress,
          amount: [{ denom: 'ukava', amount }],
        },
      }],
      memo,
    };

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

    // 构建交易体和认证信息 (用于广播)
    const txBody = {
      messages: [{
        '@type': '/cosmos.bank.v1beta1.MsgSend',
        from_address: fromAddress,
        to_address: toAddress,
        amount: [{ denom: 'ukava', amount }],
      }],
      memo,
      timeout_height: '0',
      extension_options: [],
      non_critical_extension_options: [],
    };

    const authInfo = {
      signer_infos: [{
        public_key: {
          '@type': '/cosmos.crypto.secp256k1.PubKey',
          key: Buffer.from(publicKeyHex, 'hex').toString('base64'),
        },
        mode_info: { single: { mode: 'SIGN_MODE_LEGACY_AMINO_JSON' } },
        sequence: accountInfo.sequence.toString(),
      }],
      fee: {
        amount: [{ denom: 'ukava', amount: feeAmount.toString() }],
        gas_limit: gasLimit.toString(),
      },
    };

    return {
      signBytes: new Uint8Array(signBytes),
      signBytesHex: signBytes.toString('hex'),
      txBodyBytes: new Uint8Array(Buffer.from(JSON.stringify(txBody))),
      authInfoBytes: new Uint8Array(Buffer.from(JSON.stringify(authInfo))),
      accountNumber: accountInfo.accountNumber,
      sequence: accountInfo.sequence,
      chainId: this.config.chainId,
      from: fromAddress,
      to: toAddress,
      amount,
      denom: 'ukava',
      memo,
      fee: feeAmount.toString(),
      gasLimit,
    };
  }

  /**
   * 完成交易 (添加签名)
   */
  async completeTx(unsignedTx: UnsignedTxData, signatureHex: string): Promise<SignedTxData> {
    // 解析保存的交易数据
    const txBody = JSON.parse(Buffer.from(unsignedTx.txBodyBytes).toString());
    const authInfo = JSON.parse(Buffer.from(unsignedTx.authInfoBytes).toString());

    // 构建完整的已签名交易
    const signedTx = {
      body: txBody,
      auth_info: authInfo,
      signatures: [Buffer.from(signatureHex, 'hex').toString('base64')],
    };

    const txBytes = Buffer.from(JSON.stringify(signedTx));
    const txHash = crypto.createHash('sha256').update(txBytes).digest('hex').toUpperCase();

    return {
      txBytes: new Uint8Array(txBytes),
      txBytesBase64: txBytes.toString('base64'),
      txHash,
    };
  }

  /**
   * 广播交易
   */
  async broadcastTx(signedTx: SignedTxData): Promise<TxBroadcastResult> {
    try {
      const response = await this.request<{
        tx_response: {
          code: number;
          txhash: string;
          raw_log: string;
          gas_used: string;
          gas_wanted: string;
          height: string;
        };
      }>('/cosmos/tx/v1beta1/txs', 'POST', {
        tx_bytes: signedTx.txBytesBase64,
        mode: 'BROADCAST_MODE_SYNC',
      });

      return {
        success: response.tx_response.code === 0,
        txHash: response.tx_response.txhash,
        code: response.tx_response.code,
        rawLog: response.tx_response.raw_log,
        gasUsed: response.tx_response.gas_used,
        gasWanted: response.tx_response.gas_wanted,
        height: response.tx_response.height,
      };
    } catch (error) {
      return {
        success: false,
        rawLog: (error as Error).message,
      };
    }
  }

  // ===========================================================================
  // 工具方法
  // ===========================================================================

  formatAmount(amount: string, denom: string): string {
    if (denom === 'ukava') {
      const kava = Number(amount) / 1_000_000;
      return `${kava.toFixed(6)} KAVA`;
    }
    return `${amount} ${denom}`;
  }

  toUkava(kava: number | string): string {
    return Math.floor(Number(kava) * 1_000_000).toString();
  }

  fromUkava(ukava: string): number {
    return Number(ukava) / 1_000_000;
  }

  /**
   * 断开连接 (清理资源)
   */
  disconnect(): void {
    // 目前使用 REST API，无需特殊清理
  }
}

// ===========================================================================
// 辅助函数
// ===========================================================================

/**
 * 递归排序对象键 (Amino JSON 签名需要)
 */
function sortObject(obj: unknown): unknown {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  if (Array.isArray(obj)) {
    return obj.map(sortObject);
  }

  const sortedObj: Record<string, unknown> = {};
  const keys = Object.keys(obj as Record<string, unknown>).sort();
  for (const key of keys) {
    sortedObj[key] = sortObject((obj as Record<string, unknown>)[key]);
  }
  return sortedObj;
}

// 导出默认实例
export const kavaTxService = new KavaTxService();
