package com.zsElectric.boot.charging.aspect;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zsElectric.boot.charging.entity.ThirdPartyApiLog;
import com.zsElectric.boot.charging.service.ThirdPartyApiLogService;
import com.zsElectric.boot.common.util.electric.ChargingUtil;
import com.zsElectric.boot.common.util.electric.RequestParmsEntity;
import com.zsElectric.boot.common.util.electric.ResponseParmsEntity;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 第三方接口日志记录切面
 * 拦截 LinkDataController 和 ChargingBusinessController 的所有方法,自动记录请求和响应日志
 *
 * @author system
 * @since 2025-01-02
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class ThirdPartyApiLogAspect {

    private final ThirdPartyApiLogService thirdPartyApiLogService;
    private final ObjectMapper objectMapper;
    private final ChargingUtil chargingUtil;
    
    /** 需要保留的关键请求头 */
    private static final Set<String> IMPORTANT_HEADERS = new HashSet<>(Arrays.asList(
            "content-type", "authorization", "content-length"
    ));

    /**
     * 定义切入点: LinkDataController 的所有方法
     */
    @Pointcut("execution(* com.zsElectric.boot.charging.controller.LinkDataController.*(..))")
    public void linkDataControllerPointcut() {
    }

    /**
     * 定义切入点: ChargingBusinessController 的所有方法
     */
    @Pointcut("execution(* com.zsElectric.boot.charging.controller.ChargingBusinessController.*(..))")
    public void chargingBusinessControllerPointcut() {
    }

    /**
     * 环绕通知: 记录 LinkDataController 接口日志
     */
    @Around("linkDataControllerPointcut()")
    public Object aroundLinkDataController(ProceedingJoinPoint joinPoint) throws Throwable {
        return recordApiLog(joinPoint, "LinkDataController", 2); // 2-接收推送
    }

    /**
     * 环绕通知: 记录 ChargingBusinessController 接口日志
     */
    @Around("chargingBusinessControllerPointcut()")
    public Object aroundChargingBusinessController(ProceedingJoinPoint joinPoint) throws Throwable {
        return recordApiLog(joinPoint, "ChargingBusinessController", 1); // 1-请求出去
    }

    /**
     * 记录接口日志的通用方法
     */
    private Object recordApiLog(ProceedingJoinPoint joinPoint, String controllerName, Integer logType) throws Throwable {
        long startTime = System.currentTimeMillis();
        ThirdPartyApiLog apiLog = new ThirdPartyApiLog();
        
        // 获取 HttpServletRequest
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
        
        try {
            // 设置基本信息
            apiLog.setLogType(logType);
            apiLog.setControllerName(controllerName);
            LocalDateTime now = LocalDateTime.now();
            apiLog.setCreatedTime(now);
            apiLog.setUpdatedTime(now);
            
            if (request != null) {
                // 请求信息
                apiLog.setRequestMethod(request.getMethod());
                apiLog.setRequestUrl(request.getRequestURL().toString());
                apiLog.setInterfaceName(request.getRequestURI());
                apiLog.setInterfaceDescription(getInterfaceDescription(request.getRequestURI()));
                apiLog.setClientIp(getClientIp(request));
                apiLog.setUserAgent(request.getHeader("User-Agent"));
                
                // 请求头(只保留关键信息)
                apiLog.setRequestHeaders(getSimplifiedHeaders(request));
                
                // 请求参数
                Map<String, String[]> parameterMap = request.getParameterMap();
                if (!parameterMap.isEmpty()) {
                    // 简化参数格式，单值的数组转为简单字符串
                    Map<String, Object> simplifiedParams = new HashMap<>();
                    for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                        String[] values = entry.getValue();
                        if (values != null && values.length == 1) {
                            simplifiedParams.put(entry.getKey(), values[0]);
                        } else {
                            simplifiedParams.put(entry.getKey(), values);
                        }
                    }
                    apiLog.setRequestParams(objectMapper.writeValueAsString(simplifiedParams));
                }
                
                // 请求体(从切点参数中获取)
                Object[] args = joinPoint.getArgs();
                if (args != null && args.length > 0) {
                    for (Object arg : args) {
                        if (arg != null && !(arg instanceof HttpServletRequest) && !(arg instanceof jakarta.servlet.http.HttpServletResponse)) {
                            // 序列化时忽略null值
                            ObjectMapper cleanMapper = objectMapper.copy();
                            cleanMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                            apiLog.setRequestBody(cleanMapper.writeValueAsString(arg));
                            
                            // 尝试提取业务字段
                            extractBusinessFields(arg, apiLog);
                            
                            // 尝试解密请求数据(针对 RequestParmsEntity 类型)
                            decryptRequestData(arg, apiLog);
                            break;
                        }
                    }
                }
            }
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 记录响应
            long endTime = System.currentTimeMillis();
            apiLog.setResponseTime(endTime - startTime);
            apiLog.setResponseStatus(200);
            apiLog.setIsSuccess(1);
            
            if (result != null) {
                // 序列化时忽略null值
                ObjectMapper cleanMapper = objectMapper.copy();
                cleanMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                apiLog.setResponseBody(cleanMapper.writeValueAsString(result));
                
                // 解密响应数据(针对 ResponseParmsEntity 类型)
                decryptResponseData(result, apiLog);
            }
            
            // 异步保存日志
            thirdPartyApiLogService.saveLogAsync(apiLog);
            
            return result;
            
        } catch (Exception e) {
            // 记录异常
            long endTime = System.currentTimeMillis();
            apiLog.setResponseTime(endTime - startTime);
            apiLog.setResponseStatus(500);
            apiLog.setIsSuccess(0);
            apiLog.setErrorMessage(e.getMessage());
            
            // 异步保存日志
            thirdPartyApiLogService.saveLogAsync(apiLog);
            
            throw e;
        }
    }

    /**
     * 解密请求数据
     * 针对 LinkDataController 接收的 RequestParmsEntity 类型参数进行解密
     */
    private void decryptRequestData(Object requestBody, ThirdPartyApiLog apiLog) {
        try {
            if (requestBody instanceof RequestParmsEntity) {
                RequestParmsEntity requestParms = (RequestParmsEntity) requestBody;
                String encryptedData = requestParms.getData();
                
                if (StrUtil.isNotBlank(encryptedData)) {
                    // 使用 ChargingUtil 解密（与业务代码保持一致）
                    String decryptedData = chargingUtil.decryptData(encryptedData);
                    apiLog.setDecryptedRequestData(decryptedData);
                    log.debug("请求数据解密成功: {}", decryptedData);
                }
            }
        } catch (Exception e) {
            log.warn("解密请求数据失败: {}", e.getMessage());
        }
    }

    /**
     * 解密响应数据
     * 针对返回的 ResponseParmsEntity 类型响应进行解密
     */
    private void decryptResponseData(Object responseBody, ThirdPartyApiLog apiLog) {
        try {
            if (responseBody instanceof ResponseParmsEntity) {
                ResponseParmsEntity responseParms = (ResponseParmsEntity) responseBody;
                String encryptedData = responseParms.getData();
                
                if (StrUtil.isNotBlank(encryptedData)) {
                    // 使用 ChargingUtil 解密（与业务代码保持一致）
                    String decryptedData = chargingUtil.decryptData(encryptedData);
                    apiLog.setDecryptedResponseData(decryptedData);
                    log.debug("响应数据解密成功: {}", decryptedData);
                }
            }
        } catch (Exception e) {
            log.warn("解密响应数据失败: {}", e.getMessage());
        }
    }

    /**
     * 提取业务字段
     */
    private void extractBusinessFields(Object requestBody, ThirdPartyApiLog apiLog) {
        try {
            String json = objectMapper.writeValueAsString(requestBody);
            Map<String, Object> map = objectMapper.readValue(json, Map.class);
            
            // 提取运营商ID
            if (map.containsKey("OperatorID")) {
                apiLog.setOperatorId(String.valueOf(map.get("OperatorID")));
            } else if (map.containsKey("operatorID")) {
                apiLog.setOperatorId(String.valueOf(map.get("operatorID")));
            }
            
            // 提取充电站ID
            if (map.containsKey("StationID")) {
                apiLog.setStationId(String.valueOf(map.get("StationID")));
            } else if (map.containsKey("stationID")) {
                apiLog.setStationId(String.valueOf(map.get("stationID")));
            }
            
            // 提取充电桩ID
            if (map.containsKey("ConnectorID")) {
                apiLog.setConnectorId(String.valueOf(map.get("ConnectorID")));
            } else if (map.containsKey("connectorID")) {
                apiLog.setConnectorId(String.valueOf(map.get("connectorID")));
            }
            
            // 提取业务流水号
            if (map.containsKey("StartChargeSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("StartChargeSeq")));
            } else if (map.containsKey("startChargeSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("startChargeSeq")));
            } else if (map.containsKey("EquipAuthSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("EquipAuthSeq")));
            } else if (map.containsKey("equipAuthSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("equipAuthSeq")));
            } else if (map.containsKey("EquipBizSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("EquipBizSeq")));
            } else if (map.containsKey("equipBizSeq")) {
                apiLog.setBusinessSeq(String.valueOf(map.get("equipBizSeq")));
            }
            
        } catch (Exception e) {
            log.debug("提取业务字段失败: {}", e.getMessage());
        }
    }

    /**
     * 获取简化的请求头信息（只保留关键字段）
     */
    private String getSimplifiedHeaders(HttpServletRequest request) {
        try {
            Map<String, String> headers = new HashMap<>();
            for (String headerName : IMPORTANT_HEADERS) {
                String headerValue = request.getHeader(headerName);
                if (StrUtil.isNotBlank(headerValue)) {
                    // 对于 authorization 只保留类型，不记录完整 token
                    if ("authorization".equalsIgnoreCase(headerName) && headerValue.length() > 20) {
                        headers.put(headerName, headerValue.substring(0, 20) + "...");
                    } else {
                        headers.put(headerName, headerValue);
                    }
                }
            }
            return headers.isEmpty() ? null : objectMapper.writeValueAsString(headers);
        } catch (Exception e) {
            log.error("获取请求头失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取接口中文说明
     * 根据接口路径返回对应的中文说明
     */
    private String getInterfaceDescription(String uri) {
        if (StrUtil.isBlank(uri)) {
            return null;
        }
        
        // LinkDataController 接口说明
        if (uri.contains("/query_token")) {
            return "获取token";
        } else if (uri.contains("/notification_start_charge_result")) {
            return "推送启动充电结果";
        } else if (uri.contains("/notification_equip_charge_status")) {
            return "推送充电状态";
        } else if (uri.contains("/notification_stop_charge_result")) {
            return "推送停止充电结果";
        } else if (uri.contains("/notification_charge_order_info")) {
            return "推送充电订单信息";
        } else if (uri.contains("/notification_stationStatus")) {
            return "设备状态变化推送";
        }
        // ChargingBusinessController 接口说明
        else if (uri.contains("/query_equip_business_policy")) {
            return "查询业务策略信息";
        } else if (uri.contains("/query_equip_auth")) {
            return "请求设备认证";
        } else if (uri.contains("/query_stations_info")) {
            return "查询充电站信息";
        } else if (uri.contains("/query_station_status")) {
            return "设备接口状态查询";
        } else if (uri.contains("/query_start_charge")) {
            return "请求启动充电";
        } else if (uri.contains("/query_equip_charge_status")) {
            return "查询充电状态";
        } else if (uri.contains("/query_stop_charge")) {
            return "请求停止充电";
        }
        
        return null;
    }

    /**
     * 获取客户端真实IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值,第一个为真实IP
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            }
            return ip;
        }
        
        ip = request.getHeader("X-Real-IP");
        if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        
        return request.getRemoteAddr();
    }
}
