'use strict';
const fs = require('fs');
const moment = require('moment')
const { alarmConfirmLog } = require('./alarmConfirmLog');
async function inspection (ctx) {
   // 巡查
   try {
      const models = ctx.fs.dc.models;
      const { projectAppId, screenshot = [], } = ctx.request.body

      const now = moment().format()
      const storageData = screenshot.map(s => {
         return {
            projectAppId,
            screenshot: s.url,
            createTime: now,
            description: s.description,
            router: s.router
         }
      })

      await models.AppInspection.bulkCreate(storageData)

      ctx.status = 204;
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function inspectionList (ctx) {
   try {
      const models = ctx.fs.dc.models;
      const { clickHouse } = ctx.app.fs
      const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
      const { pepProjectId, timeStart, timeEnd, projectId, appId, noted } = ctx.query

      let pomsProjectIds = null
      if (!projectId && !appId) {
         let pomsProject = await pomsProjectRange({
            ctx, pepProjectId
         })
         pomsProjectIds = pomsProject.map(p => p.id)
      }

      let findOption = {
         where: {

         },
         order: [['id', 'DESC']],
         include: [{
            model: models.App,
            required: true,
            where: appId ? {
               id: appId
            } : undefined,
            include: {
               model: models.ProjectCorrelation,
               required: true,
               where: Object.assign({},
                  {
                     del: false
                  },
                  projectId ?
                     {
                        id: projectId
                     } : {},
                  pomsProjectIds ? {
                     id: { $in: pomsProjectIds }
                  } : {}
               ),
               attributes: {
                  exclude: ['anxinProjectId', 'createTime', 'createUser']
               }
            }
         }]
      }
      if (timeStart && timeEnd) {
         findOption.where.createTime = { $between: [moment(timeStart).format(), moment(timeEnd).format()] }
      }
      if (noted) {
         if (noted == 'noted') {
            findOption.where.notedTime = { $ne: null }
         } else if (noted == 'unnote') {
            findOption.where.notedTime = null
         }
      }

      const inspectionRes = await models.AppInspection.findAll(findOption)
      let notedUserIds = new Set()
      for (let ins of inspectionRes) {
         if (ins.notedPepUserId) {
            notedUserIds.add(ins.notedPepUserId)
         }
      }
      let userPepRes = notedUserIds.size ?
         await clickHouse.pepEmis.query(`SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id IN (${[...notedUserIds].join(',')}, -1)`).toPromise() :
         []
      for (let ins of inspectionRes) {
         if (ins.notedPepUserId) {
            const corUser = userPepRes.find(up => up.id == ins.notedPepUserId)
            ins.dataValues.notedPepUser = corUser ? corUser.name : ''
         }
      }
      ctx.status = 200;
      ctx.body = inspectionRes
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function notedInspection (ctx) {
   try {
      const models = ctx.fs.dc.models;
      const { inspectionId } = ctx.request.body
      const { userId, pepUserId } = ctx.fs.api

      await models.AppInspection.update({
         notedPepUserId: pepUserId,
         notedTime: moment().format()
      }, {
         where: {
            id: inspectionId
         }
      })

      ctx.status = 204;
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function apiError (ctx) {
   try {
      const { utils: { sendAppearToWeb }, clickHouse } = ctx.app.fs
      const models = ctx.fs.dc.models;
      const { projectAppId, alarmContent, router, statusCode, screenshot = '', type } = ctx.request.body
      const now = moment().format()

      if (!type) {
         throw '请标明告警类型 type'
      }

      const isRestore = statusCode == 200 || statusCode == 204
      if (isRestore) {
         const existRes = await models.AppAlarm.findAll({
            where: {
               projectAppId, router, alarmContent,
               confirmTime: null, type
            }
         })
         if (existRes.length) {
            await models.AppAlarm.update({
               updateTime: now,
               confirmTime: now,
               confirmAuto: true,
            }, {
               where: {
                  id: { $in: existRes.map(e => e.id) }
               }
            })
         }
      } else {
         let storageData = {
            projectAppId, alarmContent, router, statusCode, type
         }
         const existRes = await models.AppAlarm.findOne({
            where: {
               projectAppId, alarmContent, router, statusCode,
               confirmTime: null, type,
            }
         })
         if (existRes) {
            await models.AppAlarm.update({
               updateTime: now
            }, {
               where: {
                  id: existRes.id
               }
            })
         } else {
            const existCount = await models.AppAlarm.count({
               where: {

               }
            })
            storageData.serialNumber = 'WEB' + (existCount < 9 ? '0' + (existCount + 1) : existCount)
            storageData.createTime = now
            storageData.screenshot = screenshot
            await models.AppAlarm.create(storageData)

            //存告警记录
            let constTypes = { 'element': "元素异常", 'apiError': "接口报错 ", 'timeout': "加载超时" }
            let belongsTo = await models.ProjectApp.findOne({
               where: {
                  id: projectAppId
               },
               include: [{
                  model: models.ProjectCorrelation,
                  attributes: ['id', 'name', 'pepProjectId'],
                  where: { del: false }
               }]
            })
            if (belongsTo) {
               let appName = await models.App.findOne({
                  where: {
                     id: belongsTo.appId
                  },
                  attributes: ['name'],
               })
               let pInfo = belongsTo.projectCorrelation.dataValues;//归属项目
               let pId = pInfo.id;//归属项目id
               let pepProjects = pInfo.pepProjectId ? await clickHouse.projectManage.query(`
               SELECT id, project_name FROM t_pim_project WHERE id=${pInfo.pepProjectId}`
               ).toPromise() : [];

               let data = {
                  projectCorrelationId: pId,
                  alarmInfo: { messageMode: 'AlarmGeneration', sourceName: appName.name, content: alarmContent, type: constTypes[type] },//AlarmGeneration代表告警首次产生
                  time: now,
                  type: '应用异常'
               }
               let r = await models.AlarmAppearRecord.create(data, { returning: true });
               let dynamic = {
                  time: r.dataValues.time,
                  alarmAppearId: r.dataValues.id,
                  projectCorrelationId: r.dataValues.projectCorrelationId,
                  type: 1//发现
               }
               await models.LatestDynamicList.create(dynamic);


               //消息推送到前端
               let project = {//构造数据结构
                  pomsProject: [{
                     id: pId,
                     name: pInfo.name,
                     pepProject: { projectName: pepProjects.length ? pepProjects[0].project_name : '' }
                  }]
               }
               await sendAppearToWeb([data], 'app', project);
            }
         }

      }

      ctx.status = 200;
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function apiErrorList (ctx) {
   try {
      const models = ctx.fs.dc.models;
      const { clickHouse } = ctx.app.fs
      const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
      const { keyword, errType, confirmState, state, sustainTimeStart, sustainTimeEnd, limit, page, pepProjectId, toExport } = ctx.query

      let pomsProject = await pomsProjectRange({
         ctx, pepProjectId,
         keywordTarget: 'pepProject',
         keyword,
      })
      const pomsProjectIds = pomsProject.map(p => p.id)
      let findOption = {
         distinct: true,
         where: {
            $or: []
         },
         include: [{
            model: models.App,
            where: {

            },
            attributes: {
               exclude: ['projectId']
            },
            include: [{
               model: models.ProjectCorrelation,
               where: {

               },
               attributes: {
                  exclude: ['createTime', 'createUser', 'anxinProjectId',]
               },
            }]
         }]
      }
      if (keyword) {
         findOption.where.$or.push(
            {
               '$app->projectCorrelations.id$': {
                  $in: pomsProjectIds
               },
            }
         )
         findOption.where.$or.push(
            {
               '$app.name$': { $like: `%${keyword}%` }
            },
         )
      } else {
         // findOption.where['$app->projectCorrelations.id$'] = {
         //    $in: pomsProjectIds
         // }
         findOption.include[0].include[0].where.id = { $in: pomsProjectIds }
      }

      if (errType) {
         findOption.where.type = errType // element / apiError
      }
      if (confirmState || state) {
         if (confirmState == 'confirmd' || state == 'histroy') {
            findOption.where.confirmTime = { $ne: null }
         } else if (confirmState == 'unconfirmed' || state == 'new') {
            findOption.where.confirmTime = null
         }
      }
      if (sustainTimeStart && sustainTimeEnd) {
         findOption.where.$or = findOption.where.$or.concat([
            {
               createTime: { $between: [moment(sustainTimeStart).format(), moment(sustainTimeEnd).format()] },
            },
            {
               updateTime: { $between: [moment(sustainTimeStart).format(), moment(sustainTimeEnd).format()] }
            },
            {
               createTime: { $lte: moment(sustainTimeStart).format() },
               updateTime: { $gte: moment(sustainTimeEnd).format() },
            }
         ])
      }
      if (!toExport) {//非导出时考虑分页
         if (limit) {
            findOption.limit = limit
         }
         if (page && limit) {
            findOption.offset = page * limit
         }
      }
      if (!findOption.where.$or.length) {
         delete findOption.where.$or
      }
      const listRes = await models.AppAlarm.findAndCountAll(findOption)

      for (let lr of listRes.rows) {
         if (lr.app && lr.app.projectCorrelations) {
            for (let p of lr.app.projectCorrelations) {
               let corProjectCorrelations = pomsProject.find(pr => pr.id == p.id)
               if (corProjectCorrelations) {
                  p.dataValues = corProjectCorrelations
               }
            }
         }
      }
      if (toExport) {//数据导出相关
         await exportAppAlarms(ctx, listRes);
      } else {
         ctx.status = 200;
         ctx.body = listRes
      }
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function exportAppAlarms (ctx, listRes) {
   let typeData = { element: "元素异常", apiError: "接口报错 ", timeout: "加载超时" }
   try {
      const { utils: { simpleExcelDown, getExportAlarmHeader } } = ctx.app.fs;
      let header = await getExportAlarmHeader('app');
      let exportData = []
      for (let { dataValues: item } of listRes.rows) {
         let projectName = [];
         for (let project of item.app.projectCorrelations) {
            if (project.dataValues.name) {
               projectName.push(project.dataValues.name)
            } else {
               projectName.push(project.dataValues.pepProject.projectName)
            }
         }
         item.projectName = projectName.join('\r\n');//项目名称

         item.appName = item.app.dataValues.name;
         item.url = item.app.dataValues.url;
         item.type = item.type ? typeData[item.type] : '无';//异常类型

         let time = moment(item.confirmTime || item.updateTime || moment().format("YYYY-MM-DD HH:mm:ss")).diff(moment(item.createTime), 'seconds')
         item.sustainTime = time < 60 ? '< 1分钟' : time > 3600 ? Math.floor(time / 3600) + '小时' + Math.floor((time - Math.floor(time / 3600) * 3600) / 60) + '分钟' : Math.floor((time - Math.floor(time / 3600) * 3600) / 60) + '分钟';

         item.alarmContent = item.alarmContent || '无';
         item.updateTime = item.updateTime || '无';
         item.confirm = item.confirm || '无';
         item.confirmTime = item.confirmTime || '无';

         exportData.push(item)
      }
      const fileName = `应用异常列表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
      const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName })
      const fileData = fs.readFileSync(filePath);
      ctx.status = 200;
      ctx.set('Content-Type', 'application/x-xls');
      ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
      ctx.body = fileData;
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

async function confirmApiError (ctx) {
   try {
      const models = ctx.fs.dc.models;
      const { confirm, appAlarmId = [], confirmPost } = ctx.request.body
      await models.AppAlarm.update({
         confirm,
         confirmTime: moment().format()
      }, {
         where: {
            id: { $in: appAlarmId }
         }
      })

      await alarmConfirmLog(ctx, confirmPost, confirm);//告警确认日志

      ctx.status = 204;
   } catch (error) {
      ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
      ctx.status = 400;
      ctx.body = {
         message: typeof error == 'string' ? error : undefined
      }
   }
}

module.exports = {
   inspection,
   inspectionList,
   notedInspection,
   apiError,
   apiErrorList,
   confirmApiError,
};