import { Injectable, Logger } from '@nestjs/common';
import { HDKey } from '@scure/bip32';
import { mnemonicToSeedSync, validateMnemonic } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
import { createHash } from 'crypto';
import { bech32 } from 'bech32';
import { Wallet } from 'ethers';
import { ChainTypeEnum } from '@/domain/enums';

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

/**
 * 链配置
 */
const CHAIN_CONFIG: Record<ChainTypeEnum, { derivationPath: string; prefix?: string }> = {
  [ChainTypeEnum.KAVA]: {
    derivationPath: "m/44'/459'/0'/0/0",
    prefix: 'kava',
  },
  [ChainTypeEnum.DST]: {
    derivationPath: "m/44'/118'/0'/0/0",
    prefix: 'dst',
  },
  [ChainTypeEnum.BSC]: {
    derivationPath: "m/44'/60'/0'/0/0",
  },
};

/**
 * 助记词派生适配器
 * 从 BIP39 助记词派生多链钱包地址
 */
@Injectable()
export class MnemonicDerivationAdapter {
  private readonly logger = new Logger(MnemonicDerivationAdapter.name);

  /**
   * 验证助记词格式
   */
  validateMnemonic(mnemonic: string): boolean {
    return validateMnemonic(mnemonic, wordlist);
  }

  /**
   * 从助记词派生所有支持链的地址
   */
  deriveAllAddresses(mnemonic: string): MnemonicDerivedAddress[] {
    if (!this.validateMnemonic(mnemonic)) {
      throw new Error('Invalid mnemonic');
    }

    const seed = mnemonicToSeedSync(mnemonic);
    const addresses: MnemonicDerivedAddress[] = [];

    this.logger.log('[DERIVE] Starting mnemonic address derivation');

    // KAVA
    const kavaAddress = this.deriveCosmosAddress(seed, CHAIN_CONFIG[ChainTypeEnum.KAVA]);
    addresses.push({ chainType: ChainTypeEnum.KAVA, address: kavaAddress });
    this.logger.log(`[DERIVE] KAVA address: ${kavaAddress}`);

    // DST
    const dstAddress = this.deriveCosmosAddress(seed, CHAIN_CONFIG[ChainTypeEnum.DST]);
    addresses.push({ chainType: ChainTypeEnum.DST, address: dstAddress });
    this.logger.log(`[DERIVE] DST address: ${dstAddress}`);

    // BSC
    const bscAddress = this.deriveEvmAddress(seed, CHAIN_CONFIG[ChainTypeEnum.BSC]);
    addresses.push({ chainType: ChainTypeEnum.BSC, address: bscAddress });
    this.logger.log(`[DERIVE] BSC address: ${bscAddress}`);

    return addresses;
  }

  /**
   * 验证助记词是否对应指定的地址
   */
  verifyMnemonic(
    mnemonic: string,
    expectedAddresses: Array<{ chainType: string; address: string }>,
  ): { valid: boolean; matchedAddresses: string[]; mismatchedAddresses: string[] } {
    if (!this.validateMnemonic(mnemonic)) {
      return { valid: false, matchedAddresses: [], mismatchedAddresses: [] };
    }

    const derivedAddresses = this.deriveAllAddresses(mnemonic);
    const derivedMap = new Map(derivedAddresses.map((a) => [a.chainType, a.address.toLowerCase()]));

    const matchedAddresses: string[] = [];
    const mismatchedAddresses: string[] = [];

    for (const expected of expectedAddresses) {
      const chainType = expected.chainType as ChainTypeEnum;
      const derivedAddress = derivedMap.get(chainType);

      if (derivedAddress && derivedAddress === expected.address.toLowerCase()) {
        matchedAddresses.push(expected.chainType);
      } else {
        mismatchedAddresses.push(expected.chainType);
      }
    }

    return {
      valid: mismatchedAddresses.length === 0 && matchedAddresses.length > 0,
      matchedAddresses,
      mismatchedAddresses,
    };
  }

  /**
   * 派生 Cosmos 地址 (bech32 格式)
   */
  private deriveCosmosAddress(
    seed: Uint8Array,
    config: { derivationPath: string; prefix?: string },
  ): string {
    const hdkey = HDKey.fromMasterSeed(seed);
    const childKey = hdkey.derive(config.derivationPath);

    if (!childKey.publicKey) {
      throw new Error('Failed to derive public key');
    }

    const hash = createHash('sha256').update(childKey.publicKey).digest();
    const addressHash = createHash('ripemd160').update(hash).digest();
    const words = bech32.toWords(addressHash);

    return bech32.encode(config.prefix!, words);
  }

  /**
   * 派生 EVM 地址
   */
  private deriveEvmAddress(seed: Uint8Array, config: { derivationPath: string }): string {
    const hdkey = HDKey.fromMasterSeed(seed);
    const childKey = hdkey.derive(config.derivationPath);

    if (!childKey.privateKey) {
      throw new Error('Failed to derive private key');
    }

    const wallet = new Wallet(Buffer.from(childKey.privateKey).toString('hex'));
    return wallet.address;
  }
}
