import * as bip39 from 'bip39'
import { BIP32Factory } from 'bip32'
import * as bitcoin from 'bitcoinjs-lib'
import { ethers } from 'ethers'
import { TronWeb } from 'tronweb'
import * as ecc from 'tiny-secp256k1'
import Logger from '@ioc:Adonis/Core/Logger'
import axios from 'axios'

interface WalletBalance {
    address: string
    balance: string
}

interface WalletAddresses {
    btc: WalletBalance
    eth: WalletBalance
    tron: WalletBalance
}

const CONFIG = {
    ETH_API_KEY: process.env.ETH_API_KEY || 'JSYCSZEYYRI1G6CTYW8J6RPXEZGTMBFFMM',
    BITCOIN_API_URL: 'https://blockchain.info/rawaddr',
    ETHERSCAN_API_URL: 'https://api.etherscan.io/v2/api',
    TRON_API_URL: 'https://apilist.tronscan.org/api/account',
    TRON_FULL_HOST: 'https://api.trongrid.io',
    DECIMAL_PLACES: 8,
    BIP_PATHS: {
        BTC: (accountIndex: number) => `m/44'/0'/${accountIndex}'/0/0`,
        ETH: (accountIndex: number) => `m/44'/60'/${accountIndex}'/0/0`,
        TRON: (accountIndex: number) => `m/44'/195'/${accountIndex}'/0/0`
    }
}

const bip32 = BIP32Factory(ecc)

class BlockchainWalletService {
    /**
     * 获取所有支持的区块链地址和余额
     * @param mnemonic 助记词字符串
     * @param accountIndex 账户索引，默认为0
     * @returns 包含所有地址和余额的对象
     */
    public async getAllAddresses(
        mnemonic: string,
        accountIndex: number = 0
    ): Promise<WalletAddresses> {
        // 设置默认返回值
        const defaultResult: WalletAddresses = {
            btc: { address: '-', balance: this.formatBalance(0) },
            eth: { address: '-', balance: this.formatBalance(0) },
            tron: { address: '-', balance: this.formatBalance(0) }
        }

        try {
            // 验证助记词
            this.validateMnemonic(mnemonic)

            // 获取种子
            const seed = this.getSeedFromMnemonic(mnemonic)

            // 并行生成所有地址
            const [btcAddress, ethAddress, tronAddress] = await Promise.all([
                Promise.resolve(this.generateBTCAddress(seed, accountIndex)),
                Promise.resolve(this.generateETHAddress(seed, accountIndex)),
                Promise.resolve(this.generateTRONAddress(seed, accountIndex))
            ])

            // 并行获取所有余额
            const [btcBalance, ethBalance, tronBalance] = await Promise.all([
                this.getBitcoinBalance(btcAddress),
                this.getEthereumBalance(ethAddress),
                this.getTronBalance(tronAddress)
            ])

            return {
                btc: { address: btcAddress, balance: btcBalance },
                eth: { address: ethAddress, balance: ethBalance },
                tron: { address: tronAddress, balance: tronBalance }
            }
        } catch (error) {
            Logger.error(`Failed to get addresses: ${(error as Error).message}`)
            return defaultResult
        }
    }

    /**
     * 格式化余额为统一的8位小数格式
     * @param balance 数值余额
     * @returns 格式化后的字符串余额，固定8位小数
     */
    private formatBalance(balance: number): string {
        return balance.toFixed(CONFIG.DECIMAL_PLACES)
    }

    /**
     * 验证助记词是否有效
     * @param mnemonic 助记词字符串
     * @throws {Error} 如果助记词无效
     */
    private validateMnemonic(mnemonic: string): void {
        if (!bip39.validateMnemonic(mnemonic)) {
            throw new Error('Invalid mnemonic')
        }
    }

    /**
     * 获取助记词的种子
     * @param mnemonic 助记词字符串
     * @returns 种子Buffer
     */
    private getSeedFromMnemonic(mnemonic: string): Buffer {
        this.validateMnemonic(mnemonic)
        return bip39.mnemonicToSeedSync(mnemonic)
    }

    /**
     * 根据给定的路径派生子节点
     * @param seed 助记词生成的种子
     * @param path 路径
     * @returns 派生的子节点
     * @throws {Error} 如果派生过程失败
     */
    private deriveChildNodeFromSeed(seed: Buffer, path: string) {
        try {
            const root = bip32.fromSeed(seed)
            return root.derivePath(path)
        } catch (error) {
            Logger.error(`Failed to derive child node: ${(error as Error).message}`)
            throw new Error(`Key derivation failed: ${(error as Error).message}`)
        }
    }

    /**
     * 生成比特币(BTC)地址
     * @param seed 助记词生成的种子
     * @param accountIndex 账户索引，默认为0
     * @param network 网络类型，默认为主网
     * @returns BTC地址
     * @throws {Error} 如果地址生成失败
     */
    private generateBTCAddress(
        seed: Buffer,
        accountIndex: number = 0,
        network = bitcoin.networks.bitcoin
    ): string {
        const path = CONFIG.BIP_PATHS.BTC(accountIndex)
        const child = this.deriveChildNodeFromSeed(seed, path)

        if (!child.publicKey) {
            throw new Error('Failed to generate BTC public key')
        }

        const { address } = bitcoin.payments.p2pkh({
            pubkey: Buffer.from(child.publicKey),
            network
        })

        if (!address) {
            throw new Error('Failed to generate BTC address')
        }

        return address
    }

    /**
     * 生成以太坊(ETH)地址
     * @param seed 助记词生成的种子
     * @param accountIndex 账户索引，默认为0
     * @returns ETH地址
     * @throws {Error} 如果地址生成失败
     */
    private generateETHAddress(seed: Buffer, accountIndex: number = 0): string {
        const path = CONFIG.BIP_PATHS.ETH(accountIndex)
        const child = this.deriveChildNodeFromSeed(seed, path)

        if (!child.privateKey) {
            throw new Error('Failed to generate ETH private key')
        }

        const privateKeyHex = Buffer.from(child.privateKey).toString('hex')
        const wallet = new ethers.Wallet(`0x${privateKeyHex}`)

        return wallet.address
    }

    /**
     * 生成波场(TRON)地址
     * @param seed 助记词生成的种子
     * @param accountIndex 账户索引，默认为0
     * @returns TRON地址
     * @throws {Error} 如果地址生成失败
     */
    private generateTRONAddress(seed: Buffer, accountIndex: number = 0): string {
        const path = CONFIG.BIP_PATHS.TRON(accountIndex)
        const child = this.deriveChildNodeFromSeed(seed, path)

        if (!child.privateKey) {
            throw new Error('Failed to generate TRON private key')
        }

        const privateKeyHex = Buffer.from(child.privateKey).toString('hex')
        const tronWeb = new TronWeb({ fullHost: CONFIG.TRON_FULL_HOST })
        const account = tronWeb.address.fromPrivateKey(privateKeyHex)

        if (typeof account !== 'string') {
            throw new Error('Failed to generate TRON address')
        }

        return account
    }

    /**
     * 生成比特币(BTC)地址 - 公共接口
     * @param mnemonic 助记词字符串
     * @param accountIndex 账户索引，默认为0
     * @param network 网络类型，默认为主网
     * @returns BTC地址
     */
    public getBTCAddress(
        mnemonic: string,
        accountIndex: number = 0,
        network = bitcoin.networks.bitcoin
    ): string {
        const seed = this.getSeedFromMnemonic(mnemonic)
        return this.generateBTCAddress(seed, accountIndex, network)
    }

    /**
     * 生成以太坊(ETH)地址 - 公共接口
     * @param mnemonic 助记词字符串
     * @param accountIndex 账户索引，默认为0
     * @returns ETH地址
     */
    public getETHAddress(mnemonic: string, accountIndex: number = 0): string {
        const seed = this.getSeedFromMnemonic(mnemonic)
        return this.generateETHAddress(seed, accountIndex)
    }

    /**
     * 生成波场(TRON)地址 - 公共接口
     * @param mnemonic 助记词字符串
     * @param accountIndex 账户索引，默认为0
     * @returns TRON地址
     */
    public getTRONAddress(mnemonic: string, accountIndex: number = 0): string {
        const seed = this.getSeedFromMnemonic(mnemonic)
        return this.generateTRONAddress(seed, accountIndex)
    }

    /**
     * 获取比特币余额
     * @param address 比特币地址
     * @returns 比特币余额（8位小数格式字符串）
     */
    public async getBitcoinBalance(address: string): Promise<string> {
        try {
            const response = await axios.get(`${CONFIG.BITCOIN_API_URL}/${address}`)
            const balance = response.data.final_balance / 1e8
            return this.formatBalance(balance)
        } catch (error) {
            Logger.error(`Failed to get Bitcoin balance: ${(error as Error).message}`)
            return this.formatBalance(0)
        }
    }

    /**
     * 获取以太坊余额
     * @param address 以太坊地址
     * @returns 以太坊余额（8位小数格式字符串）
     */
    public async getEthereumBalance(address: string): Promise<string> {
        try {
            const response = await axios.get(CONFIG.ETHERSCAN_API_URL, {
                params: {
                    chainid: 1,
                    module: 'account',
                    action: 'balance',
                    address: address,
                    tag: 'latest',
                    apikey: CONFIG.ETH_API_KEY
                }
            })

            if (response.data.status !== '1') {
                Logger.warn(`Etherscan API error: ${response.data.message}`)
                return this.formatBalance(0)
            }

            const balance = Number(response.data.result) / 1e18
            return this.formatBalance(balance)
        } catch (error) {
            Logger.error(`Failed to get Ethereum balance: ${(error as Error).message}`)
            return this.formatBalance(0)
        }
    }

    /**
     * 获取波场USDT余额
     * @param address 波场地址
     * @returns 波场USDT余额（8位小数格式字符串）
     */
    public async getTronBalance(address: string): Promise<string> {
        try {
            const response = await axios.get(`${CONFIG.TRON_API_URL}?address=${address}`)
            const data = response.data

            // 查找USDT TRC20代币余额
            let usdtBalance = 0
            if (data.trc20token_balances && Array.isArray(data.trc20token_balances)) {
                const usdtToken = data.trc20token_balances.find(
                    (token: any) => token.tokenAbbr === 'USDT'
                )

                if (usdtToken) {
                    const decimals = Number(usdtToken.tokenDecimal) || 6
                    usdtBalance = Number(usdtToken.balance) / Math.pow(10, decimals)
                }
            }

            return this.formatBalance(usdtBalance)
        } catch (error) {
            Logger.error(`Failed to get TRON balance: ${(error as Error).message}`)
            return this.formatBalance(0)
        }
    }
}

export default new BlockchainWalletService()
