package com.erp.audit.service.impl;

import com.erp.audit.dto.AuditLogDTO;
import com.erp.audit.dto.AuditLogQueryRequest;
import com.erp.audit.dto.ExportRequest;
import com.erp.audit.entity.AuditLog;
import com.erp.audit.repository.AuditLogRepository;
import com.erp.audit.service.ExportService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.LongestMatchColumnWidthStyleStrategy;
import com.erp.common.utils.BeanCopyUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.springframework.stereotype.Service;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 导出服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ExportServiceImpl implements ExportService {

    private final AuditLogRepository auditLogRepository;

    private static final String[] CSV_HEADERS = {
            "ID", "用户名", "真实姓名", "模块", "操作类型", "描述", "请求URL", 
            "请求方法", "IP", "业务类型", "业务ID", "耗时(ms)", "状态", "是否敏感", "创建时间"
    };

    @Override
    public void exportCsv(ExportRequest request, HttpServletResponse response) throws Exception {
        List<AuditLog> data = queryLogs(request.getQuery());
        String fileName = getFileName(request.getFileName(), "csv");

        response.setContentType("text/csv");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", 
                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

        try (OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8);
             CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.builder()
                     .setHeader(CSV_HEADERS)
                     .build())) {

            for (AuditLog log : data) {
                csvPrinter.printRecord(
                        log.getId(),
                        log.getUsername(),
                        log.getRealName(),
                        log.getModule(),
                        log.getOperationType(),
                        log.getDescription(),
                        log.getRequestUrl(),
                        log.getRequestMethod(),
                        log.getIpAddress(),
                        log.getBizType(),
                        log.getBizId(),
                        log.getDuration(),
                        log.getAuditStatus(),
                        log.getSensitive() ? "是" : "否",
                        log.getCreatedAt() != null ? log.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : ""
                );
            }
        }
        log.info("✅ CSV导出完成，共 {} 条记录", data.size());
    }

    @Override
    public void exportExcel(ExportRequest request, HttpServletResponse response) throws Exception {
        List<AuditLog> data = queryLogs(request.getQuery());
        String fileName = getFileName(request.getFileName(), "xlsx");

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", 
                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

        List<AuditLogExcelDTO> excelData = data.stream()
                .map(log -> AuditLogExcelDTO.builder()
                        .id(log.getId())
                        .username(log.getUsername())
                        .realName(log.getRealName())
                        .module(log.getModule())
                        .operationType(log.getOperationType())
                        .description(log.getDescription())
                        .requestUrl(log.getRequestUrl())
                        .requestMethod(log.getRequestMethod())
                        .ipAddress(log.getIpAddress())
                        .bizType(log.getBizType())
                        .bizId(log.getBizId())
                        .duration(log.getDuration())
                        .auditStatus(log.getAuditStatus())
                        .sensitive(log.getSensitive() ? "是" : "否")
                        .createdAt(log.getCreatedAt() != null ? log.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : "")
                        .build())
                .collect(Collectors.toList());

        EasyExcel.write(response.getOutputStream(), AuditLogExcelDTO.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet("审计日志")
                .doWrite(excelData);

        log.info("✅ Excel导出完成，共 {} 条记录", data.size());
    }

    @Override
    public void exportList(List<?> data, String format, String[] headers, String fileName, HttpServletResponse response) throws Exception {
        if ("CSV".equalsIgnoreCase(format)) {
            response.setContentType("text/csv");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", 
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            try (OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8);
                 CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT.builder().setHeader(headers).build())) {
                for (Object row : data) {
                    if (row instanceof Map) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> map = (Map<String, Object>) row;
                        printer.print(map.values().toArray());
                    }
                }
            }
        } else {
            exportExcel(ExportRequest.builder().fileName(fileName).build(), response);
        }
    }

    private List<AuditLog> queryLogs(AuditLogQueryRequest query) {
        com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<AuditLog> wrapper = 
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
        if (query.getTenantId() != null) {
            wrapper.eq(AuditLog::getTenantId, query.getTenantId());
        }
        if (query.getStartTime() != null) {
            wrapper.ge(AuditLog::getCreatedAt, query.getStartTime());
        }
        if (query.getEndTime() != null) {
            wrapper.le(AuditLog::getCreatedAt, query.getEndTime());
        }
        if (query.getUsername() != null) {
            wrapper.like(AuditLog::getUsername, query.getUsername());
        }
        if (query.getModule() != null) {
            wrapper.eq(AuditLog::getModule, query.getModule());
        }
        if (query.getOperationType() != null) {
            wrapper.eq(AuditLog::getOperationType, query.getOperationType());
        }
        wrapper.orderByDesc(AuditLog::getCreatedAt);
        wrapper.last("LIMIT 100000");
        return auditLogRepository.selectList(wrapper);
    }

    private String getFileName(String fileName, String ext) {
        if (fileName != null && !fileName.isEmpty()) {
            return fileName.endsWith("." + ext) ? fileName : fileName + "." + ext;
        }
        return "audit_log_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + "." + ext;
    }

    /**
     * Excel导出DTO
     */
    @lombok.Data
    @lombok.Builder
    @lombok.NoArgsConstructor
    @lombok.AllArgsConstructor
    public static class AuditLogExcelDTO {
        private Long id;
        private String username;
        private String realName;
        private String module;
        private String operationType;
        private String description;
        private String requestUrl;
        private String requestMethod;
        private String ipAddress;
        private String bizType;
        private String bizId;
        private Long duration;
        private String auditStatus;
        private String sensitive;
        private String createdAt;
    }
}
