package com.njcn.gather.system.log.service.impl;

import cn.afterturn.easypoi.csv.entity.CsvExportParams;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.constant.LogInfo;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.db.mybatisplus.constant.UserConstant;
import com.njcn.gather.system.log.mapper.SysLogAuditMapper;
import com.njcn.gather.system.log.pojo.param.SysLogParam;
import com.njcn.gather.system.log.pojo.po.SysLogAudit;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.gather.system.log.util.CSVUtil;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.ReflectCommonUtil;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author caozehui
 * @date 2024-11-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLogAudit> implements ISysLogAuditService {

    private final ISysUserService sysUserService;

    @Override
    public Page<SysLogAudit> listSysLogAudit(SysLogParam.QueryParam param) {
        QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(param)) {
            queryWrapper.like(StrUtil.isNotBlank(param.getUserName()), "sys_log_audit.User_name", param.getUserName())
                    .ge(StrUtil.isNotBlank(param.getSearchBeginTime()), "sys_log_audit.Log_time", LocalDateTimeUtil.parse(param.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_FORMATTER))
                    .le(StrUtil.isNotBlank(param.getSearchEndTime()), "sys_log_audit.Log_time", LocalDateTimeUtil.parse(param.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_FORMATTER));
        }
        queryWrapper.orderByDesc("sys_log_audit.Log_time");
        return this.page(new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)), queryWrapper);
    }

    @Override
    public void exportCSV(SysLogParam.QueryParam param) {
        QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(param)) {
            queryWrapper.like(StrUtil.isNotBlank(param.getUserName()), "sys_log_audit.User_name", param.getUserName())
                    .ge(StrUtil.isNotBlank(param.getSearchBeginTime()), "sys_log_audit.Log_time", LocalDateTimeUtil.parse(param.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_FORMATTER))
                    .le(StrUtil.isNotBlank(param.getSearchEndTime()), "sys_log_audit.Log_time", LocalDateTimeUtil.parse(param.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_FORMATTER));
        }
        queryWrapper.orderByDesc("sys_log_audit.Log_time");
        List<SysLogAudit> list = this.list(queryWrapper);
        CsvExportParams params = new CsvExportParams();
        CSVUtil.exportCsv("日志数据.csv", params, SysLogAudit.class, list);
    }

    /**
     * 暂时废弃，有需要再重新写
     *
     * @param param .
     */
    @Override
    @Deprecated
    public void analyse(SysLogParam.QueryParam param) {
        QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(param)) {
            queryWrapper.like(StrUtil.isNotBlank(param.getUserName()), "sys_log_audit.User_name", param.getUserName())
                    .between(StrUtil.isAllNotBlank(param.getSearchBeginTime(), param.getSearchEndTime()), "sys_log_audit.Log_time", param.getSearchBeginTime(), param.getSearchEndTime());
        }
        List<SysLogAudit> list = this.list(queryWrapper);
        Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(SysLogAudit::getLoginName, Collectors.counting()));

        this.exportAnalyseExcel("分析结果", collect);
    }

    /**
     * 记录controller全局拦截的日志，并非全局异常捕获的
     *
     * @param returnType     返回类型
     * @param httpResult     返回结果
     * @param methodDescribe 方法描述
     */
    @Override
    public void recodeAdviceLog(MethodParameter returnType, HttpResult<?> httpResult, String methodDescribe) {
        SysLogAudit sysLogAudit = new SysLogAudit();
        String userId = RequestUtil.getUserId();
        if (StrUtil.isBlank(userId)) {
            String loginName = RequestUtil.getLoginName();
            if (loginName.equalsIgnoreCase(LogInfo.UNKNOWN_USER)) {
                sysLogAudit.setUserName(UserConstant.UN_LOGIN);
                sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
            } else {
                sysLogAudit.setUserName(loginName);
                sysLogAudit.setLoginName(loginName);
            }
        } else {
            SysUser sysUser = sysUserService.getById(userId);
            if (sysUser != null) {
                sysLogAudit.setUserName(sysUser.getName());
                sysLogAudit.setLoginName(sysUser.getLoginName());
            } else {
                sysLogAudit.setUserName(UserConstant.UN_LOGIN);
                sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
            }
        }
        // 不是很重要,本地部署的，除非以后工具类C端有必要，到时候再丰富读IP的过程
        sysLogAudit.setIp(RequestUtil.getUserIp());
        // 操作内容
        sysLogAudit.setOperate(methodDescribe);
        // 操作结果
        String result = httpResult.getCode().equalsIgnoreCase(CommonResponseEnum.FAIL.getCode()) ? CommonResponseEnum.FAIL.getMessage() : CommonResponseEnum.SUCCESS.getMessage();
        sysLogAudit.setResult(result);
        // 事件类型
        String type = ReflectCommonUtil.getOperateInfoByMethod(returnType.getMethod()).getOperateType();
        sysLogAudit.setType(type.equalsIgnoreCase("业务事件") ? 0 : 1);
        // 等级
        String level = ReflectCommonUtil.getOperateInfoByMethod(returnType.getMethod()).getOperateLevel();
        sysLogAudit.setLevel(levelStringToNumber(level));
        // 中等以上的需要置为未告警，但是告警功能暂未做
        if (sysLogAudit.getLevel() == 1) {
            sysLogAudit.setWarn(1);
        } else {
            sysLogAudit.setWarn(0);
        }
        // 操作类型
        String operateType = ReflectCommonUtil.getOperateTypeByMethod(returnType.getMethod());
        sysLogAudit.setOperateType(operateType);
        sysLogAudit.setLogTime(LocalDateTime.now());
        this.save(sysLogAudit);
    }

    @Override
    public void recodeBusinessExceptionLog(Exception exception, String message) {
        SysLogAudit sysLogAudit = new SysLogAudit();
        String userId = RequestUtil.getUserId();
        if (StrUtil.isBlank(userId)) {
            String loginName = RequestUtil.getLoginName();
            if (loginName.equalsIgnoreCase(LogInfo.UNKNOWN_USER)) {
                sysLogAudit.setUserName(UserConstant.UN_LOGIN);
                sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
            } else {
                sysLogAudit.setUserName(loginName);
                sysLogAudit.setLoginName(loginName);
            }
        } else {
            SysUser sysUser = sysUserService.getById(userId);
            if (sysUser != null) {
                sysLogAudit.setUserName(sysUser.getName());
                sysLogAudit.setLoginName(sysUser.getLoginName());
            } else {
                sysLogAudit.setUserName(UserConstant.UN_LOGIN);
                sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
            }
        }
        //根据异常获取method方法
        Method method = ReflectCommonUtil.getMethod(exception);
        if (exception instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) exception;
            method = methodArgumentNotValidException.getParameter().getMethod();
        }
        // 不是很重要,本地部署的，除非以后工具类C端有必要，到时候再丰富读IP的过程
        sysLogAudit.setIp(RequestUtil.getUserIp());
        sysLogAudit.setReason(message);
        String result = CommonResponseEnum.FAIL.getMessage();
        sysLogAudit.setResult(result);
        String methodDescribe = ReflectCommonUtil.getMethodDescribeByMethod(method);
        sysLogAudit.setOperate(methodDescribe);
        String type = ReflectCommonUtil.getOperateInfoByMethod(method).getOperateType();
        sysLogAudit.setType(type.equalsIgnoreCase("业务事件") ? 0 : 1);
        String level = ReflectCommonUtil.getOperateInfoByMethod(method).getOperateLevel();
        sysLogAudit.setLevel(levelStringToNumber(level));
        String operateType = ReflectCommonUtil.getOperateTypeByMethod(method);
        sysLogAudit.setOperateType(operateType);
        sysLogAudit.setLogTime(LocalDateTime.now());
        this.save(sysLogAudit);
    }

    private void exportAnalyseExcel(String fileName, Map<String, Long> collect) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        XSSFWorkbook wb = new XSSFWorkbook();

        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");

            XSSFSheet sheet = wb.createSheet("sheet1");

            //createPieChart(sheet, collect.keySet().stream().collect(Collectors.toList()), collect.values().stream().collect(Collectors.toList()));

            wb.write(outputStream);
            wb.close();
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }


    /**
     * 严重度 文字 转 数字
     */
    private Integer levelStringToNumber(String level) {
        switch (level) {
            case "中等":
                return 1;
            case "严重":
                return 2;
            default:
                return 0;
        }
    }
}
