package com.cmeeting.job;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cmeeting.constant.MeetingState;
import com.cmeeting.email.EmailSender;
import com.cmeeting.mapper.primary.MeetingInfoMapper;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.util.AESUtils;
import com.cmeeting.util.MinioUtils;
import com.cmeeting.util.RedisUtils;
import com.cmeeting.vo.EmailPush;
import com.deepoove.poi.XWPFTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Data
@NoArgsConstructor
@Slf4j
@Service
public class EmailPushTask {
    private String meetingRecordId;
    private String meetingId;
    private String subMeetingId;
    private String savePath;
    private Map<String, Object> metadata;

    private static final int MAX_RETRY = 3;


    private MeetingInfoMapper meetingInfoMapper;
    private MinioUtils minioUtils;
    private RedisUtils redisUtils;
    private EmailSender emailSender;
    private MeetingRecordTemplateMapper meetingRecordTemplateMapper;
    private Map<String,String> tidWidRelations;
    public String aesKey;

    // 实际处理逻辑
    public void process() {
        String key = "meet_process-" + meetingRecordId;
        if (!redisUtils.setnx(key, 1, 120)) {
            log.warn("key already exists in redis!, key: {}", key);
            return;
        }
        log.info("线程开始------------>");
        long l = System.currentTimeMillis();
        Boolean isSuccess = false;
        AtomicInteger retryCount = new AtomicInteger(0);
        MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>()
                .eq(MeetingInfo::getMeetingRecordId, meetingRecordId));
        if (meetingInfo.getIsPushed()) {
            log.warn("邮件已推送, meetingId: {}", meetingId);
            return;
        }
        while (retryCount.intValue() <= MAX_RETRY && isSuccess != null && !isSuccess) {
            String hostUid = meetingInfo.getHostUid();
            if(!tidWidRelations.containsKey(hostUid)){
                log.error("邮件推送重试失败: 主持人对应关系未配置。meetingId {}", meetingId);
                continue;
            }
            try {
                String meetingName;
                String xml;

                try(InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordXml());){
                    // 解密
                    xml = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
                }catch (Exception e){
                    log.error(e.getMessage());
                    throw new RuntimeException(e);
                }

                //将xml格式的内容转换为map,用于填充模板
                Map<String, Object> dataModel = convertXmlToMap(xml);
                //追加参会人员信息
                Map<String,Object> participantsMap = new ConcurrentHashMap<>();
                participantsMap.put("meeting_date",meetingInfo.getStartTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                participantsMap.put("meeting_location","线上腾讯会议");
                participantsMap.put("meeting_participants", meetingInfo.getParticipantUsers());
                participantsMap.put("meeting_host",meetingInfo.getHost());
                dataModel.putAll(participantsMap);

                MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(1);
                meetingName = dataModel.get("meeting_name") != null ? String.valueOf(dataModel.get("meeting_name")) : "腾讯会议纪要";
                String dataNetworkMinutesPath = savePath + meetingName + "_" + meetingRecordTemplate.getName() + ".docx";
                try (InputStream inputStream = minioUtils.getFile(meetingRecordTemplate.getTemplate())) {
                    XWPFTemplate template = XWPFTemplate.compile(inputStream).render(dataModel);
                    template.writeAndClose(new FileOutputStream(dataNetworkMinutesPath));
                } catch (IOException e) {
                    e.printStackTrace();
                }

                //邮件推送
                List<EmailPush.Attachment> attachments = new ArrayList<>();
                try(InputStream is = new FileInputStream(dataNetworkMinutesPath)){
                    byte[] meetingMinutesBytes = IOUtils.toByteArray(is);
                    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd");
                    EmailPush.Attachment attachment = EmailPush.Attachment.builder().name(meetingInfo.getSubject()+"会议纪要_" + fmt.format(meetingInfo.getStartTime())).bytes(meetingMinutesBytes).build();
                    attachments.add(attachment);
                }catch (Exception e){
                    throw new RuntimeException(e);
                }finally {
                    FileUtil.del(dataNetworkMinutesPath);
                }

                Resource resource = new ClassPathResource("static/Cmeeting会议助手使用手册.pdf");
                byte[] meetingMinutesBytes = IOUtils.toByteArray(resource.getInputStream());
                EmailPush.Attachment attachment = EmailPush.Attachment.builder().name("Cmeeting会议助手使用手册.pdf").bytes(meetingMinutesBytes).build();
                attachments.add(attachment);

                String userCode = tidWidRelations.get(hostUid);
                EmailPush emailPushBuilder = EmailPush.builder()
                        .toEmail(meetingInfo.getEmail())
                        .meetingId(meetingId)
                        .meetingRecordId(meetingRecordId)
                        .attachments(attachments)
                        .subject(meetingInfo.getSubject())
                        .meetingInstanceId(meetingInfo.getId())
                        .subMeetingId(meetingInfo.getSubMeetingId())
                        .toUserCode(userCode)
                        .toUser(meetingInfo.getHost())
                        .emailPushAccess(meetingInfo.getEmailPushAccess())
                        .build();
                isSuccess = emailSender.sendEmailWithAttachment(emailPushBuilder);
                if (!isSuccess) {
                    int currentRetryCount = retryCount.getAndIncrement();
                    if (currentRetryCount <= MAX_RETRY) {
                        // 指数退避
                        try {
                            Thread.sleep((long) Math.pow(2, currentRetryCount) * 1000);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            log.error("重试失败: {}", ie.getMessage());
                        }
                    }
                }
            } catch (Exception e) {
                // 异常处理
                e.printStackTrace();
                log.error("push email error: {}", e.getMessage());
                int currentRetryCount = retryCount.getAndIncrement();
                if (currentRetryCount > MAX_RETRY) {
                    log.error("达到最大重试次数:meetingId {}", meetingId);
                } else {
                    // 指数退避
                    try {
                        Thread.sleep((long) Math.pow(2, currentRetryCount) * 1000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        log.error("重试失败: {}", ie.getMessage());
                    }
                }
            }
        }
        if (retryCount.get() > MAX_RETRY) {
            log.error("达到最大重试次数:meetingId {}", meetingId);
        }
        if (isSuccess != null) {
            meetingInfoMapper.update(meetingInfo,
                    new LambdaUpdateWrapper<MeetingInfo>()
                            .eq(MeetingInfo::getMeetingRecordId, meetingRecordId)
                            .set(MeetingInfo::getIsPushed,isSuccess)
                            .set(MeetingInfo::getStatus, isSuccess ? MeetingState.PUSH_SUCCESS.getCode() : MeetingState.PUSH_ERROR.getCode())
                            .set(MeetingInfo::getPushRetry, Boolean.TRUE)
            );
        }
        redisUtils.del(key);
        log.info("线程结束, 耗时: {} ms", System.currentTimeMillis() - l);
    }


    public static Map<String, Object> convertXmlToMap(String xml) throws Exception {

        XmlMapper xmlMapper = new XmlMapper();
        ObjectMapper objectMapper = new ObjectMapper();

        // 先将 XML 读取为 Map
        Map<?, ?> xmlMap = xmlMapper.readValue(xml, Map.class);

        // 转换为更标准的 Map<String, Object>
        Map<String,Object> map = objectMapper.convertValue(xmlMap, Map.class);
        //特殊处理map格式
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Map<String,Object> value = (Map<String, Object>) entry.getValue();
            //取出正确的value并设置
            String realValue = String.valueOf(value.get("")).replaceAll("^\\n+","");
            //内容段首移除换行，段末追加换行(会议名称结尾不换行
            entry.setValue(realValue.endsWith("\n") || "meeting_name".equals(entry.getKey()) || "meeting_purpose".equals(entry.getKey()) ? realValue : realValue + "\n");
        }
        return map;
    }


    public static String convertInputStreamToString(InputStream inputStream) throws IOException {
        StringBuilder result = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
                // 如果需要保留换行符，可添加：result.append("\n");
            }
        }
        return result.toString();
    }


    public EmailPushTask(String meetingRecordId, String meetingId, String subMeetingId, String savePath, Map<String, Object> metadata,
                         MeetingInfoMapper meetingInfoMapper, MinioUtils minioUtils, RedisUtils redisUtils, EmailSender emailSender,
                         MeetingRecordTemplateMapper meetingRecordTemplateMapper, Map<String,String> tidWidRelations, String aesKey) {
        this.savePath = savePath;
        this.metadata = metadata;
        this.meetingRecordId = meetingRecordId;
        this.meetingId = meetingId;
        this.subMeetingId = subMeetingId;
        this.meetingInfoMapper = meetingInfoMapper;
        this.minioUtils = minioUtils;
        this.redisUtils = redisUtils;
        this.emailSender = emailSender;
        this.meetingRecordTemplateMapper = meetingRecordTemplateMapper;
        this.tidWidRelations = tidWidRelations;
        this.aesKey = aesKey;
    }
}