package com.caliverse.admin.scheduler.service;

import com.caliverse.admin.domain.RabbitMq.MessageHandlerService;
import com.caliverse.admin.domain.RabbitMq.message.LanguageType;
import com.caliverse.admin.domain.RabbitMq.message.MailItem;
import com.caliverse.admin.domain.RabbitMq.message.OperationSystemMessage;
import com.caliverse.admin.domain.entity.*;
import com.caliverse.admin.domain.service.MailService;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import com.caliverse.admin.domain.entity.redis.RedisLoginInfo;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.ExcelUtils;
import com.caliverse.admin.scheduler.config.ScheduleExecutionConfig;
import com.caliverse.admin.scheduler.ScheduleType;
import com.caliverse.admin.scheduler.Scheduler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MailScheduler implements Scheduler {
    private Mail mail;
    private MailService mailService;
    private RedisUserInfoService redisUserInfoService;
    private MessageHandlerService messageHandlerService;
    private ExcelUtils excelUtils;

    @Override
    public void execute() {
        try {
            processMail();
        } catch (Exception e) {
            log.error("Mail execution failed for mail ID: {}", mail.getId(), e);
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
        }
    }

    @Override
    public boolean isRepeatable() {
        return false;
    }

    @Override
    public ScheduleType getScheduleType() {
        return mail.getSendType() == Mail.SENDTYPE.DIRECT_SEND
                ? ScheduleType.IMMEDIATE
                : ScheduleType.DELAYED;
    }

    @Override
    public ScheduleExecutionConfig getSchedulerConfig() {
        return ScheduleExecutionConfig.builder()
                .taskId("mail-" + mail.getId())
                .startTime(mail.getSendDt())
                .build();
    }

    private void processMail() {
        if (mail.getReceiveType().equals(Mail.RECEIVETYPE.SINGLE)) {
            boolean isSend = mailSend(mail);
            if (isSend) {
                mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
            }
        } else {
            List<Excel> excelList = excelUtils.getExcelListData(mail.getTarget());
            log.info("mailJob mailSchedule schedule run ExcelList : {}", excelList);
            boolean isSend;
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.RUNNING);

            for (Excel excel : excelList) {
                String guid = mailService.getGuid(excel.getUser(), excel.getType());
                Mail tempMail = copyMailWithNewTarget(mail, guid);
                isSend = mailSend(tempMail);

                if (!isSend) {
                    log.error("mailJob mailSchedule Excel fail user : {}", excel.getUser());
                    mailService.setScheduleLog(HISTORYTYPE.SCHEDULE_MAIL_FAIL,
                            "mail schedule id: " + mail.getId() + " Excel Send Fail User: " + excel.getUser());
                } else {
                    log.info("mailJob mailSchedule Excel send user : {}", excel.getUser());
                }
            }
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
        }
    }

    private Mail copyMailWithNewTarget(Mail originalMail, String newTarget) {
        Mail newMail = new Mail();
        BeanUtils.copyProperties(originalMail, newMail);
        newMail.setTarget(newTarget);
        return newMail;
    }

    // 메일 전송
    private boolean mailSend(Mail mail) {
        try {
            RedisLoginInfo info = redisUserInfoService.getUserLoginSessionInfo(mail.getTarget());
            String serverName = Optional.ofNullable(info)
                    .map(RedisLoginInfo::getCurrentServer)
                    .orElseGet(() -> {
                        String firstChannel = redisUserInfoService.getFirstChannel();
                        if (firstChannel == null) {
                            log.error("mailJob mailSend serverName is empty");
                            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
                            mailService.setScheduleLog(HISTORYTYPE.MAIL_SEND_FAIL, "is null server name");
                            return null;
                        }
                        return firstChannel;
                    });

            if (serverName == null) {
                return false;
            }

            List<Message> msgList = mailService.getMailMessageList(mail.getId());
            List<OperationSystemMessage> titleList = new ArrayList<>();
            List<OperationSystemMessage> contentList = new ArrayList<>();
            List<OperationSystemMessage> senderList = new ArrayList<>();

            for (Message msg : msgList) {
                LanguageType lang;
                String sender;

                switch (msg.getLanguage()) {
                    case "EN" -> {
                        lang = LanguageType.LanguageType_en;
                        sender = "Administrator";
                    }
                    case "JA" -> {
                        lang = LanguageType.LanguageType_ja;
                        sender = "アドミニストレーター";
                    }
                    default -> {
                        lang = LanguageType.LanguageType_ko;
                        sender = "시스템 관리자";
                    }
                }

                titleList.add(createOperationSystemMessage(lang, msg.getTitle()));
                contentList.add(createOperationSystemMessage(lang, msg.getContent()));
                senderList.add(createOperationSystemMessage(lang, sender));
            }

            List<Item> itemList = mailService.getMailItemList(mail.getId());
            List<MailItem> mailItemList = itemList.stream()
                    .map(item -> MailItem.newBuilder()
                            .setItemId(CommonUtils.stringToInt(item.getItem()))
                            .setCount(item.getItemCnt())
                            .build())
                    .collect(Collectors.toList());

            log.info("Send Mail Message : {}, target : {}, type :{}",
                    contentList, mail.getTarget(), mail.getMailType());

            messageHandlerService.sendMailMessage(
                    serverName,
                    mail.getTarget(),
                    mail.getMailType().toString(),
                    titleList,
                    contentList,
                    mailItemList,
                    senderList
            );

            log.info("mailJob mailSend completed");
            return true;

        } catch (Exception e) {
            log.error("mailSend Exception: {}", e.getMessage());
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
            mailService.setScheduleLog(HISTORYTYPE.MAIL_SEND_FAIL, e.getMessage());
            return false;
        }
    }

    private OperationSystemMessage createOperationSystemMessage(LanguageType lang, String text) {
        return OperationSystemMessage.newBuilder()
                .setLanguageType(lang)
                .setText(CommonUtils.stringToByte(text))
                .build();
    }
}
