package com.caliverse.admin.scheduler.polling.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.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.domain.entity.redis.RedisLoginInfo;
import com.caliverse.admin.domain.service.MailService;
import com.caliverse.admin.global.common.annotation.BusinessProcess;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.ExcelUtils;
import com.caliverse.admin.global.component.manager.BusinessProcessIdManager;
import com.caliverse.admin.mongodb.service.BusinessLogService;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import com.caliverse.admin.scheduler.CommonScheduler;
import com.caliverse.admin.scheduler.ScheduleService;
import com.caliverse.admin.scheduler.entity.SchedulerType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
public class MailScheduler extends CommonScheduler {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final MailService mailService;
    private final RedisUserInfoService redisUserInfoService;
    private final MessageHandlerService messageHandlerService;
    private final ScheduleService schedulerService;
    private final ExcelUtils excelUtils;

    public MailScheduler(MailService mailService,
                         RedisUserInfoService redisUserInfoService,
                         MessageHandlerService messageHandlerService,
                         ScheduleService schedulerService,
                         ExcelUtils excelUtils,
                         BusinessLogService businessLogService,
                         BusinessProcessIdManager processIdManager) {
        super(businessLogService, processIdManager);
        this.mailService = mailService;
        this.redisUserInfoService = redisUserInfoService;
        this.messageHandlerService = messageHandlerService;
        this.schedulerService = schedulerService;
        this.excelUtils = excelUtils;
    }

    @Override
    protected void executeInternal() {
        executeWithProcessId(LogAction.SCHEDULE_MAIL, this::executeLogic);
    }

    private void executeLogic(){
        List<Mail> mailList = mailService.getScheduleMailList();

        if (mailList.isEmpty()) {
            return;
        }

        AtomicInteger processedCount = new AtomicInteger(0);
        AtomicInteger directSendCount = new AtomicInteger(0);
        AtomicInteger scheduledCount = new AtomicInteger(0);

        LocalDateTime startTime = LocalDateTime.now();

        try{
            mailList.forEach(mail -> {
                if(mail.getSendStatus().equals(Mail.SENDSTATUS.WAIT)){
                    processedCount.incrementAndGet();
                    if(mail.getSendType() == Mail.SENDTYPE.DIRECT_SEND){
                        directSendCount.incrementAndGet();
                        log.info("mailJob mailSchedule direct run : {}", mail);
                        mailTypeProcess(mail);
                    }else {
                        String key = "mail-" + mail.getId();
                        if (!schedulerService.isTaskExist(key)) {
                            scheduledCount.incrementAndGet();
                            Runnable taskRunner = () -> {
                                log.info("mailJob mailSchedule schedule run : {}", mail);
                                mailTypeProcess(mail);
                                // 스케줄 해제
                                schedulerService.closeTask(key);
                            };

                            // 시작일자 - 현재일자
                            LocalDateTime startDate = mail.getSendDt();
                            long initialDelay = Duration.between(LocalDateTime.now(), startDate).toMillis();

                            log.info("mailJob mailSchedule schedule add : {}", mail);
                            ScheduledFuture<?> schedule = scheduler.schedule(taskRunner, initialDelay, TimeUnit.MILLISECONDS);
                            schedulerService.addTask(key, schedule);
                        }
                    }
                }
            });

            if (processedCount.get() > 0 && (directSendCount.get() > 0 || scheduledCount.get() > 0)) {
                businessLogService.logScheduler(
                        LogStatus.SUCCESS,
                        String.format("메일 스케줄 처리 완료: 총 %d건 처리 (즉시발송: %d건, 예약등록: %d건)",
                                processedCount.get(), directSendCount.get(), scheduledCount.get()),
                        "MAIL_SCHEDULER",
                        startTime,
                        LocalDateTime.now(),
                        Map.of(
                                "totalProcessed", processedCount.get(),
                                "directSend", directSendCount.get(),
                                "scheduled", scheduledCount.get(),
                                "totalMailCount", mailList.size()
                        )
                );
            }
        }catch (Exception e){
            log.error("mailSchedule Exception: {}", e.getMessage());
            throw e;
        }
    }

    private void mailTypeProcess(Mail mail){
        if(mail.getReceiveType().equals(Mail.RECEIVETYPE.SINGLE)){
            boolean is_send = mainSend(mail);
            if(is_send) mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
        }
        else{
            List<Excel> excelList = excelUtils.getExcelListData(mail.getTarget());
            log.info("mailJob mailSchedule schedule run ExcelList : {}", excelList);
            boolean is_send;
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.RUNNING);
            for(Excel excel : excelList){
                String guid = mailService.getGuid(excel.getUser(), excel.getType());
                mail.setTarget(guid);
                is_send = mainSend(mail);
                if(!is_send){
                    log.error("mailJob mailSchedule Excel fail user : {}", excel.getUser());
                }
                else log.info("mailJob mailSchedule Excel send user : {}", excel.getUser());
            }
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
        }
    }

    // 우편 전송
    private boolean mainSend(Mail mail){
        // 서버정보 가져오기
        try{
            RedisLoginInfo info = redisUserInfoService.getUserLoginSessionInfo(mail.getTarget());
            String server_name = null;
            if(info == null){
                server_name = redisUserInfoService.getFirstChannel();
                if(server_name == null){
                    log.error("mailJob mainSend serverName is empty");
                    mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
                    return false;
                }
            }else{
                server_name = info.getCurrentServer();
            }

            // 메시지 처리
            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 = null;
                String langText = msg.getLanguage();
                String sender = null;

                if(langText.equals(LANGUAGETYPE.EN.toString())){
                    lang = LanguageType.LanguageType_en;
                    sender = "Administrator";
                }else if(langText.equals(LANGUAGETYPE.JA.toString())){
                    lang = LanguageType.LanguageType_ja;
                    sender = "アドミニストレーター";
                }else{
                    lang = LanguageType.LanguageType_ko;
                    sender = "시스템 관리자";
                }
                titleList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(msg.getTitle())).build());
                contentList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(msg.getContent())).build());
                senderList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(sender)).build());
            }

            // 아이템 처리
            List<Item> itemList = mailService.getMailItemList(mail.getId());
            List<MailItem> mailItemList = new ArrayList<>();
            for(Item item : itemList){
                mailItemList.add(MailItem.newBuilder()
                        .setItemId(CommonUtils.stringToInt(item.getItem()))
                        .setCount(item.getItemCnt()).build());
            }

            log.info("Send Mail Message : {}, target : {}, type :{}", contentList, mail.getTarget(), mail.getMailType());
            messageHandlerService.sendMailMessage(server_name, 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);
        }
        return false;
    }

    @Override
    public SchedulerType getSchedulerType() {
        return SchedulerType.MAIL;
    }
}
