package com.erp.audit.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.audit.dto.*;
import com.erp.audit.entity.AuditLog;
import com.erp.audit.repository.AuditLogRepository;
import com.erp.audit.service.AuditLogService;
import com.erp.audit.service.SensitiveOperationService;
import com.erp.common.utils.BeanCopyUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 审计日志服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuditLogServiceImpl implements AuditLogService {

    private final AuditLogRepository auditLogRepository;
    private final SensitiveOperationService sensitiveOperationService;

    @Override
    @Transactional
    public AuditLog create(AuditLogCreateRequest request) {
        AuditLog entity = BeanCopyUtils.copy(request, AuditLog.class);
        entity.setAuditStatus("SUCCESS");
        entity.setCreatedAt(LocalDateTime.now());
        
        // 检查敏感操作
        if (request.getSensitive() != null && request.getSensitive()) {
            sensitiveOperationService.triggerAlert(null, null);
        }
        
        auditLogRepository.insert(entity);
        return entity;
    }

    @Override
    @Transactional
    public AuditLog update(Long id, AuditLogDTO dto) {
        AuditLog existing = auditLogRepository.selectById(id);
        if (existing == null) {
            throw new RuntimeException("审计日志不存在: " + id);
        }
        BeanCopyUtils.copyNonNullProperties(dto, existing);
        existing.setUpdatedAt(LocalDateTime.now());
        auditLogRepository.updateById(existing);
        return existing;
    }

    @Override
    @Transactional
    public void delete(Long id) {
        auditLogRepository.deleteById(id);
    }

    @Override
    public AuditLogDTO getById(Long id) {
        AuditLog entity = auditLogRepository.selectById(id);
        return entity != null ? BeanCopyUtils.copy(entity, AuditLogDTO.class) : null;
    }

    @Override
    public IPage<AuditLogDTO> pageQuery(AuditLogQueryRequest request) {
        Page<AuditLog> page = new Page<>(request.getPageNum(), request.getPageSize());
        IPage<AuditLog> result = auditLogRepository.selectPage(page, request);
        return result.convert(entity -> BeanCopyUtils.copy(entity, AuditLogDTO.class));
    }

    @Override
    public AuditStatisticsDTO getStatistics(Long tenantId, String startDate, String endDate) {
        LocalDateTime start = LocalDateTime.parse(startDate + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime end = LocalDateTime.parse(endDate + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        LambdaQueryWrapper<AuditLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(tenantId != null, AuditLog::getTenantId, tenantId);
        wrapper.between(AuditLog::getCreatedAt, start, end);

        List<AuditLog> logs = auditLogRepository.selectList(wrapper);

        long totalCount = logs.size();
        long successCount = logs.stream().filter(l -> "SUCCESS".equals(l.getAuditStatus())).count();
        long failCount = logs.stream().filter(l -> "FAIL".equals(l.getAuditStatus())).count();
        long sensitiveCount = logs.stream().filter(l -> Boolean.TRUE.equals(l.getSensitive())).count();

        double avgDuration = logs.stream()
                .filter(l -> l.getDuration() != null)
                .mapToLong(AuditLog::getDuration)
                .average().orElse(0.0);

        Map<String, Long> operationTypeStats = logs.stream()
                .collect(Collectors.groupingBy(AuditLog::getOperationType, Collectors.counting()));

        Map<String, Long> moduleStats = logs.stream()
                .collect(Collectors.groupingBy(AuditLog::getModule, Collectors.counting()));

        Map<String, Long> userStats = logs.stream()
                .collect(Collectors.groupingBy(AuditLog::getUsername, Collectors.counting()));

        return AuditStatisticsDTO.builder()
                .totalCount(totalCount)
                .successCount(successCount)
                .failCount(failCount)
                .sensitiveCount(sensitiveCount)
                .avgDuration(avgDuration)
                .operationTypeStats(operationTypeStats)
                .moduleStats(moduleStats)
                .userStats(userStats)
                .build();
    }

    @Override
    public List<AuditStatisticsDTO.DailyTrend> getTrend(Long tenantId, String startDate, String endDate) {
        LocalDateTime start = LocalDateTime.parse(startDate + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime end = LocalDateTime.parse(endDate + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        LambdaQueryWrapper<AuditLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(tenantId != null, AuditLog::getTenantId, tenantId);
        wrapper.between(AuditLog::getCreatedAt, start, end);
        wrapper.orderByAsc(AuditLog::getCreatedAt);

        List<AuditLog> logs = auditLogRepository.selectList(wrapper);

        return logs.stream()
                .collect(Collectors.groupingBy(log -> log.getCreatedAt().toLocalDate().toString()))
                .entrySet().stream()
                .map(entry -> AuditStatisticsDTO.DailyTrend.builder()
                        .date(entry.getKey())
                        .count((long) entry.getValue().size())
                        .successCount((long) entry.getValue().stream().filter(l -> "SUCCESS".equals(l.getAuditStatus())).count())
                        .failCount((long) entry.getValue().stream().filter(l -> "FAIL".equals(l.getAuditStatus())).count())
                        .build())
                .sorted(Comparator.comparing(AuditStatisticsDTO.DailyTrend::getDate))
                .collect(Collectors.toList());
    }

    @Override
    @Async
    public void batchCreate(List<AuditLogCreateRequest> requests) {
        List<AuditLog> entities = requests.stream()
                .map(req -> {
                    AuditLog entity = BeanCopyUtils.copy(req, AuditLog.class);
                    entity.setAuditStatus("SUCCESS");
                    entity.setCreatedAt(LocalDateTime.now());
                    return entity;
                })
                .collect(Collectors.toList());
        auditLogRepository.batchInsert(entities);
    }

    @Override
    @Transactional
    public int cleanExpiredLogs(int retentionDays) {
        LocalDateTime threshold = LocalDateTime.now().minusDays(retentionDays);
        LambdaQueryWrapper<AuditLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(AuditLog::getCreatedAt, threshold);
        return auditLogRepository.delete(wrapper);
    }
}
