package com.caliverse.admin.domain.service;

import com.caliverse.admin.domain.dao.admin.HistoryMapper;
import com.caliverse.admin.domain.dao.admin.MailMapper;
import com.caliverse.admin.domain.dao.total.WalletUserMapper;
import com.caliverse.admin.domain.datacomponent.MetaDataHandler;
import com.caliverse.admin.domain.entity.*;
import com.caliverse.admin.domain.request.MailRequest;
import com.caliverse.admin.domain.response.MailResponse;
import com.caliverse.admin.dynamodb.service.DynamodbCaliumService;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.code.SuccessCode;
import com.caliverse.admin.global.common.constants.CommonConstants;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.ExcelUtils;
import com.caliverse.admin.scheduler.ScheduleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.ResourceLoader;

import java.net.MalformedURLException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class MailService {
    private final DynamoDBService dynamoDBService;
    private final WalletUserMapper walletUserMapper;

    @Value("${excel.file-path}")
    private String excelPath;
    @Value("${caliverse.file}")
    private String samplePath;
    private final ExcelUtils excelUtils;
    private final MailMapper mailMapper;
    private final HistoryMapper historyMapper;
    private final MetaDataHandler metaDataHandler;
    private final ResourceLoader resourceLoader;
    private final ScheduleService scheduleService;
    private final HistoryService historyService;
    private final DynamodbCaliumService dynamodbCaliumService;

    public MailResponse getStockCalium(){
        double stock_calium = dynamodbCaliumService.getCaliumTotal();

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .stockCalium(stock_calium)
                        .build()
                )
                .build();
    }

    public MailResponse getList(Map requestParam){

        // gameDB 조회 및 서비스 로직
        //페이징 처리
        requestParam = CommonUtils.pageSetting(requestParam);

        List<Mail> list = mailMapper.getMailList(requestParam);

        int allCnt = mailMapper.getAllCnt(requestParam);

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .mailList(list)
                        .total(mailMapper.getTotal())
                        .totalAll(allCnt)
                        .pageNo(requestParam.get("page_no")!=null?
                                Integer.valueOf(requestParam.get("page_no").toString()):1)
                        .build()
                )
                .build();
    }

    public MailResponse getdetail(Long id){

        // gameDB 조회 및 서비스 로직
        Mail mail = mailMapper.getMailDetail(id);

        mail.setMailList(mailMapper.getMessage(id));
//        mail.setItemList(mailMapper.getItem(id));
        List<Item> itemList = mailMapper.getItem(id);
        for(Item item : itemList){
            String itemName = metaDataHandler.getMetaItemNameData(Integer.parseInt(item.getItem()));
            item.setItemName(metaDataHandler.getTextStringData(itemName));
        }
        mail.setItemList(itemList);

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .mail(mail)
                        .build())
                .build();
    }

    public MailResponse excelUpload(MultipartFile file){

        List<Mail> list = new ArrayList<>();
        // 파일 존재하지 않는 경우
        if (file.isEmpty()) {
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.getMessage() );   //Excel 파일을 선택해주세요.
        }

        List listData = excelUtils.getListData(file, 1, 0);

        // 엑셀 파일내 중복된 데이터 있는지 체크
        if(excelUtils.hasDuplicates(listData)){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DUPLICATE_EXCEL.getMessage() );   //Excel 파일을 선택해주세요.
        }

        listData.forEach(
                item->{
                    Mail mail = new Mail();
                    mail.setGuid(item.toString());
                    list.add(mail);
                }
        );

        // 파일을 서버에 저장
        String fileName = excelUtils.saveExcelFile(file);

        return MailResponse.builder()
                .resultData(MailResponse.ResultData.builder()
                        .mailList(list).message(SuccessCode.EXCEL_UPLOAD.getMessage())
                        .fileName(fileName)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();
    }

    public Resource excelDown(String fileName) {
        Path filePath = Path.of(excelPath+fileName);
        try {
            if(fileName.contains("sample")){
                return resourceLoader.getResource(samplePath + fileName);
            }
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists() && resource.isReadable()) {
                return resource;
            } else {
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.getMessage());
            }
        }catch (MalformedURLException e){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.getMessage());
        }
    }

    @Transactional(transactionManager = "transactionManager")
    public MailResponse postMail(MailRequest mailRequest){
        mailRequest.setCreateBy(CommonUtils.getAdmin().getId());
        if(mailRequest.isReserve()){
            mailRequest.setSendType(Mail.SENDTYPE.RESERVE_SEND);
        }else{
            mailRequest.setSendType(Mail.SENDTYPE.DIRECT_SEND);
            mailRequest.setSendDt(LocalDateTime.now());
        }


        //단일일경우 guid 를 target 설정, 복수이면은 fileName을 target에 설정
        if(mailRequest.getGuid() != null){
            if(mailRequest.getUserType().equals(Mail.USERTYPE.GUID) && dynamoDBService.isGuidChecked(mailRequest.getGuid())){
                log.error("postMail RECEIVETYPE Single Guid Find Fail");
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.GUID_CHECK.getMessage());
            }
            String guid = mailRequest.getGuid();
            // 닉네임이면 guid로 바꾼다
            if(mailRequest.getUserType().equals(Mail.USERTYPE.NICKNAME)) {
//                guid = dynamoDBService.getNickNameByGuid(guid);
                guid = getGuid(guid,  Mail.USERTYPE.NICKNAME.name());
                if(guid == null || mailRequest.getGuid().equals(guid)){
                    log.error("postMail RECEIVETYPE Single Nickname Find Fail");
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NICKNAME_CHECK.getMessage() );
                }
            }else if(mailRequest.getUserType().equals(Mail.USERTYPE.EMAIL)){
                guid = getGuid(guid,  Mail.USERTYPE.EMAIL.name());
                if(guid == null || guid.isEmpty()){
                    log.error("postMail RECEIVETYPE Single EMAIL Find Fail");
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.EMAIL_CHECK.getMessage() );
                }
            }
            mailRequest.setReceiveType(Mail.RECEIVETYPE.SINGLE);
            mailRequest.setTarget(guid);
        }
        //엑셀 처리
        else{
            mailRequest.setReceiveType(Mail.RECEIVETYPE.MULTIPLE);
            if(mailRequest.getFileName() == null){
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.toString() );   //Excel 파일을 선택해주세요.
            }
            List<Excel> excelList = excelUtils.getExcelListData(mailRequest.getFileName());
            for(Excel excel : excelList){
                switch (excel.getType()) {
                    case "GUID" -> {
                        if (dynamoDBService.isGuidChecked(excel.getUser())) {
                            log.error("postMail Multi Guid({}) Find Fail", excel.getUser());
                            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.GUID_CHECK.toString());
                        }
                    }
                    case "NICKNAME" -> {
                        String user = getGuid(excel.getUser(), Mail.USERTYPE.NICKNAME.name());
                        if (user == null || user.isEmpty()) {
                            log.error("postMail Multi Nickname({}) Find Fail", excel.getUser());
                            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NICKNAME_CHECK.toString());
                        }
                    }
                    case "EMAIL" -> {
                        String user = getGuid(excel.getUser(), Mail.USERTYPE.EMAIL.name());
                        if (user == null || user.isEmpty()) {
                            log.error("postMail Multi Email({}) Find Fail", excel.getUser());
                            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.EMAIL_CHECK.toString());
                        }
                    }
                    default ->
                            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.USERTYPE_CHECK_EXCEL.toString());   //Excel 파일을 선택해주세요.
                }
            }
            mailRequest.setTarget(mailRequest.getFileName());
        }

        mailMapper.postMail(mailRequest);

        //log.info("mail_id::"+ mailRequest.getId());
        HashMap<String,String> map = new HashMap<>();
        map.put("mailId",String.valueOf(mailRequest.getId()));

        //아이템 저장
        if(mailRequest.getItemList()!= null && !mailRequest.getItemList().isEmpty()){
            //칼리움일경우 수량체크
            boolean hasCalium = mailRequest.getItemList().stream().anyMatch(item -> item.getItem().equals(CommonConstants.CALIUM_ITEM_CODE));
            double caliumSum = 0;
            if(hasCalium){
                caliumSum = mailRequest.getItemList().stream()
                        .filter(item -> item.getItem().equals(CommonConstants.CALIUM_ITEM_CODE))
                        .mapToDouble(Item::getItemCnt)
                        .sum();
                double stockCalium = dynamodbCaliumService.getCaliumTotal();
                if(caliumSum > stockCalium){
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_MAIL_ITEM_CALIUM_OVER.toString());
                }
            }

            mailRequest.getItemList().forEach(
                    item -> {
                        map.put("goodsId",item.getItem());
                        map.put("itemCnt", String.valueOf(item.getItemCnt()));
                        mailMapper.insertItem(map);
                    }
            );

            //재원저장소 칼리움 차감
            if(hasCalium){
                dynamodbCaliumService.caliumStockDeduction(caliumSum);
            }
        }


        //메시지 저장
        if(mailRequest.getMailList()!= null && !mailRequest.getMailList().isEmpty()){
            mailRequest.getMailList().forEach(
                    item -> {
                        map.put("title",item.getTitle());
                        map.put("content",item.getContent());
                        map.put("language",item.getLanguage());
                        mailMapper.insertMessage(map);
                    }
            );
        }
        log.info("postMail Insert Mail: {}", mailRequest);

        //로그 기록
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("mail_type",mailRequest.getMailType());
        jsonObject.put("receiver",mailRequest.getTarget());
        Mail.SENDTYPE sendType = mailRequest.getSendType();
        jsonObject.put("send_type",sendType);
        if(sendType.equals(Mail.SENDTYPE.RESERVE_SEND))
            jsonObject.put("send_dt",mailRequest.getSendDt());
        jsonObject.put("mail_list",map);
        historyService.setLog(HISTORYTYPE.MAIL_ADD, jsonObject);

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .message(SuccessCode.SAVE.getMessage())
                        .build())
                .build();
    }
    @Transactional(transactionManager = "transactionManager")
    public MailResponse updateMail(Long id, MailRequest mailRequest) {
        mailRequest.setId(id);
        mailRequest.setUpdateBy(CommonUtils.getAdmin().getId());
        mailRequest.setUpdateDt(LocalDateTime.now());

        if (mailRequest.isReserve()) {
            mailRequest.setSendType(Mail.SENDTYPE.RESERVE_SEND);
        } else {
            mailRequest.setSendType(Mail.SENDTYPE.DIRECT_SEND);
            mailRequest.setSendDt(LocalDateTime.now());
        }

        //단일일경우 guid 를 target 설정, 복수이면은 fileName을 target에 설정
        if (mailRequest.getGuid() != null) {
            String guid = mailRequest.getGuid();
            // 닉네임이면 guid로 바꾼다
            if(mailRequest.getUserType().equals(Mail.USERTYPE.NICKNAME)) guid = dynamoDBService.getNickNameByGuid(guid);
            mailRequest.setReceiveType(Mail.RECEIVETYPE.SINGLE);
            mailRequest.setTarget(guid);
        } else {
            mailRequest.setReceiveType(Mail.RECEIVETYPE.MULTIPLE);

            if (mailRequest.getFileName() == null) {
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.getMessage());
            }
            mailRequest.setTarget(mailRequest.getFileName());
        }

        Long mail_id = mailRequest.getId();
        Mail before_info = mailMapper.getMailDetail(mail_id);
        List<Message> before_msg = mailMapper.getMessage(mail_id);
        List<Item> before_item = mailMapper.getItem(mail_id);

        log.info("updateMail Update Before MailInfo: {}, Message: {}, Item: {}", before_info, before_msg, before_item);

        mailMapper.updateMail(mailRequest);

        // 스케줄에서 제거
        scheduleService.closeTask("mail-" + mail_id.toString());

        Map<String, String> map = new HashMap<>();
        map.put("mailId", String.valueOf(mailRequest.getId()));

        // item 테이블 데이터 삭제 처리 by mail_id
        mailMapper.deleteItem(map);

        // 아이템 업데이트
        if (mailRequest.getItemList() != null && !mailRequest.getItemList().isEmpty()) {
            mailRequest.getItemList().forEach(item -> {
                map.put("goodsId", item.getItem());
                map.put("itemCnt", String.valueOf(item.getItemCnt()));

                mailMapper.insertItem(map);
            });
        }

        // message 테이블 데이터 삭제 처리 by mail_id
        mailMapper.deleteMessage(map);

        // 메일 업데이트
        if (mailRequest.getMailList() != null && !mailRequest.getMailList().isEmpty()) {
            mailRequest.getMailList().forEach(item -> {
                map.put("title", item.getTitle());
                map.put("content", item.getContent());
                map.put("language", item.getLanguage());

                mailMapper.insertMessage(map);
            });
        }
        log.info("updateMail Update After Mail: {}", mailRequest);

        //로그 기록
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("before_info",before_info.toString());
        jsonObject.put("before_msg",before_msg.toString());
        jsonObject.put("before_item",before_item.toString());
        jsonObject.put("mail_type",mailRequest.getMailType());
        jsonObject.put("receiver",mailRequest.getTarget());
        Mail.SENDTYPE sendType = mailRequest.getSendType();
        jsonObject.put("send_type",sendType);
        if(sendType.equals(Mail.SENDTYPE.RESERVE_SEND))
            jsonObject.put("send_dt",mailRequest.getSendDt());
        historyService.setLog(HISTORYTYPE.MAIL_UPDATE, jsonObject);

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .message(SuccessCode.UPDATE.getMessage())
                        .build())
                .build();
    }

    @Transactional(transactionManager = "transactionManager")
    public MailResponse deleteMail(MailRequest mailRequest){
        Map<String,Object> map = new HashMap<>();

        mailRequest.getList().forEach(
                item->{
                    map.put("id",item.getId());
                    mailMapper.deleteMail(map);

                    // 스케줄에서 제거
                    scheduleService.closeTask("mail-" + item.getId());
                    log.info("deleteMail Mail: {}", item);

                    //로그 기록
                    List<Message> message = mailMapper.getMessage(Long.valueOf(item.getId()));
                    map.put("adminId",  CommonUtils.getAdmin().getId());
                    map.put("name",  CommonUtils.getAdmin().getName());
                    map.put("mail",  CommonUtils.getAdmin().getEmail());
                    map.put("type", HISTORYTYPE.MAIL_DELETE);
                    JSONObject jsonObject = new JSONObject();
                    if(!message.isEmpty()){
                        jsonObject.put("message",message.get(0).getTitle());
                    }
                    map.put("content",jsonObject.toString());
                    historyMapper.saveLog(map);
                }
        );

        return MailResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(MailResponse.ResultData.builder()
                        .message(SuccessCode.DELETE.getMessage())
                        .build())
                .build();
    }

    public List<Mail> getScheduleMailList(){
        return mailMapper.getSchesuleMailList();
    }

    public List<Message> getMailMessageList(Long id){
        return mailMapper.getMessage(id);
    }

    public List<Item> getMailItemList(Long id){
        return mailMapper.getItem(id);
    }

    public void updateMailStatus(Long id, Mail.SENDSTATUS status){
        Map<String,Object> map = new HashMap<>();
        map.put("id", id);
        map.put("status", status.toString());
        mailMapper.updateCompleteMail(map);
    }

    //메타 아이템
    public MailResponse getMetaItem(String metaId){
        long id = Long.parseLong(metaId);
        String item = metaDataHandler.getMetaItemNameData((int) id);
        boolean isItem = (item != null && !item.isEmpty());
        if(isItem) {
            Item item_info = new Item();
            item_info.setItem(metaId);
            item_info.setItemName(metaDataHandler.getTextStringData(item));
            return MailResponse.builder()
                    .status(CommonCode.SUCCESS.getHttpStatus())
                    .result(CommonCode.SUCCESS.getResult())
                    .resultData(MailResponse.ResultData.builder()
                            .message(SuccessCode.ITEM_EXIST.getMessage())
                            .itemInfo(item_info)
                            .build())
                    .build();
        }else
            return MailResponse.builder()
                    .status(CommonCode.ERROR.getHttpStatus())
                    .result(CommonCode.ERROR.getResult())
                    .resultData(MailResponse.ResultData.builder()
                            .message(ErrorCode.NOT_ITEM.getMessage())
                            .build())
                    .build();
    }

    public String getGuid(String target, String type){
        if(Mail.USERTYPE.EMAIL.name().equals(type)){
            WalletUser user = walletUserMapper.getUser(target);
            return dynamoDBService.getAccountIdByGuid(user.getAccount_id());
        }else if(Mail.USERTYPE.NICKNAME.name().equals(type)){
            return dynamoDBService.getNickNameByGuid(target);
        }
        return target;
    }

    public void setScheduleLog(HISTORYTYPE type, String message){
        //스케줄 로그 기록
        historyService.setScheduleLog(type, message);
    }
}
