const fs = require('fs')
const CryptoJS = require('crypto-js')
const rawCrypto = require('crypto')
const NodeRSA = require('node-rsa')
const jwt = require('jsonwebtoken')
const axios = require('axios')
const request = axios.create({ timeout: 3000 })

const { sshRecordPath, hostListPath, keyPath } = require('../config')

const readSSHRecord = () => {
  let list
  try {
    list = JSON.parse(fs.readFileSync(sshRecordPath, 'utf8'))
  } catch (error) {
    console.log('读取ssh-record错误, 即将重置ssh列表: ', error)
    writeSSHRecord([])
  }
  return list || []
}

const writeSSHRecord = (record = []) => {
  fs.writeFileSync(sshRecordPath, JSON.stringify(record, null, 2))
}

const readHostList = () => {
  let list
  try {
    list = JSON.parse(fs.readFileSync(hostListPath, 'utf8'))
  } catch (error) {
    console.log('读取host-list错误, 即将重置host列表: ', error)
    writeHostList([])
  }
  return list || []
}

const writeHostList = (record = []) => {
  fs.writeFileSync(hostListPath, JSON.stringify(record, null, 2))
}

const readKey = () => {
  let keyObj = JSON.parse(fs.readFileSync(keyPath, 'utf8'))
  return keyObj
}

const writeKey = (keyObj = {}) => {
  fs.writeFileSync(keyPath, JSON.stringify(keyObj, null, 2))
}

// 为空时请求本地IP
const getNetIPInfo = async (ip = '') => {
  try {
    let date = getUTCDate(8)
    let ipUrls = [`http://ip-api.com/json/${ ip }?lang=zh-CN`, `http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=${ ip }`]
    let result = await Promise.allSettled(ipUrls.map(url => request.get(url)))
    let [ipApi, pconline] = result
    if(ipApi.status === 'fulfilled') {
      let { query: ip, country, regionName, city } = ipApi.value.data
      // console.log({ ip, country, city: regionName + city })
      return { ip, country, city: regionName + city, date }
    }
    if(pconline.status === 'fulfilled') {
      let { ip, pro, city, addr } = pconline.value.data
      // console.log({ ip, country: pro || addr, city })
      return { ip, country: pro || addr, city, date }
    }
    throw Error('获取IP信息API出错,请排查或更新API')
  } catch (error) {
    console.error('getIpInfo Error: ', error)
    return {
      ip: '未知',
      country: '未知',
      city: '未知',
      error
    }
  }
}

const throwError = ({ status = 500, msg = 'defalut error' } = {}) => {
  const err = new Error(msg)
  err.status = status // 主动抛错
  throw err
}

const isIP = (ip = '') => {
  const isIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/
  const isIPv6 = /^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}|:((:[\da−fA−F]1,4)1,6|:)|:((:[\da−fA−F]1,4)1,6|:)|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?|([\da−fA−F]1,4:)6:|([\da−fA−F]1,4:)6:/
  return isIPv4.test(ip) || isIPv6.test(ip)
}

const randomStr = (e) =>{
  e = e || 16
  let str = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678',
    a = str.length,
    res = ''
  for (let i = 0; i < e; i++) res += str.charAt(Math.floor(Math.random() * a))
  return res
}

// 校验token与登录IP
const verifyAuth = (token, clientIp) =>{
  token = AESDecrypt(token) // 先aes解密
  const { commonKey } = readKey()
  try {
    const { exp } = jwt.verify(token, commonKey)
    // console.log('校验token：', new Date(), '---', new Date(exp * 1000))
    if(Date.now() > (exp * 1000)) return { code: -1, msg: 'token expires' } // 过期

    let lastLoginIp = global.loginRecord[0] ? global.loginRecord[0].ip : ''
    console.log('校验客户端IP：', clientIp)
    console.log('最后登录的IP：', lastLoginIp)
    // 判断: (生产环境)clientIp与上次登录成功IP不一致
    if(isProd() && (!lastLoginIp || !clientIp || !clientIp.includes(lastLoginIp))) {
      return { code: -1, msg: '登录IP发生变化, 需重新登录' } // IP与上次登录访问的不一致
    }
    // console.log('token验证成功')
    return { code: 1, msg: 'success' } // 验证成功
  } catch (error) {
    // console.log('token校验错误：', error)
    return { code: -2, msg: error } // token错误, 验证失败
  }
}

const isProd = () => {
  const EXEC_ENV = process.env.EXEC_ENV || 'production'
  return EXEC_ENV === 'production'
}

// rsa非对称 私钥解密
const RSADecrypt = (ciphertext) => {
  if(!ciphertext) return
  let { privateKey } = readKey()
  privateKey = AESDecrypt(privateKey) // 先解密私钥
  const rsakey = new NodeRSA(privateKey)
  rsakey.setOptions({ encryptionScheme: 'pkcs1' }) // Must Set It When Frontend Use jsencrypt
  const plaintext = rsakey.decrypt(ciphertext, 'utf8')
  return plaintext
}

// aes对称 加密(default commonKey)
const AESEncrypt = (text, key) => {
  if(!text) return
  let { commonKey } = readKey()
  let ciphertext = CryptoJS.AES.encrypt(text, key || commonKey).toString()
  return ciphertext
}

// aes对称 解密(default commonKey)
const AESDecrypt = (ciphertext, key) => {
  if(!ciphertext) return
  let { commonKey } = readKey()
  let bytes = CryptoJS.AES.decrypt(ciphertext, key || commonKey)
  let originalText = bytes.toString(CryptoJS.enc.Utf8)
  return originalText
}

// sha1 加密(不可逆)
const SHA1Encrypt = (clearText) => {
  return rawCrypto.createHash('sha1').update(clearText).digest('hex')
}

// 获取UTC-x时间
const getUTCDate = (num = 8) => {
  let date = new Date()
  let now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
    date.getUTCDate(), date.getUTCHours() + num,
    date.getUTCMinutes(), date.getUTCSeconds())
  return new Date(now_utc)
}

module.exports = {
  readSSHRecord,
  writeSSHRecord,
  readHostList,
  writeHostList,
  getNetIPInfo,
  throwError,
  isIP,
  readKey,
  writeKey,
  randomStr,
  verifyAuth,
  isProd,
  RSADecrypt,
  AESEncrypt,
  AESDecrypt,
  SHA1Encrypt,
  getUTCDate
}