import { Injectable, Logger } from '@nestjs/common';
import { keccak256, getBytes, sha256, ripemd160 } from 'ethers';
import { bech32 } from 'bech32';
import { EvmAddress } from '@/domain/value-objects';
import { ChainTypeEnum } from '@/domain/enums';

export interface DerivedAddress {
  chainType: ChainTypeEnum;
  address: string;
}

/**
 * 地址派生适配器
 * 从 MPC 公钥派生多链钱包地址
 */
@Injectable()
export class AddressDerivationAdapter {
  private readonly logger = new Logger(AddressDerivationAdapter.name);

  /**
   * 从压缩公钥派生 EVM 地址
   *
   * @param compressedPublicKey 压缩格式的公钥 (33 bytes, 0x02/0x03 开头)
   * @returns EVM 地址
   */
  deriveEvmAddress(compressedPublicKey: string): string {
    // 移除 0x 前缀
    const pubKeyHex = compressedPublicKey.replace('0x', '');

    // 验证压缩公钥格式
    if (pubKeyHex.length !== 66) {
      throw new Error(`Invalid compressed public key length: ${pubKeyHex.length}, expected 66`);
    }

    const prefix = pubKeyHex.slice(0, 2);
    if (prefix !== '02' && prefix !== '03') {
      throw new Error(`Invalid compressed public key prefix: ${prefix}, expected 02 or 03`);
    }

    // 解压缩公钥
    const uncompressedPubKey = this.decompressPublicKey(pubKeyHex);

    // 移除 04 前缀（非压缩公钥标识）
    const pubKeyWithoutPrefix = uncompressedPubKey.slice(2);

    // Keccak256 哈希
    const hash = keccak256(getBytes('0x' + pubKeyWithoutPrefix));

    // 取最后 20 bytes 作为地址
    const address = '0x' + hash.slice(-40);

    return address;
  }

  /**
   * 解压缩公钥
   *
   * 使用 secp256k1 曲线解压缩
   */
  private decompressPublicKey(compressedPubKeyHex: string): string {
    const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');

    const prefix = parseInt(compressedPubKeyHex.slice(0, 2), 16);
    const x = BigInt('0x' + compressedPubKeyHex.slice(2));

    // y² = x³ + 7 (mod p)
    const ySquared = (x ** 3n + 7n) % p;

    // 计算模平方根
    let y = this.modPow(ySquared, (p + 1n) / 4n, p);

    // 根据前缀选择 y 的奇偶性
    const isYOdd = y % 2n === 1n;
    const shouldBeOdd = prefix === 0x03;

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

    // 返回非压缩格式 (04 + x + y)
    const xHex = x.toString(16).padStart(64, '0');
    const yHex = y.toString(16).padStart(64, '0');

    return '04' + xHex + yHex;
  }

  /**
   * 模幂运算
   */
  private modPow(base: bigint, exp: bigint, mod: bigint): bigint {
    let result = 1n;
    base = base % mod;
    while (exp > 0n) {
      if (exp % 2n === 1n) {
        result = (result * base) % mod;
      }
      exp = exp / 2n;
      base = (base * base) % mod;
    }
    return result;
  }

  /**
   * 从压缩公钥派生 Cosmos 地址 (bech32 格式)
   *
   * @param compressedPublicKey 压缩格式的公钥 (33 bytes, 0x02/0x03 开头)
   * @param prefix bech32 地址前缀 (如 'kava', 'dst')
   * @returns bech32 格式的地址
   */
  deriveCosmosAddress(compressedPublicKey: string, prefix: string): string {
    // 移除 0x 前缀
    const pubKeyHex = compressedPublicKey.replace('0x', '');

    // 验证压缩公钥格式
    if (pubKeyHex.length !== 66) {
      throw new Error(`Invalid compressed public key length: ${pubKeyHex.length}, expected 66`);
    }

    // SHA256 哈希
    const pubKeyBytes = getBytes('0x' + pubKeyHex);
    const sha256Hash = sha256(pubKeyBytes);

    // RIPEMD160 哈希 (得到 20 bytes)
    const ripemd160Hash = ripemd160(sha256Hash);

    // 转换为 5-bit words 用于 bech32 编码
    const hashBytes = getBytes(ripemd160Hash);
    const words = bech32.toWords(Buffer.from(hashBytes));

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

    this.logger.debug(`Derived Cosmos address with prefix '${prefix}': ${address}`);

    return address;
  }

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

    this.logger.log(`[DERIVE] Starting address derivation for public key: ${compressedPublicKey.slice(0, 20)}...`);

    // EVM 链共用同一个地址
    const evmAddress = this.deriveEvmAddress(compressedPublicKey);
    this.logger.log(`[DERIVE] EVM address derived: ${evmAddress}`);

    // KAVA (EVM 格式 - 0x...) - Kava EVM 兼容链
    addresses.push({
      chainType: ChainTypeEnum.KAVA,
      address: evmAddress,
    });
    this.logger.log(`[DERIVE] KAVA address (EVM): ${evmAddress}`);

    // DST (Cosmos bech32 格式 - dst1...)
    const dstAddress = this.deriveCosmosAddress(compressedPublicKey, 'dst');
    addresses.push({
      chainType: ChainTypeEnum.DST,
      address: dstAddress,
    });
    this.logger.log(`[DERIVE] DST address (Cosmos): ${dstAddress}`);

    // BSC (EVM 格式 - 0x...)
    addresses.push({
      chainType: ChainTypeEnum.BSC,
      address: evmAddress,
    });
    this.logger.log(`[DERIVE] BSC address (EVM): ${evmAddress}`);

    this.logger.log(`[DERIVE] Successfully derived ${addresses.length} addresses from public key`);

    return addresses;
  }

  /**
   * 验证地址格式
   */
  validateEvmAddress(address: string): boolean {
    try {
      EvmAddress.create(address);
      return true;
    } catch {
      return false;
    }
  }
}
