/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package com.youchain.service.impl;

import cn.hutool.core.lang.Dict;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.youchain.aspect.DefaultOperDataUtil;
import com.youchain.domain.Log;
import com.youchain.domain.OperLog;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.repository.OperLogRepository;
import com.youchain.service.OperLogService;
import com.youchain.service.dto.OperLogDto;
import com.youchain.service.dto.OperLogQueryCriteria;
import com.youchain.service.mapstruct.OperLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;

/**
* @website https://eladmin.vip
* @description 服务实现
* @author houjianlan
* @date 2025-12-23
**/
@Service
@RequiredArgsConstructor
@Slf4j
public class OperLogServiceImpl implements OperLogService {

    private final OperLogRepository operLogRepository;
    private final OperLogMapper operLogMapper;

    @Override
    public Map<String,Object> queryAll(OperLogQueryCriteria criteria, Pageable pageable){
        Page<OperLog> page = operLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(operLogMapper::toDto));
    }

    @Override
    public List<OperLogDto> queryAll(OperLogQueryCriteria criteria){
        return operLogMapper.toDto(operLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public OperLogDto create(OperLog resources) {
        return operLogMapper.toDto(operLogRepository.save(resources));
    }



    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            operLogRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<OperLogDto> all, HttpServletResponse response) throws Exception {
        List<Map<String, Object>> list = new ArrayList<>();
        for (OperLogDto operLog : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("描述", operLog.getDescription());
            map.put("类型", operLog.getLogType());
            map.put("参数", operLog.getParams());
            map.put("返回", operLog.getReturnData());
            map.put(" requestIp",  operLog.getRequestIp());
            map.put("耗时", operLog.getTime());
            map.put("操作人", operLog.getUsername());
            map.put(" address",  operLog.getAddress());
            map.put("模块名", operLog.getModule());
            map.put("方法名", operLog.getMethod());
            map.put("操作内容", operLog.getContent());
            map.put("来源序号", operLog.getSourceId());
            map.put(" browser",  operLog.getBrowser());
            map.put(" exceptionDetail",  operLog.getExceptionDetail());
            map.put("创建时间", operLog.getCreateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Async
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(String url, String  returnValue, String username, String browser, String ip, JoinPoint joinPoint, OperLog log_data) {
        if (log_data == null) {
            throw new IllegalArgumentException("Log 不能为 null!");
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        com.youchain.annotation.Log aopLog = method.getAnnotation(com.youchain.annotation.Log.class);

        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";

        // 描述
        log_data.setDescription(aopLog.value());

        log_data.setRequestIp(ip);
        log_data.setAddress(StringUtils.getCityInfo(log_data.getRequestIp()));
        log_data.setMethod(url+"---"+methodName);
        log_data.setUsername(username);
        log_data.setParams(getParameter(method, joinPoint.getArgs()));
        log_data.setCreateTime(new Timestamp(new Date().getTime()));
        // 记录登录用户，隐藏密码信息
        if(signature.getName().equals("login") && StringUtils.isNotEmpty(log_data.getParams())){
            JSONObject obj = JSONUtil.parseObj(log_data.getParams());
            log_data.setUsername(obj.getStr("username", ""));
            log_data.setParams(JSONUtil.toJsonStr(Dict.create().set("username", log_data.getUsername())));
        }
        log_data.setBrowser(browser);
        log_data.setReturnData(returnValue);
        String module= DefaultOperDataUtil.getValue(methodName);
        log_data.setModule(module);
        operLogRepository.save(log_data);
    }
    /**
     * 根据方法和传入的参数获取请求参数
     */
    private String getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>(2);
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.isEmpty()) {
            return "";
        }
        return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
    }

}