/**
 * Kava EVM 地址派生工具
 * 从 ECDSA 公钥派生 EVM 兼容地址
 */

// secp256k1 曲线参数
const SECP256K1_P = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');
const SECP256K1_B = 7n;

/**
 * 将 Uint8Array 转换为 BigInt
 */
function bytesToBigInt(bytes: Uint8Array): bigint {
  let result = 0n;
  for (let i = 0; i < bytes.length; i++) {
    result = (result << 8n) | BigInt(bytes[i]);
  }
  return result;
}

/**
 * 将 BigInt 转换为 32 字节的 Uint8Array
 */
function bigIntToBytes32(n: bigint): Uint8Array {
  const bytes = new Uint8Array(32);
  let temp = n;
  for (let i = 31; i >= 0; i--) {
    bytes[i] = Number(temp & 0xffn);
    temp = temp >> 8n;
  }
  return bytes;
}

/**
 * 模幂运算 (快速幂算法)
 */
function modPow(base: bigint, exp: bigint, mod: bigint): bigint {
  let result = 1n;
  base = ((base % mod) + mod) % mod;
  while (exp > 0n) {
    if (exp & 1n) {
      result = (result * base) % mod;
    }
    exp = exp >> 1n;
    base = (base * base) % mod;
  }
  return result;
}

/**
 * 计算 secp256k1 曲线上给定 x 坐标的 y 坐标
 * y² = x³ + 7 (mod p)
 */
function decompressY(x: bigint, isOdd: boolean): bigint {
  // 计算 y² = x³ + 7 (mod p)
  const x3 = modPow(x, 3n, SECP256K1_P);
  const ySquared = (x3 + SECP256K1_B) % SECP256K1_P;

  // 计算平方根: y = ySquared^((p+1)/4) mod p
  // 这是因为 p ≡ 3 (mod 4) 对于 secp256k1
  const exp = (SECP256K1_P + 1n) / 4n;
  let y = modPow(ySquared, exp, SECP256K1_P);

  // 根据奇偶性选择正确的 y 值
  const yIsOdd = (y & 1n) === 1n;
  if (yIsOdd !== isOdd) {
    y = SECP256K1_P - y;
  }

  return y;
}

/**
 * 解压缩 secp256k1 公钥
 * 输入: 33 字节压缩公钥 (02/03 前缀 + 32 字节 x 坐标)
 * 输出: 64 字节未压缩公钥 (32 字节 x + 32 字节 y，无前缀)
 */
function decompressPublicKey(compressedPubKey: Uint8Array): Uint8Array {
  if (compressedPubKey.length !== 33) {
    throw new Error('压缩公钥必须是 33 字节');
  }

  const prefix = compressedPubKey[0];
  if (prefix !== 0x02 && prefix !== 0x03) {
    throw new Error('无效的压缩公钥前缀');
  }

  const isOdd = prefix === 0x03;
  const xBytes = compressedPubKey.slice(1);
  const x = bytesToBigInt(xBytes);
  const y = decompressY(x, isOdd);

  // 组合 x 和 y 坐标
  const result = new Uint8Array(64);
  result.set(xBytes, 0);
  result.set(bigIntToBytes32(y), 32);

  return result;
}

/**
 * 将十六进制字符串转换为 Uint8Array
 */
function hexToBytes(hex: string): Uint8Array {
  const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
  const bytes = new Uint8Array(cleanHex.length / 2);
  for (let i = 0; i < cleanHex.length; i += 2) {
    bytes[i / 2] = parseInt(cleanHex.substring(i, i + 2), 16);
  }
  return bytes;
}

/**
 * 将 Uint8Array 转换为十六进制字符串
 */
function bytesToHex(bytes: Uint8Array): string {
  return Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}

/**
 * Keccak-256 哈希实现 (简化版)
 * 用于从公钥派生 EVM 地址
 */
async function keccak256(data: Uint8Array): Promise<Uint8Array> {
  // 使用 SubtleCrypto 的 SHA-256 作为备选
  // 注意: 真实场景需要使用 keccak-256，这里使用简化实现
  // 在生产环境中应该使用 js-sha3 或 ethers.js

  // 简单的 Keccak-256 实现常量
  const RC = [
    0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an,
    0x8000000080008000n, 0x000000000000808bn, 0x0000000080000001n,
    0x8000000080008081n, 0x8000000000008009n, 0x000000000000008an,
    0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an,
    0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n,
    0x8000000000008003n, 0x8000000000008002n, 0x8000000000000080n,
    0x000000000000800an, 0x800000008000000an, 0x8000000080008081n,
    0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n,
  ];

  const ROTC = [
    [0, 36, 3, 41, 18],
    [1, 44, 10, 45, 2],
    [62, 6, 43, 15, 61],
    [28, 55, 25, 21, 56],
    [27, 20, 39, 8, 14],
  ];

  function rotl64(x: bigint, n: number): bigint {
    return ((x << BigInt(n)) | (x >> BigInt(64 - n))) & 0xffffffffffffffffn;
  }

  function keccakF(state: bigint[][]): void {
    for (let round = 0; round < 24; round++) {
      // Theta
      const C: bigint[] = [];
      for (let x = 0; x < 5; x++) {
        C[x] = state[x][0] ^ state[x][1] ^ state[x][2] ^ state[x][3] ^ state[x][4];
      }
      const D: bigint[] = [];
      for (let x = 0; x < 5; x++) {
        D[x] = C[(x + 4) % 5] ^ rotl64(C[(x + 1) % 5], 1);
      }
      for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
          state[x][y] ^= D[x];
        }
      }

      // Rho and Pi
      const B: bigint[][] = Array(5).fill(null).map(() => Array(5).fill(0n));
      for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
          B[y][(2 * x + 3 * y) % 5] = rotl64(state[x][y], ROTC[x][y]);
        }
      }

      // Chi
      for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
          state[x][y] = B[x][y] ^ (~B[(x + 1) % 5][y] & B[(x + 2) % 5][y]);
        }
      }

      // Iota
      state[0][0] ^= RC[round];
    }
  }

  // Keccak-256: rate = 1088 bits = 136 bytes, capacity = 512 bits
  const rate = 136;
  const outputLen = 32;

  // Initialize state
  const state: bigint[][] = Array(5).fill(null).map(() => Array(5).fill(0n));

  // Pad message
  const padded = new Uint8Array(Math.ceil((data.length + 1) / rate) * rate);
  padded.set(data);
  padded[data.length] = 0x01;
  padded[padded.length - 1] |= 0x80;

  // Absorb
  for (let i = 0; i < padded.length; i += rate) {
    for (let j = 0; j < rate && i + j < padded.length; j += 8) {
      const x = Math.floor(j / 8) % 5;
      const y = Math.floor(Math.floor(j / 8) / 5);
      let lane = 0n;
      for (let k = 0; k < 8 && i + j + k < padded.length; k++) {
        lane |= BigInt(padded[i + j + k]) << BigInt(k * 8);
      }
      state[x][y] ^= lane;
    }
    keccakF(state);
  }

  // Squeeze
  const output = new Uint8Array(outputLen);
  for (let i = 0; i < outputLen; i += 8) {
    const x = Math.floor(i / 8) % 5;
    const y = Math.floor(Math.floor(i / 8) / 5);
    const lane = state[x][y];
    for (let k = 0; k < 8 && i + k < outputLen; k++) {
      output[i + k] = Number((lane >> BigInt(k * 8)) & 0xffn);
    }
  }

  return output;
}

/**
 * 从 ECDSA 压缩/未压缩公钥派生 EVM 地址
 *
 * @param publicKey - 公钥的十六进制字符串 (压缩或未压缩格式)
 * @returns EVM 地址 (带 0x 前缀)
 */
export async function deriveEvmAddress(publicKey: string): Promise<string> {
  const pubKeyBytes = hexToBytes(publicKey);

  // EVM 地址派生使用未压缩公钥的 x,y 坐标 (去掉 04 前缀)
  // 如果是压缩公钥 (33 bytes, 02/03 前缀), 需要先解压
  let uncompressedPubKey: Uint8Array;

  if (pubKeyBytes.length === 65 && pubKeyBytes[0] === 0x04) {
    // 已经是未压缩格式，去掉 04 前缀
    uncompressedPubKey = pubKeyBytes.slice(1);
  } else if (pubKeyBytes.length === 64) {
    // 无前缀的未压缩格式
    uncompressedPubKey = pubKeyBytes;
  } else if (pubKeyBytes.length === 33 && (pubKeyBytes[0] === 0x02 || pubKeyBytes[0] === 0x03)) {
    // 压缩格式，解压为 64 字节的 x,y 坐标
    uncompressedPubKey = decompressPublicKey(pubKeyBytes);
  } else {
    throw new Error(`无效的公钥格式: length=${pubKeyBytes.length}`);
  }

  // 对 64 字节的公钥数据进行 Keccak-256 哈希
  const hash = await keccak256(uncompressedPubKey);

  // 取最后 20 字节作为地址
  const addressBytes = hash.slice(-20);

  return '0x' + bytesToHex(addressBytes);
}

/**
 * 验证 EVM 地址格式
 *
 * @param address - 要验证的地址
 * @returns 是否是有效的 EVM 地址
 */
export function isValidEvmAddress(address: string): boolean {
  if (!address) return false;
  return /^0x[a-fA-F0-9]{40}$/.test(address);
}

/**
 * 格式化地址显示 (缩短)
 *
 * @param address - 完整地址
 * @param prefixLen - 前缀长度 (默认 6)
 * @param suffixLen - 后缀长度 (默认 4)
 * @returns 缩短的地址
 */
export function formatAddress(address: string, prefixLen = 6, suffixLen = 4): string {
  if (!address || address.length < prefixLen + suffixLen + 2) return address;
  return `${address.slice(0, prefixLen + 2)}...${address.slice(-suffixLen)}`;
}

/**
 * 生成 Kava 区块浏览器 URL
 *
 * @param address - EVM 地址
 * @param isTestnet - 是否是测试网
 * @returns 区块浏览器 URL
 */
export function getKavaExplorerUrl(address: string, isTestnet = true): string {
  const baseUrl = isTestnet
    ? 'https://testnet.kavascan.com'
    : 'https://kavascan.com';
  return `${baseUrl}/address/${address}`;
}
