const { contextBridge, ipcRenderer } = require('electron')
const needle = require('needle')
const { createCipheriv, publicEncrypt, constants, randomBytes, createHash } = require('crypto')
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')

const sendMessage = (action, status, data, message) => {
  ipcRenderer.send(action, { status, data, message })
}

let isInitedApi = false
const EVENT_NAMES = {
  request: 'request',
  inited: 'inited',
}
const eventNames = Object.values(EVENT_NAMES)
const events = {
  request: null,
}
const allSources = ['kw', 'kg', 'tx', 'wy', 'mg']
const supportQualitys = {
  kw: ['128k', '320k', 'flac'],
  kg: ['128k', '320k', 'flac'],
  tx: ['128k', '320k', 'flac'],
  wy: ['128k', '320k', 'flac'],
  mg: ['128k', '320k', 'flac'],
}
const supportActions = {
  kw: ['musicUrl'],
  kg: ['musicUrl'],
  tx: ['musicUrl'],
  wy: ['musicUrl'],
  mg: ['musicUrl'],
  xm: ['musicUrl'],
}

const handleRequest = (context, { requestKey, data }) => {
  // console.log(data)
  if (!events.request) return sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, 'Request event is not defined')
  try {
    events.request.call(context, { source: data.source, action: data.action, info: data.info }).then(response => {
      let sendData = {
        requestKey,
      }
      switch (data.action) {
        case 'musicUrl':
          sendData.result = {
            source: data.source,
            action: data.action,
            data: {
              type: data.info.type,
              url: response,
            },
          }
          break
      }
      sendMessage(USER_API_RENDERER_EVENT_NAME.response, true, sendData)
    }).catch(err => {
      sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, err.message)
    })
  } catch (err) {
    sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, err.message)
  }
}

/**
 *
 * @param {*} context
 * @param {*} info {
 *                    openDevTools: false,
 *                    status: true,
 *                    message: 'xxx',
 *                    sources: {
 *                         kw: ['128k', '320k', 'flac'],
 *                         kg: ['128k', '320k', 'flac'],
 *                         tx: ['128k', '320k', 'flac'],
 *                         wy: ['128k', '320k', 'flac'],
 *                         mg: ['128k', '320k', 'flac'],
 *                     }
 *                 }
 */
const handleInit = (context, info) => {
  if (!info) {
    sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, 'Init failed')
    // sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
    return
  }
  if (info.openDevTools === true) {
    sendMessage(USER_API_RENDERER_EVENT_NAME.openDevTools)
  }
  if (!info.status) {
    sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, 'Init failed')
    // sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
    return
  }
  const sourceInfo = {
    sources: {},
  }
  try {
    for (const source of allSources) {
      const userSource = info.sources[source]
      if (!userSource || userSource.type !== 'music') continue
      const qualitys = supportQualitys[source]
      const actions = supportActions[source]
      sourceInfo.sources[source] = {
        type: 'music',
        actions: actions.filter(a => userSource.actions.includes(a)),
        qualitys: qualitys.filter(q => userSource.qualitys.includes(q)),
      }
    }
  } catch (error) {
    console.log(error)
    sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, error.message)
    return
  }
  sendMessage(USER_API_RENDERER_EVENT_NAME.init, true, sourceInfo)

  ipcRenderer.on(USER_API_RENDERER_EVENT_NAME.request, (event, data) => {
    handleRequest(context, data)
  })
}

contextBridge.exposeInMainWorld('lx', {
  EVENT_NAMES,
  request(url, { method = 'get', timeout, headers, body, form, formData }, callback) {
    let options = { headers }
    let data
    if (body) {
      data = body
    } else if (form) {
      data = form
      // data.content_type = 'application/x-www-form-urlencoded'
      options.json = false
    } else if (formData) {
      data = formData
      // data.content_type = 'multipart/form-data'
      options.json = false
    }
    options.response_timeout = timeout

    let request = needle.request(method, url, data, options, (err, resp, body) => {
      if (!err) {
        body = resp.body = resp.raw.toString()
        try {
          resp.body = JSON.parse(resp.body)
        } catch (_) {}
        body = resp.body
      }
      callback(err, {
        statusCode: resp.statusCode,
        statusMessage: resp.statusMessage,
        headers: resp.headers,
        bytes: resp.bytes,
        raw: resp.raw,
        body: body,
      }, body)
    }).request

    return () => {
      if (!request.aborted) request.abort()
      request = null
    }
  },
  send(eventName, data) {
    return new Promise((resolve, reject) => {
      if (!eventNames.includes(eventName)) return reject(new Error('The event is not supported: ' + eventName))
      switch (eventName) {
        case EVENT_NAMES.inited:
          if (isInitedApi) return
          isInitedApi = true
          handleInit(this, data)
          break
        default:
          resolve(new Error('Unknown event name: ' + eventName))
      }
    })
  },
  on(eventName, handler) {
    if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
    switch (eventName) {
      case EVENT_NAMES.request:
        events.request = handler
        break
    }
  },
  utils: {
    crypto: {
      aesEncrypt(buffer, mode, key, iv) {
        const cipher = createCipheriv(mode, key, iv)
        return Buffer.concat([cipher.update(buffer), cipher.final()])
      },
      rsaEncrypt(buffer, key) {
        buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
        return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer)
      },
      randomBytes(size) {
        return randomBytes(size)
      },
      md5(str) {
        return createHash('md5').update(str).digest('hex')
      },
    },
    buffer: {
      from(...args) {
        return Buffer.from(...args)
      },
      bufToString(buf, format) {
        return Buffer.from(buf, 'binary').toString(format)
      },
    },
  },
  version: '1.1.0',
  // removeEvent(eventName, handler) {
  //   if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
  //   let handlers
  //   switch (eventName) {
  //     case EVENT_NAMES.request:
  //       handlers = events.request
  //       break
  //   }
  //   for (let index = 0; index < handlers.length; index++) {
  //     if (handlers[index] === handler) {
  //       handlers.splice(index, 1)
  //       break
  //     }
  //   }
  // },
  // removeAllEvents() {
  //   for (const handlers of Object.values(events)) {
  //     handlers.splice(0, handlers.length)
  //   }
  // },
})
