text: 'use strict'
  
  const crypto = require('crypto')
  const parse = require('url').parse
  const bodyify = require('querystring').stringify
  
  const eapiKey = 'e82ckenh8dichen8'
  const linuxapiKey = 'rFgB&h#%2?^eDg:Q'
  
  const decrypt = (buffer, key) => {
  	const decipher = crypto.createDecipheriv('aes-128-ecb', key, '')
  	return Buffer.concat([decipher.update(buffer), decipher.final()])
  }
  
  const encrypt = (buffer, key) => {
  	const cipher = crypto.createCipheriv('aes-128-ecb', key, '')
  	return Buffer.concat([cipher.update(buffer), cipher.final()])
  }
  
  module.exports = {
  	eapi: {
  		encrypt: buffer => encrypt(buffer, eapiKey),
  		decrypt: buffer => decrypt(buffer, eapiKey),
  		encryptRequest: (url, object) => {
  			url = parse(url)
  			const text = JSON.stringify(object)
  			const message = `nobody${url.path}use${text}md5forencrypt`
  			const digest = crypto.createHash('md5').update(message).digest('hex')
  			const data = `${url.path}-36cd479b6b5-${text}-36cd479b6b5-${digest}`
  			return {
  				url: url.href.replace(/\w*api/, 'eapi'),
  				body: bodyify({
  					params: module.exports.eapi.encrypt(Buffer.from(data)).toString('hex').toUpperCase()
  				})
  			}
  		}
  	},
  	linuxapi: {
  		encrypt: buffer => encrypt(buffer, linuxapiKey),
  		decrypt: buffer => decrypt(buffer, linuxapiKey),
  		encryptRequest: (url, object) => {
  			url = parse(url)
  			const text = JSON.stringify({method: 'POST', url: url.href, params: object})
  			return {
  				url: url.resolve('/api/linux/forward'),
  				body: bodyify({
  					eparams: module.exports.linuxapi.encrypt(Buffer.from(text)).toString('hex').toUpperCase()
  				})
  			}
  		}
  	},
  	miguapi: {
  		encrypt: object => {
  			const text = JSON.stringify(object)
  			const derive = (password, salt, keyLength, ivSize) => { // EVP_BytesToKey
  				salt = salt || Buffer.alloc(0)
  				const keySize = keyLength / 8
  				const repeat = Math.ceil((keySize + ivSize * 8) / 32)
  				const buffer = Buffer.concat(Array(repeat).fill(null).reduce(
  					result => result.concat(crypto.createHash('md5').update(Buffer.concat([result.slice(-1)[0], password, salt])).digest()),
  					[Buffer.alloc(0)]
  				))
  				return {
  					key: buffer.slice(0, keySize),
  					iv: buffer.slice(keySize, keySize + ivSize)
  				}
  			}
  			const password = Buffer.from(crypto.randomBytes(32).toString('hex')), salt = crypto.randomBytes(8)
  			const key = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8asrfSaoOb4je+DSmKdriQJKWVJ2oDZrs3wi5W67m3LwTB9QVR+cE3XWU21Nx+YBxS0yun8wDcjgQvYt625ZCcgin2ro/eOkNyUOTBIbuj9CvMnhUYiR61lC1f1IGbrSYYimqBVSjpifVufxtx/I3exReZosTByYp4Xwpb1+WAQIDAQAB\n-----END PUBLIC KEY-----'
  			const secret = derive(password, salt, 256, 16)
  			const cipher = crypto.createCipheriv('aes-256-cbc', secret.key, secret.iv)
  			return bodyify({
  				data: Buffer.concat([Buffer.from('Salted__'), salt, cipher.update(Buffer.from(text)), cipher.final()]).toString('base64'),
  				secKey: crypto.publicEncrypt({key, padding: crypto.constants.RSA_PKCS1_PADDING}, password).toString('base64')
  			})
  		}
  	},
  	base64: {
  		encode: (text, charset) => Buffer.from(text, charset).toString('base64').replace(/\+/g, '-').replace(/\//g, '_'),
  		decode: (text, charset) => Buffer.from(text.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString(charset)
  	},
  	uri: {
  		retrieve: id => {
  			id = id.toString().trim()
  			const key = '3go8&$8*3*3h0k(2)2'
  			const string = Array.from(Array(id.length).keys()).map(index => String.fromCharCode(id.charCodeAt(index) ^ key.charCodeAt(index % key.length))).join('')
  			const result = crypto.createHash('md5').update(string).digest('base64').replace(/\//g, '_').replace(/\+/g, '-')
  			return `http://p1.music.126.net/${result}/${id}`
  		}
  	},
  	md5: {
  		digest: value => crypto.createHash('md5').update(value).digest('hex'),
  		pipe: source => new Promise((resolve, reject) => {
  			const digest = crypto.createHash('md5').setEncoding('hex')
  			source.pipe(digest)
  			.on('error', error => reject(error))
  			.once('finish', () => resolve(digest.read()))
  		})
  	}
  }