package com.erp.approval.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.approval.dto.AuditLogQueryRequest;
import com.erp.common.core.model.PageResponse;
import com.erp.approval.entity.AuditLog;
import com.erp.approval.repository.AuditLogRepository;
import com.erp.approval.service.AuditLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 审核日志服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuditLogServiceImpl implements AuditLogService {
    
    private final AuditLogRepository auditLogRepository;
    private final ObjectMapper objectMapper;
    
    private static final String DEFAULT_MODULE = "System";
    private static final String DEFAULT_TARGET_TYPE = "Order";
    
    @Override
    public PageResponse<AuditLog> listLogs(Long tenantId, AuditLogQueryRequest request) {
        LambdaQueryWrapper<AuditLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditLog::getTenantId, tenantId);
        
        if (StringUtils.hasText(request.getModule())) {
            queryWrapper.eq(AuditLog::getModule, request.getModule());
        }
        
        if (StringUtils.hasText(request.getAction())) {
            queryWrapper.eq(AuditLog::getAction, request.getAction());
        }
        
        if (request.getUserId() != null) {
            queryWrapper.eq(AuditLog::getUserId, request.getUserId());
        }
        
        if (StringUtils.hasText(request.getTargetType())) {
            queryWrapper.eq(AuditLog::getTargetType, request.getTargetType());
        }
        
        if (request.getTargetId() != null) {
            queryWrapper.eq(AuditLog::getTargetId, request.getTargetId());
        }
        
        if (StringUtils.hasText(request.getStartDate())) {
            queryWrapper.ge(AuditLog::getCreatedAt, request.getStartDate());
        }
        
        if (StringUtils.hasText(request.getEndDate())) {
            queryWrapper.le(AuditLog::getCreatedAt, request.getEndDate() + " 23:59:59");
        }
        
        queryWrapper.orderByDesc(AuditLog::getCreatedAt);
        
        int page = request.getPage() != null ? request.getPage() : 1;
        int pageSize = request.getPageSize() != null ? request.getPageSize() : 20;
        Page<AuditLog> pageResult = auditLogRepository.selectPage(
            new Page<>(page, pageSize),
            queryWrapper
        );
        
        return PageResponse.of(pageResult.getRecords(), pageResult.getTotal(), page, pageSize);
    }
    
    @Override
    public Optional<AuditLog> getLogById(Long tenantId, Long id) {
        LambdaQueryWrapper<AuditLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditLog::getId, id)
                   .eq(AuditLog::getTenantId, tenantId);
        
        AuditLog auditLog = auditLogRepository.selectOne(queryWrapper);
        return Optional.ofNullable(auditLog);
    }
    
    @Override
    public Map<String, Map<String, Long>> getStats(Long tenantId, String startDate, String endDate) {
        if (!StringUtils.hasText(startDate)) {
            startDate = LocalDateTime.now().minusDays(7).toLocalDate().toString();
        }
        if (!StringUtils.hasText(endDate)) {
            endDate = LocalDateTime.now().toLocalDate().toString();
        }
        
        List<Map<String, Object>> statsData = auditLogRepository.statsByModuleAndAction(
            tenantId, startDate, endDate + " 23:59:59"
        );
        
        Map<String, Map<String, Long>> result = new HashMap<>();
        for (Map<String, Object> item : statsData) {
            String module = String.valueOf(item.get("module"));
            String action = String.valueOf(item.get("action"));
            long count = ((Number) item.get("count")).longValue();
            
            result.computeIfAbsent(module, k -> new HashMap<>()).put(action, count);
        }
        
        return result;
    }
    
    @Override
    @Transactional
    public AuditLog log(String module, String action, String targetType, Long targetId,
                        Map<String, Object> oldValues, Map<String, Object> newValues) {
        AuditLog auditLog = new AuditLog();
        auditLog.setTenantId(getCurrentTenantId());
        auditLog.setUserId(getCurrentUserId());
        auditLog.setUserName(getCurrentUserName());
        auditLog.setModule(module != null ? module : DEFAULT_MODULE);
        auditLog.setAction(action);
        auditLog.setTargetType(targetType != null ? targetType : DEFAULT_TARGET_TYPE);
        auditLog.setTargetId(targetId);
        auditLog.setOldValues(toJson(oldValues));
        auditLog.setNewValues(toJson(newValues));
        auditLog.setIp(getClientIp());
        auditLog.setUserAgent(getUserAgent());
        auditLog.setRequestId(getRequestId());
        auditLog.setCreatedAt(LocalDateTime.now());
        
        auditLogRepository.insert(auditLog);
        return auditLog;
    }
    
    @Override
    @Transactional
    public AuditLog logCreate(String module, String targetType, Long targetId, Map<String, Object> newValues) {
        return log(module, "Create", targetType, targetId, null, newValues);
    }
    
    @Override
    @Transactional
    public AuditLog logUpdate(String module, String targetType, Long targetId,
                               Map<String, Object> oldValues, Map<String, Object> newValues) {
        return log(module, "Update", targetType, targetId, oldValues, newValues);
    }
    
    @Override
    @Transactional
    public AuditLog logDelete(String module, String targetType, Long targetId, Map<String, Object> oldValues) {
        return log(module, "Delete", targetType, targetId, oldValues, null);
    }
    
    @Override
    @Transactional
    public AuditLog logApprove(String module, Long targetId,
                                Map<String, Object> oldValues, Map<String, Object> newValues) {
        return log(module, "Approve", DEFAULT_TARGET_TYPE, targetId, oldValues, newValues);
    }
    
    @Override
    @Transactional
    public AuditLog logReject(String module, Long targetId,
                               Map<String, Object> oldValues, Map<String, Object> newValues, String reason) {
        AuditLog auditLog = log(module, "Reject", DEFAULT_TARGET_TYPE, targetId, oldValues, newValues);
        // 可以将reason存储到扩展字段或备注字段
        return auditLog;
    }
    
    // ==================== 私有方法 ====================
    
    private Long getCurrentTenantId() {
        // 从安全上下文获取当前租户ID
        return 0L; // TODO: 从SecurityContext获取
    }
    
    private Long getCurrentUserId() {
        // 从安全上下文获取当前用户ID
        return 0L; // TODO: 从SecurityContext获取
    }
    
    private String getCurrentUserName() {
        // 从安全上下文获取当前用户名
        return "System"; // TODO: 从SecurityContext获取
    }
    
    private String getClientIp() {
        try {
            HttpServletRequest request = getCurrentRequest();
            if (request != null) {
                String ip = request.getHeader("X-Forwarded-For");
                if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("X-Real-IP");
                }
                if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                }
                return ip;
            }
        } catch (Exception e) {
            log.debug("获取客户端IP失败", e);
        }
        return "0.0.0.0";
    }
    
    private String getUserAgent() {
        try {
            HttpServletRequest request = getCurrentRequest();
            if (request != null) {
                return request.getHeader("User-Agent");
            }
        } catch (Exception e) {
            log.debug("获取User-Agent失败", e);
        }
        return null;
    }
    
    private String getRequestId() {
        try {
            HttpServletRequest request = getCurrentRequest();
            if (request != null) {
                return request.getHeader("X-Request-ID");
            }
        } catch (Exception e) {
            log.debug("获取Request-ID失败", e);
        }
        return UUID.randomUUID().toString();
    }
    
    private HttpServletRequest getCurrentRequest() {
        try {
            org.springframework.web.context.request.RequestContextHolder
                .getRequestAttributes();
            return ((org.springframework.web.context.request.ServletRequestAttributes)
                org.springframework.web.context.request.RequestContextHolder.getRequestAttributes())
                .getRequest();
        } catch (Exception e) {
            return null;
        }
    }
    
    private String toJson(Map<String, Object> map) {
        if (map == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            log.error("序列化Map失败: {}", map, e);
            return null;
        }
    }
    
    public Map<String, Object> parseJson(String json) {
        if (!StringUtils.hasText(json)) {
            return Collections.emptyMap();
        }
        try {
            return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
        } catch (JsonProcessingException e) {
            log.error("解析JSON失败: {}", json, e);
            return Collections.emptyMap();
        }
    }
}
