import * as crypto from 'crypto';
import { bech32 } from 'bech32';

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

export interface ChainConfig {
  name: string;
  prefix: string;
  coinType: number;
  curve: 'secp256k1' | 'ed25519';
  derivationPath: string;
}

export const CHAIN_CONFIGS: Record<string, ChainConfig> = {
  kava: {
    name: 'Kava',
    prefix: 'kava',
    coinType: 459,
    curve: 'secp256k1',
    derivationPath: "m/44'/459'/0'/0/0",
  },
  cosmos: {
    name: 'Cosmos Hub',
    prefix: 'cosmos',
    coinType: 118,
    curve: 'secp256k1',
    derivationPath: "m/44'/118'/0'/0/0",
  },
  osmosis: {
    name: 'Osmosis',
    prefix: 'osmo',
    coinType: 118,
    curve: 'secp256k1',
    derivationPath: "m/44'/118'/0'/0/0",
  },
  ethereum: {
    name: 'Ethereum',
    prefix: '0x',
    coinType: 60,
    curve: 'secp256k1',
    derivationPath: "m/44'/60'/0'/0/0",
  },
};

// =============================================================================
// 地址派生工具
// =============================================================================

/**
 * 从公钥派生 Bech32 地址 (Cosmos 系列)
 *
 * 流程:
 * 1. 公钥 → SHA256 → RIPEMD160 → 20字节地址
 * 2. 20字节地址 → Bech32 编码
 */
export function deriveCosmosAddress(publicKeyHex: string, prefix: string): string {
  // 移除可能的 0x 前缀
  const cleanHex = publicKeyHex.startsWith('0x') ? publicKeyHex.slice(2) : publicKeyHex;
  const publicKeyBytes = Buffer.from(cleanHex, 'hex');

  // 对于 secp256k1，需要压缩公钥 (33 bytes)
  // 如果是未压缩公钥 (65 bytes)，需要先压缩
  let compressedKey: Buffer = publicKeyBytes;
  if (publicKeyBytes.length === 65) {
    compressedKey = compressSecp256k1PublicKey(publicKeyBytes);
  } else if (publicKeyBytes.length === 64) {
    // 没有前缀的未压缩公钥
    const uncompressed = Buffer.concat([Buffer.from([0x04]), publicKeyBytes]);
    compressedKey = compressSecp256k1PublicKey(uncompressed);
  }

  // SHA256 → RIPEMD160
  const sha256Hash = crypto.createHash('sha256').update(compressedKey).digest();
  const ripemd160Hash = crypto.createHash('ripemd160').update(sha256Hash).digest();

  // Bech32 编码
  const words = bech32.toWords(ripemd160Hash);
  const address = bech32.encode(prefix, words);

  return address;
}

/**
 * 从公钥派生以太坊地址
 *
 * 流程:
 * 1. 未压缩公钥 (去掉 04 前缀) → Keccak256 → 取后 20 字节
 */
export function deriveEthereumAddress(publicKeyHex: string): string {
  const cleanHex = publicKeyHex.startsWith('0x') ? publicKeyHex.slice(2) : publicKeyHex;
  const publicKeyBytes = Buffer.from(cleanHex, 'hex');

  // 需要未压缩公钥的 x, y 坐标 (64 bytes)
  let uncompressedKey: Buffer;
  if (publicKeyBytes.length === 33) {
    // 压缩公钥，需要解压
    uncompressedKey = decompressSecp256k1PublicKey(publicKeyBytes);
  } else if (publicKeyBytes.length === 65) {
    // 未压缩公钥，去掉 04 前缀
    uncompressedKey = publicKeyBytes.slice(1) as Buffer;
  } else if (publicKeyBytes.length === 64) {
    uncompressedKey = publicKeyBytes;
  } else {
    throw new Error(`Invalid public key length: ${publicKeyBytes.length}`);
  }

  // Keccak256 (使用 keccak256 而不是 sha3-256)
  const { keccak_256 } = require('@noble/hashes/sha3');
  const hash = keccak_256(uncompressedKey);

  // 取后 20 字节
  const addressBytes = hash.slice(-20);
  const address = '0x' + Buffer.from(addressBytes).toString('hex');

  return checksumAddress(address);
}

/**
 * 从 Ed25519 公钥派生地址 (用于某些链)
 */
export function deriveEd25519Address(publicKeyHex: string, prefix: string): string {
  const cleanHex = publicKeyHex.startsWith('0x') ? publicKeyHex.slice(2) : publicKeyHex;
  const publicKeyBytes = Buffer.from(cleanHex, 'hex');

  // SHA256 → RIPEMD160
  const sha256Hash = crypto.createHash('sha256').update(publicKeyBytes).digest();
  const ripemd160Hash = crypto.createHash('ripemd160').update(sha256Hash).digest();

  // Bech32 编码
  const words = bech32.toWords(ripemd160Hash);
  const address = bech32.encode(prefix, words);

  return address;
}

/**
 * 压缩 secp256k1 公钥
 */
function compressSecp256k1PublicKey(uncompressed: Buffer): Buffer {
  if (uncompressed.length !== 65 || uncompressed[0] !== 0x04) {
    throw new Error('Invalid uncompressed public key');
  }

  const x = uncompressed.slice(1, 33);
  const y = uncompressed.slice(33, 65);

  // 判断 y 是奇数还是偶数
  const prefix = y[31] % 2 === 0 ? 0x02 : 0x03;

  return Buffer.concat([Buffer.from([prefix]), x]);
}

/**
 * 解压缩 secp256k1 公钥
 * 使用椭圆曲线数学: y² = x³ + 7 (mod p)
 */
function decompressSecp256k1PublicKey(compressed: Buffer): Buffer {
  if (compressed.length !== 33) {
    throw new Error('Invalid compressed public key');
  }

  // secp256k1 曲线参数
  const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');

  const prefix = compressed[0];
  const x = BigInt('0x' + compressed.slice(1).toString('hex'));

  // 计算 y² = x³ + 7 (mod p)
  const xCubed = modPow(x, 3n, p);
  const ySquared = (xCubed + 7n) % p;

  // 计算平方根 (p ≡ 3 mod 4, 所以 y = ySquared^((p+1)/4) mod p)
  let y = modPow(ySquared, (p + 1n) / 4n, p);

  // 根据前缀选择正确的 y 值
  const isYOdd = y % 2n === 1n;
  const shouldBeOdd = prefix === 0x03;

  if (isYOdd !== shouldBeOdd) {
    y = p - y;
  }

  // 转换为 Buffer (64 bytes: x || y)
  const xBuffer = Buffer.from(x.toString(16).padStart(64, '0'), 'hex');
  const yBuffer = Buffer.from(y.toString(16).padStart(64, '0'), 'hex');

  return Buffer.concat([xBuffer, yBuffer]);
}

/**
 * 模幂运算
 */
function modPow(base: bigint, exponent: bigint, modulus: bigint): bigint {
  let result = 1n;
  base = base % modulus;

  while (exponent > 0n) {
    if (exponent % 2n === 1n) {
      result = (result * base) % modulus;
    }
    exponent = exponent / 2n;
    base = (base * base) % modulus;
  }

  return result;
}

/**
 * EIP-55 校验和地址
 */
function checksumAddress(address: string): string {
  const { keccak_256 } = require('@noble/hashes/sha3');
  const addr = address.toLowerCase().replace('0x', '');
  const hash = Buffer.from(keccak_256(Buffer.from(addr, 'utf8'))).toString('hex');

  let result = '0x';
  for (let i = 0; i < addr.length; i++) {
    if (parseInt(hash[i], 16) >= 8) {
      result += addr[i].toUpperCase();
    } else {
      result += addr[i];
    }
  }
  return result;
}

// =============================================================================
// 地址派生服务
// =============================================================================

export interface DerivedAddress {
  chain: string;
  chainName: string;
  prefix: string;
  address: string;
  derivationPath: string;
  publicKeyHex: string;
}

/**
 * 地址派生服务
 *
 * 注意：TSS keygen 生成的是聚合公钥，不是派生公钥。
 * 对于需要不同链的地址，我们直接使用聚合公钥派生地址，
 * 而不是像 HD 钱包那样从种子派生。
 *
 * 这意味着：
 * - 所有链使用相同的公钥
 * - 不同链的地址只是编码方式不同
 * - 这是 TSS 钱包的标准做法
 */
export class AddressDerivationService {
  /**
   * 从 TSS 聚合公钥派生指定链的地址
   */
  deriveAddress(publicKeyHex: string, chain: string): DerivedAddress {
    const config = CHAIN_CONFIGS[chain];
    if (!config) {
      throw new Error(`Unsupported chain: ${chain}`);
    }

    let address: string;

    if (chain === 'ethereum') {
      address = deriveEthereumAddress(publicKeyHex);
    } else if (config.curve === 'ed25519') {
      address = deriveEd25519Address(publicKeyHex, config.prefix);
    } else {
      // Cosmos 系列 (kava, cosmos, osmosis 等)
      address = deriveCosmosAddress(publicKeyHex, config.prefix);
    }

    return {
      chain,
      chainName: config.name,
      prefix: config.prefix,
      address,
      derivationPath: config.derivationPath,
      publicKeyHex,
    };
  }

  /**
   * 派生所有支持的链地址
   */
  deriveAllAddresses(publicKeyHex: string): DerivedAddress[] {
    const addresses: DerivedAddress[] = [];

    for (const chain of Object.keys(CHAIN_CONFIGS)) {
      try {
        const derived = this.deriveAddress(publicKeyHex, chain);
        addresses.push(derived);
      } catch (err) {
        console.error(`Failed to derive ${chain} address:`, err);
      }
    }

    return addresses;
  }

  /**
   * 验证地址格式
   */
  validateAddress(address: string, chain: string): boolean {
    const config = CHAIN_CONFIGS[chain];
    if (!config) {
      return false;
    }

    if (chain === 'ethereum') {
      return /^0x[a-fA-F0-9]{40}$/.test(address);
    }

    try {
      const decoded = bech32.decode(address);
      return decoded.prefix === config.prefix;
    } catch {
      return false;
    }
  }

  /**
   * 获取支持的链列表
   */
  getSupportedChains(): ChainConfig[] {
    return Object.values(CHAIN_CONFIGS);
  }
}

// 导出单例
export const addressDerivationService = new AddressDerivationService();
