package org.dromara.system.service.impl;

import cn.hutool.json.JSONUtil;
import cn.idev.excel.annotation.ExcelProperty;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.dromara.system.domain.bo.GameTeamBo;
import org.dromara.system.domain.vo.EnrollProjectVo;
import org.dromara.system.service.IEnrollService;
import org.dromara.system.service.IGameEventProjectService;
import org.dromara.system.service.IGameTeamService;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class IEnrollServiceImpl implements IEnrollService {

    private final IGameEventProjectService gameEventProjectService;
    private final IGameTeamService gameTeamService;
    private final ConcurrentHashMap<Long, Class> classMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Long, List<String>> projectsMap = new ConcurrentHashMap<>();

    /**
     * 下载报名表模板
     *
     * @param response
     * @param eventId
     */
    @Override
    public void downloadTemplate(HttpServletResponse response, Long eventId) {
        // 1. 获取默认赛事动态项目（赛事项目）
        Map<String, List<String>> projectMap = gameEventProjectService.mapProjectTypeAndProject(eventId);
        List<String> projects = new ArrayList<>();
        for (List<String> list : projectMap.values()) {
            projects.addAll(list);
        }
        projectsMap.put(eventId, projects);

        // 2. 构建表头行（CSV 用逗号分隔）
        List<String> headers = new ArrayList<>();
        // 反射获取属性的注解value值
        Class<EnrollProjectVo> enrollClass = EnrollProjectVo.class;
        Field[] declaredFields = enrollClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            // 检查字段是否含有 ExcelProperty 注解
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                // 获取注解的 value 值
                String[] value = annotation.value();
                log.error("注解值：{}", Arrays.toString(value));
                headers.add(value[0]);
            }
        }
        classMap.put(eventId, enrollClass);
        headers.addAll(projects);

        // 3. 使用 UTF-8 编码的 CSV，兼容 Excel 打开
        String fileName = URLEncoder.encode("报名导入模板", StandardCharsets.UTF_8) + ".csv";

        response.setContentType("text/csv; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName);

        // 4. 使用原始 IO 流输出 CSV
        try (OutputStream os = response.getOutputStream();
             OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {

            // 写入 BOM（可选）：让 Excel 正确识别 UTF-8
            writer.write('\uFEFF'); // UTF-8 BOM

            // 写入表头（用逗号连接）
            writer.write(String.join(",", headers));
            writer.write("\n"); // 换行

            // 可选：写入一行空数据作为示例
            String emptyRow = String.join(",", Collections.nCopies(headers.size(), ""));
            writer.write(emptyRow);
            writer.write("\n");

            // 强制刷新
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导入报名表
     *
     * @param file
     * @param eventId
     * @return
     */
    @Override
    public ResponseEntity<Map<String, Object>> importData(MultipartFile file, Long eventId) {

        Map<String, Object> result = new HashMap<>();
        List<String> errors = new ArrayList<>();

        if (file.isEmpty()) {
            errors.add("上传文件不能为空");
            result.put("success", false);
            result.put("errors", errors);
            return ResponseEntity.badRequest().body(result);
        }

        try (InputStream inputStream = file.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

            // 1. 读取第一行：表头
            String headerLine = reader.readLine();
            if (headerLine == null) {
                errors.add("文件为空");
                result.put("success", false);
                result.put("errors", errors);
                return ResponseEntity.badRequest().body(result);
            }

            // 去除 BOM（如果存在）
            if (headerLine.startsWith("\uFEFF")) {
                headerLine = headerLine.substring(1);
            }

            String[] headers = headerLine.split(",", -1); // -1 保留空字段

            // 添加调试日志，输出实际读取到的表头
            log.info("实际读取到的表头: {}", Arrays.toString(headers));
            log.info("表头行原始内容: '{}'", headerLine);

            // 2. 获取动态项目列（从数据库或服务中）
            Map<String, List<String>> projectMap = gameEventProjectService.mapProjectTypeAndProject(eventId);
            List<String> validProjects = new ArrayList<>();
            for (List<String> list : projectMap.values()) {
                validProjects.addAll(list);
            }

            // 3. 验证表头是否合法
            Set<String> expectedHeaders = new LinkedHashSet<>();
            // 添加固定字段（来自 EnrollProjectVo）
            Field[] fields = EnrollProjectVo.class.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    ExcelProperty prop = field.getAnnotation(ExcelProperty.class);
                    expectedHeaders.add(prop.value()[0]);
                }
            }
            expectedHeaders.addAll(validProjects); // 加上动态项目

            Set<String> actualHeaders = new LinkedHashSet<>(Arrays.asList(headers));

            // 改进表头验证逻辑
            if (!actualHeaders.equals(expectedHeaders)) {
                log.error("表头不匹配，期望: {}, 实际: {}", expectedHeaders, actualHeaders);

                // 检查是否完全不匹配（可能文件格式错误）
                boolean hasAnyMatch = false;
                for (String actualHeader : actualHeaders) {
                    if (expectedHeaders.contains(actualHeader)) {
                        hasAnyMatch = true;
                        break;
                    }
                }

                if (!hasAnyMatch) {
                    errors.add("上传的文件格式不正确，请使用系统生成的导入模板");
                    errors.add("期望的表头: " + String.join(", ", expectedHeaders));
                    errors.add("实际的表头: " + String.join(", ", actualHeaders));
                    result.put("success", false);
                    result.put("errors", errors);
                    return ResponseEntity.badRequest().body(result);
                }

                // 如果部分匹配，给出详细的差异信息
                Set<String> missingHeaders = new LinkedHashSet<>(expectedHeaders);
                missingHeaders.removeAll(actualHeaders);
                Set<String> extraHeaders = new LinkedHashSet<>(actualHeaders);
                extraHeaders.removeAll(expectedHeaders);

                if (!missingHeaders.isEmpty()) {
                    errors.add("缺少必要的表头字段: " + String.join(", ", missingHeaders));
                }
                if (!extraHeaders.isEmpty()) {
                    log.warn("包含额外的表头字段: {}", String.join(", ", extraHeaders));
                }

                if (!errors.isEmpty()) {
                    result.put("success", false);
                    result.put("errors", errors);
                    return ResponseEntity.badRequest().body(result);
                }
            }

            // 4. 逐行读取数据
            String line;
            int lineNumber = 2; // 数据从第2行开始（第1行是表头）
            List<EnrollProjectVo> dataList = new ArrayList<>();

            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty()) continue;

                String[] values = line.split(",", -1); // 保持空值
                if (values.length != headers.length) {
                    errors.add("第" + lineNumber + "行数据列数不匹配，期望" + headers.length + "列，实际" + values.length + "列");
                    lineNumber++;
                    continue;
                }

                // 映射到 EnrollProjectVo
                EnrollProjectVo vo = mapRowToVo(headers, values, validProjects);
                if (vo != null) {
                    dataList.add(vo);
                } else {
                    errors.add("第" + lineNumber + "行数据格式错误");
                }

                lineNumber++;
            }

            // 5. 数据校验 & 保存
            if (!errors.isEmpty()) {
                result.put("success", false);
                result.put("errors", errors);
                return ResponseEntity.badRequest().body(result);
            }
            log.info("读取到的数据:{}", dataList);
            // 调用 Service 层保存数据
            boolean saveSuccess = this.saveEnrollData(dataList, eventId);
            if (!saveSuccess) {
                result.put("success", false);
                result.put("errors", Arrays.asList("保存数据失败，请重试"));
                return ResponseEntity.status(500).body(result);
            }

            log.info("成功读取的数据条数: {}", dataList.size());

            result.put("success", true);
            result.put("message", "导入成功，共导入 " + dataList.size() + " 条记录");
            result.put("data", dataList);
            return ResponseEntity.ok(result);

        } catch (Exception e) {
            log.error("导入数据时发生异常", e);
            errors.add("文件解析失败: " + e.getMessage());
            result.put("success", false);
            result.put("errors", errors);
            return ResponseEntity.status(500).body(result);
        }
    }

    /**
     * 保存数据
     *
     * @param dataList
     * @param eventId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean saveEnrollData(List<EnrollProjectVo> dataList, Long eventId) {
        // - 读取到的数据:[EnrollProjectVo(name=林啊啊啊, teamName=废物队,
        // sex=男, age=20,  leader=少爷, phone=34567890,
        // ProjectSelections={4*400米接力=false, 4*100米接力=true, 跳高=true})]
        // 1.根据队伍生成号码段 五个一组
        // 1.2 根据队伍分类成Map
        Map<String, List<EnrollProjectVo>> groupedByTeam = dataList.stream()
            .collect(Collectors.groupingBy(EnrollProjectVo::getTeamName));
        // 1.3 根据队伍生成号码段(生成一个map key:teamName value:队员数量)
        Map<String, Long> teamCountMap = dataList.stream()
            .collect(Collectors.groupingBy(
                EnrollProjectVo::getTeamName,
                Collectors.counting()
            ));
        // 2.校验是否存在项目(如果项目不存在就无视)
        List<String> projects = projectsMap.get(eventId);
        // 3.保存参赛队员
        // 4.保存参赛队伍
        for (Map.Entry<String, List<EnrollProjectVo>> entry : groupedByTeam.entrySet()) {
            String key = entry.getKey();
            List<EnrollProjectVo> value = entry.getValue();
            GameTeamBo gameTeamBo = new GameTeamBo();
            gameTeamBo.setEventId(eventId);
            gameTeamBo.setTeamName(key);
            // gameTeamBo.setTeamCode();
            gameTeamBo.setLeader(value.get(0).getLeader());
            gameTeamBo.setAthleteValue(JSONUtil.toJsonStr(value.stream().map(EnrollProjectVo::getName).collect(Collectors.toList())));
            gameTeamBo.setAthleteNum(Long.valueOf(value.size()));
            // gameTeamBo.setNumberRange();
            gameTeamBo.setStatus("0");
            gameTeamService.insertByBo(gameTeamBo);
        }
        return true;
    }


    /**
     * 映射
     *
     * @param headers
     * @param values
     * @param validProjects
     * @return
     */
    private EnrollProjectVo mapRowToVo(String[] headers, String[] values, List<String> validProjects) {
        EnrollProjectVo vo = new EnrollProjectVo();

        try {
            for (int i = 0; i < headers.length; i++) {
                String header = headers[i];
                String value = i < values.length ? values[i].trim() : "";

                // 判断是否是固定字段
                boolean matched = false;
                Field[] fields = EnrollProjectVo.class.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(ExcelProperty.class)) {
                        ExcelProperty prop = field.getAnnotation(ExcelProperty.class);
                        if (prop.value()[0].equals(header)) {
                            field.setAccessible(true);
                            // 简单类型转换（可扩展）
                            if (field.getType() == String.class) {
                                field.set(vo, value.isEmpty() ? null : value);
                            } else if (field.getType() == Integer.class || field.getType() == int.class) {
                                field.set(vo, value.isEmpty() ? null : Integer.valueOf(value));
                            } else if (field.getType() == Long.class || field.getType() == long.class) {
                                field.set(vo, value.isEmpty() ? null : Long.valueOf(value));
                            }
                            matched = true;
                            break;
                        }
                    }
                }

                // 如果不是固定字段，则视为“项目报名”字段
                if (!matched && validProjects.contains(header)) {
                    // 假设 value 是“是/否”、“1/0”、“√”等
                    boolean enrolled = isTrueValue(value);
                    // 使用 map 或其他结构存储项目报名状态
                    if (vo.getProjectSelections() == null) {
                        vo.setProjectSelections(new HashMap<>());
                    }
                    vo.getProjectSelections().put(header, enrolled);
                }
            }
            return vo;
        } catch (Exception e) {
            log.error("映射行数据失败", e);
            return null;
        }
    }


    // 判断是否为“真值”
    private boolean isTrueValue(String val) {
        return Arrays.asList("是", "√", "1", "true", "报名", "已报").contains(val);
    }
}
