// 

import { Buffer } from 'buffer'















import {

  CurrencyEngine
} from '../engine/currencyEngine.js'
import { allInfo } from '../info/all.js'
import {

  addNetwork,
  patchCrypto
} from '../utils/bcoinExtender/bcoinExtender.js'
import {
  getForksForNetwork,
  getFromatsForNetwork,
  keysFromEntropy
} from '../utils/coinUtils.js'
import { getXPubFromSeed } from '../utils/formatSelector.js'
import { checkCacheVersion } from '../utils/upgradeCache.js'

import { PluginState } from './pluginState.js'
import { encodeUri, parseUri } from './uri.js'













/**
 * The core currency plugin.
 * Provides information about the currency,
 * as well as generic (non-wallet) functionality.
 */
export class CurrencyTools {
  
  
  
  
  

  // ------------------------------------------------------------------------
  // Private API
  // ------------------------------------------------------------------------
  constructor(
    io,
    { currencyInfo, engineInfo, log }
  ) {
    // Validate that we are a valid EdgeCurrencyTools:
    // eslint-disable-next-line no-unused-vars
    const test = this

    // Public API:
    this.currencyInfo = currencyInfo
    this.pluginId = currencyInfo.pluginId
    // Private API:
    this.io = io
    log(`Creating Currency Plugin for ${this.pluginId}`)
    this.network = engineInfo.network
    const { defaultSettings, pluginId, currencyCode } = this.currencyInfo
    this.state = new PluginState({
      io,
      defaultSettings,
      currencyCode,
      pluginId,
      log
    })
  }

  // ------------------------------------------------------------------------
  // Public API
  // ------------------------------------------------------------------------
  async createPrivateKey(
    walletType,
    opts
  ) {
    const randomBuffer = Buffer.from(this.io.random(32))
    return keysFromEntropy(randomBuffer, this.network, opts)
  }

  async derivePublicKey(walletInfo) {
    return {}
  }

  async internalDerivePublicKey(walletInfo) {
    if (!walletInfo.keys) throw new Error('InvalidKeyName')
    const network = this.network
    const { format, coinType = -1 } = walletInfo.keys
    if (!format || !getFromatsForNetwork(network).includes(format)) {
      throw new Error('InvalidWalletType')
    }
    const seed = walletInfo.keys[`${network}Key`] || ''
    if (!seed) throw new Error('InvalidKeyName')
    const xpub = await getXPubFromSeed({ seed, network, format, coinType })
    return { ...walletInfo.keys, [`${network}Xpub`]: xpub }
  }

  parseUri(uri) {
    return Promise.resolve(parseUri(uri, this.network, this.currencyInfo))
  }

  encodeUri(obj) {
    return Promise.resolve(encodeUri(obj, this.network, this.currencyInfo))
  }

  getSplittableTypes(walletInfo) {
    const { keys: { format = 'bip32' } = {} } = walletInfo
    const forks = getForksForNetwork(this.network)
    return forks
      .filter(network => getFromatsForNetwork(network).includes(format))
      .map(network => `wallet:${network}`)
  }
}

const makeCurrencyPluginFactory = (
  { currencyInfo, engineInfo, bcoinInfo },
  makeIo
) => {
  addNetwork(bcoinInfo)

  return function makePlugin(
    options
  ) {
    const io = makeIo(options)
    // Extend bcoin to support this plugin currency info
    // and faster crypto if possible
    const { secp256k1, pbkdf2 } = io
    patchCrypto(secp256k1, pbkdf2)

    let toolsPromise
    return {
      currencyInfo,

      async makeCurrencyEngine(
        walletInfo,
        options
      ) {
        const tools = await this.makeCurrencyTools()
        const engine = new CurrencyEngine({
          walletInfo,
          engineInfo,
          pluginState: tools.state,
          options,
          io
        })
        await checkCacheVersion(options, walletInfo, engineInfo)
        await engine.load()
        return engine
      },

      makeCurrencyTools() {
        if (toolsPromise != null) return toolsPromise
        const tools = new CurrencyTools(io, {
          currencyInfo,
          engineInfo,
          log: options.log
        })
        toolsPromise = tools.state.load().then(() => tools)
        return toolsPromise
      }
    }
  }
}

export function makeEdgeCorePlugins(
  makeIo
) {
  const out = {}
  for (const info of allInfo) {
    const pluginId = info.currencyInfo.pluginId
    out[pluginId] = makeCurrencyPluginFactory(info, makeIo)
  }
  return out
}
