package com.njcn.device.pms.component;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.device.pms.annotation.TerminalOperationLogDesc;
import com.njcn.device.pms.mapper.majornetwork.*;
import com.njcn.device.pms.pojo.dto.PmsDistributionMonitorDTO;
import com.njcn.device.pms.pojo.param.*;
import com.njcn.device.pms.pojo.po.*;
import com.njcn.device.pms.service.majornetwork.IDistributionMonitorService;
import com.njcn.device.pms.service.majornetwork.ITerminalLogService;
import com.njcn.web.utils.RequestUtil;
import io.netty.util.concurrent.FastThreadLocal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * pqs
 *
 * @author cdf
 * @date 2023/2/23
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class TerminalLoggerAspect {

    private final ITerminalLogService iTerminalLogService;

    private final IDistributionMonitorService iDistributionMonitorService;

    private final MonitorMapper monitorMapper;

    private final StatationStatMapper statationStatMapper;

    private final TerminalMapper terminalMapper;

    private final PmsGeneratrixWireMapper pmsGeneratrixWireMapper;

    private final PowerDistributionareaMapper powerDistributionareaMapper;

    private final PowerGenerationUserMapper powerGenerationUserMapper;

    private final PowerClientMapper powerClientMapper;


    /**
     * FastThreadLocal 依赖于  netty,如果不想用netty，可以使用jdk自带的 ThreadLocal
     */
    final FastThreadLocal<List<TerminalLog>> logFastThreadLocal = new FastThreadLocal<>();
    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");


    /**
     * 切入连接点，使用固定 controller层下的所有文件
     */
    @Pointcut(value = "execution(* com.njcn.device.pms.controller.ledgerManger..*(..)))")
    public void logPointcut() {
    }

    /**
     * 请求前置通知
     */
    @Before("logPointcut()")
    public void beforLogger(JoinPoint joinPoint) {

        String userName = RequestUtil.getUsername();

        // 验证请求方法是否带有操作日志注解
        Method signature = ((MethodSignature) joinPoint.getSignature()).getMethod();
        TerminalOperationLogDesc operationLogDesc = signature.getAnnotation(TerminalOperationLogDesc.class);
        if (operationLogDesc != null) {
            log.info("--------台账日志操作开始记录--------");
            int type = operationLogDesc.module();
            // 操作日志记录
            List<TerminalLog> terminalLogList = new ArrayList<>();
            String msg = argsArrayToString(joinPoint.getArgs());
            if (msg.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(msg);

                switch (type) {
                    case 0:
                        jsonArray.forEach(item -> {
                            StatationStat statationStat = statationStatMapper.selectById(item.toString());
                            if (Objects.nonNull(statationStat)) {
                                String mubiao = statationStat.getOrgName() + "_" + statationStat.getPowerName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 1:
                        jsonArray.forEach(item -> {
                            PmsTerminal pmsTerminal = terminalMapper.selectById(item.toString());
                            if (Objects.nonNull(pmsTerminal)) {
                                String mubiao = pmsTerminal.getOrgName() + "_" + pmsTerminal.getPowerrName() + "_" + pmsTerminal.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 2:
                        jsonArray.forEach(item -> {
                            GeneratrixWire generatrixWire = pmsGeneratrixWireMapper.selectById(item.toString());
                            if (Objects.nonNull(generatrixWire)) {
                                String mubiao = generatrixWire.getStationName() + "_" + generatrixWire.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });

                        break;
                    case 3:
                        jsonArray.forEach(item -> {
                            PowerDistributionarea powerDistributionarea = powerDistributionareaMapper.selectById(item.toString());
                            if (Objects.nonNull(powerDistributionarea)) {
                                String mubiao = powerDistributionarea.getOrgName() + "_" + powerDistributionarea.getPowerrName() + "_" + powerDistributionarea.getLineName() + "_" + powerDistributionarea.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 4:
                        jsonArray.forEach(item -> {
                            PowerGenerationUser powerGenerationUser = powerGenerationUserMapper.selectById(item.toString());
                            if (Objects.nonNull(powerGenerationUser)) {
                                String mubiao = powerGenerationUser.getOrgName() + "_" + powerGenerationUser.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 5:
                        jsonArray.forEach(item -> {
                            PowerClient powerClient = powerClientMapper.selectById(item.toString());
                            if (Objects.nonNull(powerClient)) {
                                String mubiao = powerClient.getOrgName() + "_" + powerClient.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 6:
                        jsonArray.forEach(item -> {
                            Monitor monitor = monitorMapper.selectById(item.toString());
                            if (Objects.nonNull(monitor)) {
                                String mubiao = monitor.getOrgName() + "_" + monitor.getPowerrName() + "_" + monitor.getName();
                                TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                terminalLogList.add(terminalLog);
                            }
                        });
                        break;
                    case 7:

                        jsonArray.forEach(item -> {
                            DistributionMonitor distributionMonitor = iDistributionMonitorService.getById(item.toString());
                            if (Objects.nonNull(distributionMonitor)) {
                                PmsDistributionMonitorDTO pmsDistributionMonitorDTO = iDistributionMonitorService.distributionMonitorDetail(distributionMonitor.getMonitorId(), distributionMonitor.getMonitorSort(), distributionMonitor.getIfPowerUser());
                                if (Objects.nonNull(pmsDistributionMonitorDTO)) {
                                    if (pmsDistributionMonitorDTO.getMonitorSort() == 1) {
                                        String mubiao = pmsDistributionMonitorDTO.getOrgName() + "_" + pmsDistributionMonitorDTO.getPowerStationName() + "_" + pmsDistributionMonitorDTO.getLineName() + "_" + pmsDistributionMonitorDTO.getName();
                                        TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                        terminalLogList.add(terminalLog);
                                    } else if (pmsDistributionMonitorDTO.getMonitorSort() == 2) {
                                        String mubiao = pmsDistributionMonitorDTO.getOrgName() + "_" + pmsDistributionMonitorDTO.getPowerStationName() + "_" + pmsDistributionMonitorDTO.getLineName() + "_" + pmsDistributionMonitorDTO.getName();
                                        TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                        terminalLogList.add(terminalLog);
                                    } else {
                                        String mubiao = pmsDistributionMonitorDTO.getOrgName() + "_" + pmsDistributionMonitorDTO.getPowerStationName() + "_" + pmsDistributionMonitorDTO.getLineName() + "_" + pmsDistributionMonitorDTO.getPlatformName() + "_" + pmsDistributionMonitorDTO.getName();
                                        TerminalLog terminalLog = assTerminalLog(type, operationLogDesc.value(), mubiao);
                                        terminalLogList.add(terminalLog);
                                    }
                                }
                            }
                        });
                        break;
                    default:
                        break;
                }
            } else {
                TerminalLog operationLog = new TerminalLog();
                operationLog.setStatus(DataStateEnum.ENABLE.getCode());
                operationLog.setOperationStatus(true);
                operationLog.setCreateBy(RequestUtil.getUserIndex());
                operationLog.setUpdateBy(RequestUtil.getUserIndex());
                operationLog.setCreateTime(LocalDateTime.now());
                operationLog.setUpdateTime(LocalDateTime.now());
                JSONObject jsonObject = new JSONObject(msg);
                operationLog.setType(type);
                switch (type) {

                    case 0:
                        StatationStatParam statationStat = jsonObject.toBean(StatationStatParam.class);
                        if (Objects.nonNull(statationStat)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标电站：" + statationStat.getOrgName() + "_" + statationStat.getPowerName());
                        }
                        break;
                    case 1:
                        PmsTerminalParam terminalParam = jsonObject.toBean(PmsTerminalParam.class);
                        if (Objects.nonNull(terminalParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标终端：" + terminalParam.getOrgName() + "_" + terminalParam.getPowerrName() + "_" + terminalParam.getName());
                        }
                        break;
                    case 2:
                        GeneratrixWireParam generatrixWireParam = jsonObject.toBean(GeneratrixWireParam.class);
                        if (Objects.nonNull(generatrixWireParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标线路：" + generatrixWireParam.getStationName() + "_" + generatrixWireParam.getName());
                        }
                        break;
                    case 3:
                        PowerDistributionareaParam powerDistributionareaParam = jsonObject.toBean(PowerDistributionareaParam.class);
                        if (Objects.nonNull(powerDistributionareaParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标台区：" + powerDistributionareaParam.getOrgName() + "_" + powerDistributionareaParam.getPowerrName() + "_" + powerDistributionareaParam.getLineName() + "_" + powerDistributionareaParam.getName());
                        }
                        break;
                    case 4:
                        PowerGenerationUserParam powerGenerationUserParam = jsonObject.toBean(PowerGenerationUserParam.class);
                        if (Objects.nonNull(powerGenerationUserParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标发电用户：" + powerGenerationUserParam.getOrgName() + "_" + powerGenerationUserParam.getName());
                        }
                        break;
                    case 5:
                        PowerClientParam powerClientParam = jsonObject.toBean(PowerClientParam.class);
                        if (Objects.nonNull(powerClientParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标用电用户：" + powerClientParam.getOrgName() + "_" + powerClientParam.getName());
                        }
                        break;
                    case 6:
                        MonitorParam monitorParam = jsonObject.toBean(MonitorParam.class);
                        if (Objects.nonNull(monitorParam)) {
                            operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标主网监测点：" + monitorParam.getOrgName() + "_" + monitorParam.getPowerrName() + "_" + monitorParam.getName());
                        }
                        break;
                    case 7:
                        DistributionMonitorParam distributionMonitorParam = jsonObject.toBean(DistributionMonitorParam.class);
                        if (Objects.nonNull(distributionMonitorParam)) {
                            PmsDistributionMonitorDTO pmsDistributionMonitorDTO = iDistributionMonitorService.distributionMonitorDetail(distributionMonitorParam.getMonitorId(), distributionMonitorParam.getMonitorSort(), distributionMonitorParam.getIfPowerUser());
                            if (Objects.nonNull(pmsDistributionMonitorDTO)) {
                                if (pmsDistributionMonitorDTO.getMonitorSort() == 1) {
                                    operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标配网监测点：" + pmsDistributionMonitorDTO.getOrgName() + "_" + pmsDistributionMonitorDTO.getPowerStationName() + "_" + pmsDistributionMonitorDTO.getLineName() + "_" + pmsDistributionMonitorDTO.getName());
                                } else if (pmsDistributionMonitorDTO.getMonitorSort() == 2) {
                                    operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标配网监测点：" + pmsDistributionMonitorDTO.getOrgName() + "_" + pmsDistributionMonitorDTO.getPowerStationName() + "_" + pmsDistributionMonitorDTO.getLineName() + "_" + pmsDistributionMonitorDTO.getName());
                                } else {
                                    operationLog.setOperatorDescribe(userName + "进行" + operationLogDesc.value() + "操作,目标配网监测点：" + pmsDistributionMonitorDTO.getOrgName()  + "_" + pmsDistributionMonitorDTO.getPlatformName() + "_" + pmsDistributionMonitorDTO.getName());
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
                terminalLogList.add(operationLog);
            }

            if (CollectionUtil.isNotEmpty(terminalLogList)) {
                logFastThreadLocal.set(terminalLogList);
            }

        }
    }



    private TerminalLog assTerminalLog(Integer type, String top, String mubiao) {
        TerminalLog operationLog = new TerminalLog();
        operationLog.setStatus(DataStateEnum.ENABLE.getCode());
        operationLog.setOperationStatus(true);
        operationLog.setCreateBy(RequestUtil.getUserIndex());
        operationLog.setUpdateBy(RequestUtil.getUserIndex());
        operationLog.setCreateTime(LocalDateTime.now());
        operationLog.setUpdateTime(LocalDateTime.now());
        operationLog.setType(type);
        operationLog.setOperatorDescribe(RequestUtil.getUsername() + "进行" + top + "操作,目标：" + mubiao);
        return operationLog;
    }

    /**
     * 请求后置通知，请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        // 保存操作日志
        if (logFastThreadLocal.isSet()) {
            List<TerminalLog> operationLogList = logFastThreadLocal.get();

            if (CollectionUtil.isNotEmpty(operationLogList)) {
                for (TerminalLog terminalLog : operationLogList) {
                    boolean flag = terminalLog.getOperationStatus();
                    if (flag) {
                        JSONObject jsonObject = new JSONObject(result);
                        HttpResult httpResult = jsonObject.toBean(HttpResult.class);
                        if (CommonResponseEnum.SUCCESS.getCode().equals(httpResult.getCode())) {
                            terminalLog.setResults(1);
                        } else {
                            terminalLog.setResults(0);
                        }
                    } else {
                        terminalLog.setResults(0);
                    }

                    // 调用具体的 service 保存到数据库中
                    iTerminalLogService.save(terminalLog);

                }
            }
            // 移除本地线程数据
            logFastThreadLocal.remove();
        }

    }


    /**
     * 异常通知，请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {

        //log.error("ErrorMessage：请根据异常产生时间前往异常日志查看相关信息");
        // 保存操作日志
        if (logFastThreadLocal.isSet()) {
            List<TerminalLog> operationLogList = logFastThreadLocal.get();
            if (CollectionUtil.isNotEmpty(operationLogList)) {
                operationLogList.forEach(item -> item.setOperationStatus(false));
                // 移除本地线程数据
                logFastThreadLocal.remove();
            }
        }

    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (null != o && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }


}
