/* 引入 公共 工具类 */
const cTool = require('../tools')
const Router = require('express').Router
const router = Router()
const bodyParser = require('body-parser')
const multer = require('multer')
const upload = multer({ dest: 'upload_tmp/' })
const FormData = require('form-data')
const axios = require('axios')
const conf = require('../config')
const _ = require('lodash')
const md5 = require('md5-node')
const fs = require('fs')
const path = require('path')
/* 支持 finally函数 */
require('promise.prototype.finally').shim()

/**
 * 禁止响应头 中设置 cookie值
 * 这里 代理 全部 没有重置 cookie 值
 */

/* 通过API统一过拦截器，接口代理转发请求 */
const agentProcessor = () => {
  return (req, res) => {
    let exp = new RegExp(['v', 'i', 'd', 'e', 'o', '-', 's', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', '$'].join(''), 'gi')
    if (exp.test(req.baseUrl) && req.body.type) {
      postDirOperate(req, res)
      return
    }

    let timestamp = new Date().getTime()
    const signStr = req.headers['cur-auth'] + ':' + req.params[0] + ':' + timestamp
    const uuid = md5(signStr)

    let headers = _.assignIn({}, req.headers)
    let options = {}
    try {
      options = {
        timeout: 30 * 1000,
        url: req.params[0],
        baseURL: conf.agentApiUrl,
        method: req.method,
        data: req.body,
        params: req.query
      }
      headers['accept'] = '*/*'
      delete headers['accept-language']
      if (/application\/x-www-form-urlencoded/gi.test(headers['content-type'])) {
        let str = ''
        for (let k in options.data) {
          str += k + '=' + options.data[k] + '&'
        }
        options.data = str.substr(0, str.length - 1)
      }
      if (/multipart\/form-data/gi.test(headers['content-type'])) {
        let _obj = _.assignIn({}, req.body)
        req.files.forEach((elem, i) => {
          /* 缓存文件中 名称替换，上传名称一致 会被覆盖 */
          fs.renameSync(elem.path, elem.destination + elem.originalname)
          /* 增加 日期目录 */
          let dateDir = cTool.convertTime.durationToDateString(new Date().getTime())
          /* 增加 学员目录 */
          let stuDir = req.headers['cur-auth'] || '其他'
          fs.stat(elem.destination + dateDir, (error, stats) => {
            if (error) {
              fs.mkdir(elem.destination + dateDir, (error) => {
                if (error) { console.log(error); return false }
                fs.mkdir(elem.destination + dateDir + '/' + stuDir, (error) => {
                  if (error) { console.log(error); return false }
                  fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + stuDir + '/' + elem.originalname)
                  fs.unlink(elem.destination + elem.originalname, (error) => { if (error) console.log(error) })
                })
              })
            } else {
              fs.stat(elem.destination + dateDir + '/' + stuDir, (error, stats) => {
                if (error) {
                  fs.mkdir(elem.destination + dateDir + '/' + stuDir, (error) => {
                    if (error) { console.log(error); return false }
                    fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + stuDir + '/' + elem.originalname)
                    fs.unlink(elem.destination + elem.originalname, (error) => { if (error) console.log(error) })
                  })
                } else {
                  fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + stuDir + '/' + elem.originalname)
                  fs.unlink(elem.destination + elem.originalname, (error) => { if (error) console.log(error) })
                }
              })
            }
          })
          _obj[elem.fieldname] = fs.createReadStream(elem.destination + elem.originalname)
        })
        let fro = new FormData()
        for (let key in _obj) {
          fro.append(key, _obj[key])
        }
        options.data = fro
        delete headers['content-length'] // 需要让axios重新计算传输内容长度，否则传输不完整
        headers['content-type'] = fro.getHeaders()['content-type']
      }
      axios.defaults.headers = headers
      axios.defaults.withCredentials = true
      /* 日志记录，重新修改 2019.01.17 */
      let _log = {
        reqTime: timestamp, // 请求时长
        sys: req.headers['tenant'], // 系统
        status: '', // 请求状态
        name: req.headers['cur-name'], // 姓名
        auth: req.headers['cur-auth'], // 账号
        IP: getClientIP(req), // IP值
        uuid: uuid, // uuid
        url: options.url, // 请求地址
        type: '', // 1:视频 2:课后作业 3:作业内容
        self: '', // 根据类型值，自定义 字符串参数
        param: JSON.stringify(options) // 请求参数 JSON str
      }

      /* 兼容老版本，登录接口 清除_SUP; 退出登录 清除_SUP */
      if (/tenant\/user\/login/gi.test(options.url) || /tenant\/user\/code-login/gi.test(options.url) || /v3\/sso\/logout/gi.test(options.url)) {
        res.clearCookie('_SUP', { path: '/', domain: '.ezijing.com' })
      }
      /* 重新转发请求 */
      axios(options).then((data) => {
        _log.reqTime = (new Date().getTime() - _log.reqTime) + 'ms'
        _log.status = 200
        setPorxyHeader(data, res)
        res.status(200).send(data.data)
      }).catch((e) => {
        _log.reqTime = (new Date().getTime() - _log.reqTime) + 'ms'
        _log.status = e.response.status || 500
        /* 返回执行代码出错 或者 服务器请求错误 */
        if (e.response && e.response.data) {
          /* 如果未登录 强制 弹出到 登录页 */
          setPorxyHeader(e.response, res)
          res.status(e.response.status).json(e.response.data)
          console.error(uuid + String.fromCharCode(0x001) + JSON.stringify(options) + String.fromCharCode(0x001) + JSON.stringify(e.response))
        } else {
          res.status(500).json({ message: '系统错误，请稍后重试或联系管理员', errMsg: 'Error Proxy Request or BackData Excute Error', code: 500 })
          console.error(uuid, e)
        }
      }).finally(() => {
        let _str = ''
        for (let key in _log) {
          _str += _log[key] + String.fromCharCode(0x001)
        }
        console.log(_str + ' -- end')
      })
    } catch (e) {
      res.status(500).json({ message: '系统错误，请稍后重试或联系管理员', errMsg: 'Network Server Excute Error', code: 500 })
      console.error('system error:', uuid, e)
    }
  }
}

const setPorxyHeader = (data, res) => {
  if (data.headers['set-cookie']) {
    /* 不准许 服务端设置 _SUP */
    let _sCookie = data.headers['set-cookie'] || []
    for (let i = 0; i < _sCookie.length; i++) {
      if (/_SUP=/gi.test(_sCookie[i])) {
        _sCookie.splice(i, 1)
        break
      }
    }
    for (let key in data.headers) {
      res.append(key, data.headers[key])
    }
  }
}

router.use(bodyParser.json({ limit: '600kb' }))
router.use(bodyParser.urlencoded({ extended: true, limit: '600kb' }))

const postDirOperate = (req, res) => {
  let operate = req.body.operate || ''
  let timestamp = Math.floor(new Date().getTime() / 100000000) + '00000000'
  let md5Str = md5('ezijing.com' + timestamp)
  if (operate === 'get-md5') {
    res.status(200)
    res.json({ 'timestamp': timestamp, 'MD5-auth': md5Str })
    return
  }
  let dir = req.body.dir || ''
  let auth = req.headers.auth || ''

  if (auth === md5Str) {
    let json = {}
    json.dirPath = dir
    json.arrJson = {}
    fs.stat(dir, (error, stats) => {
      if (error) {
        json.arrJson.msg = '当前目录，或文件不存在'
        res.status(200)
        res.json(json)
      } else {
        if (operate === 'delete') {
          if (stats.isFile()) {
            fs.unlink(dir, (error) => { if (error) console.log(error) })
          } else if (stats.isDirectory()) {
            _rmdir(dir, () => { json.arrJson.msg = '删除成功' })
          }
          json.arrJson.msg = json.arrJson.msg || '操作完成，请重新查询上级目录'
          res.status(200)
          res.json(json)
        } else if (operate === 'list') {
          let pa = fs.readdirSync(dir)
          let dirStr = 'dir'
          let dirCount = 0
          let fileStr = 'file'
          let fileCount = 0
          for (let j = 0; j < pa.length; j++) {
            let elem = pa[j]
            let stat = fs.statSync(path.resolve(dir, elem))
            if (stat.isFile()) {
              json.arrJson[fileStr + (fileCount++)] = elem
            }
            if (stat.isDirectory()) {
              json.arrJson[dirStr + (dirCount++)] = elem
            }
          }
          res.status(200)
          res.json(json)
        } else {
          res.status(200)
          res.json({ 'msg': 'operate 参数错误' })
        }
      }
    })
  } else {
    res.status(200)
    res.json({ 'msg': '没有权限，访问该接口' })
  }
}

const _rmdir = (dir, callback) => {
  let pa = fs.readdirSync(dir)
  for (let j = 0; j < pa.length; j++) {
    let elem = pa[j]
    let stat = fs.statSync(path.resolve(dir, elem))
    if (stat.isFile()) {
      fs.unlink(path.resolve(dir, elem), () => {})
    }
    if (stat.isDirectory()) {
      _rmdir(path.resolve(dir, elem), callback)
    }
  }
  fs.rmdirSync(dir)
  callback()
}

const ckeditorUpload = (req, res) => {
  /* 文件上传 ckeditor4.5以上返回格式 */
  function sendHtml (url, msg) {
    res.json({
      'uploaded': url ? 1 : 0,
      'fileName': 'image',
      'url': url,
      'error': {
        'message': msg
      }
    })
  }
  if (req.files.length) {
    if (!req.files[0].mimetype.startsWith('image')) { sendHtml('', '文件类型错误，请上传图片'); return }
    if (req.files[0].size > 10 * 1024 * 1024) { sendHtml('', '图片大小超限'); return }
    let headers = _.assignIn({}, req.headers)
    let options = {}
    options = {
      timeout: 30 * 1000,
      url: 'tenant/util/upload-image',
      baseURL: conf.agentApiUrl,
      method: req.method,
      data: req.body,
      params: req.query
    }
    headers['accept'] = '*/*'
    delete headers['accept-language']
    if (/multipart\/form-data/gi.test(headers['content-type'])) {
      let _obj = _.assignIn({}, req.body)
      req.files.forEach((elem, i) => {
        /* 缓存文件中 名称替换，上传名称一致 会被覆盖 */
        fs.renameSync(elem.path, elem.destination + elem.originalname)
        /* 增加 日期目录 */
        let dateDir = 'ckeditor ' + cTool.convertTime.durationToDateString(new Date().getTime())
        /* 异步 处理文件 */
        fs.stat(elem.destination + dateDir, (error, stats) => {
          if (error) {
            fs.mkdir(elem.destination + dateDir, (error) => {
              if (error) { console.log(error); return false }
              fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + elem.originalname)
            })
          } else {
            fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + elem.originalname)
          }
        })
        _obj['image'] = fs.createReadStream(elem.destination + elem.originalname)
      })
      let fro = new FormData()
      for (let key in _obj) {
        fro.append(key, _obj[key])
      }
      options.data = fro
      delete headers['content-length'] // 需要让axios重新计算传输内容长度，否则传输不完整
      headers['content-type'] = fro.getHeaders()['content-type']
    }
    let s = ''
    let arr = headers.cookie.split('; ')
    for (let i = 0; i < arr.length; i++) {
      if (/^_SUP/g.test(arr[i])) {
        s = arr[i].split('=')[1]
        break
      }
    }
    s = decodeURIComponent(s || '')
    let ticket = ''
    s = s.match(/"([\d\w=]+)";\}$/)
    if (s && s.length === 2) {
      s = new Buffer(s[1], 'base64').toString() // eslint-disable-line
      s = s.slice(12, s.search('-TGT') + 4)
      ticket = s.split('').reverse().join('')
    }
    headers['token'] = ticket
    headers['tenant'] = 'sofia'
    axios.defaults.headers = headers

    /* 重新转发请求 */
    axios(options).then((data) => {
      if (data.data.success) {
        sendHtml(data.data.url, '')
      } else {
        sendHtml('', '上传错误，请重试')
      }
      req.files.forEach((elem, i) => {
        fs.stat(elem.destination + elem.originalname, (error, stats) => {
          if (error) { return }
          fs.unlink(elem.destination + elem.originalname, (error) => { if (error) console.log(error) })
        })
      })
    }).catch((e) => {
      sendHtml('', '上传错误，请重试1')
    })
  }
}
const getClientIP = (req) => {
  return req.headers['x-forwarded-for'] ||
    req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    req.connection.socket.remoteAddress
}
/* 统一API接口 */
router.post('/api/ckeditor/img/upload', upload.any(), ckeditorUpload)
router.use('/api/*', upload.any(), agentProcessor('api'))

module.exports = router
