package com.caliverse.admin.domain.service;

import com.caliverse.admin.domain.RabbitMq.MessageHandlerService;
import com.caliverse.admin.domain.dao.admin.MenuMapper;
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.request.MenuRequest;
import com.caliverse.admin.domain.response.MenuResponse;
import com.caliverse.admin.dynamodb.service.DynamodbMenuService;
import com.caliverse.admin.global.common.annotation.BusinessProcess;
import com.caliverse.admin.global.common.annotation.RequestLog;
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.constants.MysqlConstants;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.FileUtils;
import com.caliverse.admin.global.component.manager.BusinessProcessIdManager;
import com.caliverse.admin.mongodb.service.MysqlHistoryLogService;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.services.s3.model.S3Exception;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class MenuService {

    @Value("${excel.file-path}")
    private String filePath;
    @Value("${caliverse.file}")
    private String samplePath;
    private final FileUtils fileUtils;
    private final MenuMapper menuMapper;
    private final ResourceLoader resourceLoader;
    private final MysqlHistoryLogService mysqlHistoryLogService;
    private final S3Service s3Service;
    private final DynamodbMenuService dynamodbMenuService;
    private final MessageHandlerService messageHandlerService;
    private final RedisUserInfoService redisUserInfoService;
    private final BusinessProcessIdManager processIdManager;

    @RequestLog
    public MenuResponse getList(Map requestParam){

        //페이징 처리
        requestParam = CommonUtils.pageSetting(requestParam);

        List<MenuBanner> list = menuMapper.getBannerList(requestParam);

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

    @RequestLog
    public MenuResponse getdetail(Long id){
        MenuBanner banner = menuMapper.getBannerDetail(id);

        banner.setImageList(menuMapper.getMessage(id));

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

    public MenuResponse imageUpload(MultipartFile file){
        if (file.isEmpty()) {
            log.error("File is empty");
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_FILE.toString());
        }

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

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

    public MenuResponse imageDelete(String fileName){
        if (fileName.isEmpty()) {
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_FILE.getMessage() );
        }

        // 서버 파일 삭제
        boolean is_delete = fileUtils.deleteFile(fileName);

        if(is_delete){
            return MenuResponse.builder()
                    .resultData(MenuResponse.ResultData.builder()
                            .message(SuccessCode.FILE_DELETE.getMessage())
                            .build())
                    .status(CommonCode.SUCCESS.getHttpStatus())
                    .result(CommonCode.SUCCESS.getResult())
                    .build();
        }else{
            return MenuResponse.builder()
                    .resultData(MenuResponse.ResultData.builder()
                            .message(ErrorCode.ERROR_FILE_DELETE.getMessage())
                            .build())
                    .status(CommonCode.ERROR.getHttpStatus())
                    .result(CommonCode.ERROR.getResult())
                    .build();
        }
    }

    public Resource fileDown(String fileName) {
        Path fileFullPath = Path.of(filePath+fileName);
        try {
            if(fileName.contains("sample")){
                return resourceLoader.getResource(samplePath + fileName);
            }
            Resource resource = new UrlResource(fileFullPath.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());
        }
    }

    @BusinessProcess(action = LogAction.BANNER)
    @Transactional(transactionManager = "transactionManager")
    @RequestLog
    public MenuResponse postBanner(MenuRequest menuRequest){
        menuRequest.setCreateBy(CommonUtils.getAdmin().getId());

        int maxOrderId = menuMapper.getMaxOrderId();
        menuRequest.setOrderId(maxOrderId+1);

        menuMapper.insertBanner(menuRequest);

        long banner_id = menuRequest.getId();
        HashMap<String,String> map = new HashMap<>();
        map.put("id",String.valueOf(banner_id));

        //메시지 저장(title=이미지명, content=이미지 링크)
        if(menuRequest.getImageList()!= null && !menuRequest.getImageList().isEmpty()){
            menuRequest.getImageList().forEach(image -> {
                String temp_file = image.getContent();
                String contentType = fileUtils.getContentType(temp_file);
                File file = fileUtils.loadFileObject(temp_file);
                String fileUri = "";
                try{
                    String directory = String.format("%s/%s-%d", CommonConstants.S3_MENU_BANNER_DIRECTORY, CommonConstants.S3_MENU_BANNER_DIRECTORY, banner_id);
                    fileUri = s3Service.uploadFile(file, directory, contentType);
                }catch (S3Exception e) {
                    log.error("S3 오류: {}", e.getMessage());
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_FILE_S3_UPLOAD.getMessage());

                } catch (IOException e) {
                    log.error("파일 읽기 오류: {}", e.getMessage());
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_FILE_S3_UPLOAD.getMessage());

                } catch (Exception e) {
                    log.error("파일 업로드 중 예상치 못한 오류: {}", e.getMessage(), e);
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_FILE_S3_UPLOAD.getMessage());
                }

                if(fileUri.isEmpty()){
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_FILE_S3_UPLOAD.getMessage());
                }

                map.put("title", fileUri);
                map.put("language", image.getLanguage());
                if(menuRequest.isLink() && (menuRequest.getLinkList() != null && !menuRequest.getLinkList().isEmpty())){
                    String link = menuRequest.getLinkList().stream().filter(data -> data.getLanguage().equals(image.getLanguage())).findFirst().get().getContent();
                    map.put("content", link);
                }else{
                    map.put("content", "");
                }
                menuMapper.insertMessage(map);

                fileUtils.deleteFile(temp_file);
            });
        }

        log.info("postBanner Insert MenuBanner id: {}", menuRequest.getId());

        MenuBanner banner = menuMapper.getBannerDetail(menuRequest.getId());
        banner.setImageList(menuMapper.getMessage(menuRequest.getId()));

        String prodId = processIdManager.getCurrentProcessId();
        LogAction action = processIdManager.getCurrentAction();

        //message 정보까지 저장해야해서 수동 로그
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                try {
                    mysqlHistoryLogService.insertHistoryLog(
                            prodId,
                            action,
                            LogStatus.SUCCESS,
                            MysqlConstants.TABLE_NAME_MENU_BANNER,
                            "",
                            banner
                    );
                } catch (Exception e) {
                    log.warn("Failed to log banner creation: {}", e.getMessage());
                }
            }
        });

        if(redisUserInfoService.getAllServerList().isEmpty()){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_FOUND_SERVER.getMessage());
        }

        dynamodbMenuService.insertBanner(banner);

        //운영DB 데이터 추가됐다고 게임서버 알림
        notifyGameServers("postBanner", null);

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

    @BusinessProcess(action = LogAction.BANNER)
    @Transactional(transactionManager = "transactionManager")
    @RequestLog
    public MenuResponse updateBanner(Long id, MenuRequest menuRequest) {
        menuRequest.setId(id);
        menuRequest.setUpdateBy(CommonUtils.getAdmin().getId());
        menuRequest.setUpdateDt(LocalDateTime.now());

        Long banner_id = menuRequest.getId();
        MenuBanner before_info = menuMapper.getBannerDetail(banner_id);
        List<Message> before_msg = menuMapper.getMessage(banner_id);
        before_info.setImageList(before_msg);

        menuMapper.updateBanner(menuRequest);

        log.info("updateBanner Update Banner Complete: {}", menuRequest.getId());

        MenuBanner after_info = menuMapper.getBannerDetail(banner_id);
        after_info.setImageList(menuMapper.getMessage(banner_id));

        if(redisUserInfoService.getAllServerList().isEmpty()){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_FOUND_SERVER.getMessage());
        }

        dynamodbMenuService.updateBanner(after_info);

        //운영DB 데이터 추가됐다고 게임서버 알림
        notifyGameServers("updateBanner", null);

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

    @BusinessProcess(action = LogAction.BANNER)
    @Transactional(transactionManager = "transactionManager")
    @RequestLog
    public MenuResponse deleteBanner(Long id){
        Map<String,Object> map = new HashMap<>();
        map.put("id",id);

        MenuBanner banner = menuMapper.getBannerDetail(id);
        banner.setImageList(menuMapper.getMessage(id));

        if(banner.getStartDt().isBefore(LocalDateTime.now())){
            return MenuResponse.builder()
                    .status(CommonCode.ERROR.getHttpStatus())
                    .result(CommonCode.ERROR.getResult())
                    .resultData(MenuResponse.ResultData.builder()
                            .message(ErrorCode.START_DATE_OVER.getMessage())
                            .build())
                    .build();
        }

        menuMapper.deleteBanner(map);
        menuMapper.deleteMessage(map);

        log.info("deleteBanner Delete Banner Complete id: {}", id);

        if(redisUserInfoService.getAllServerList().isEmpty()){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_FOUND_SERVER.getMessage());
        }

        dynamodbMenuService.deleteBanner(banner.getId().intValue());

        //게임서버 알림
        notifyGameServers("deleteBanner", null);

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

    public List<Message> getMessageList(Long id){
        return menuMapper.getMessage(id);
    }

    private void notifyGameServers(String methodName, Runnable rollbackFunction) {
        // 운영DB 데이터 추가됐다고 게임서버 알림
        List<String> serverList = redisUserInfoService.getAllServerList();
        if(serverList.isEmpty()){
            log.error("{} serverList is empty", methodName);
            if (rollbackFunction != null) {
                rollbackFunction.run();
            }
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_FOUND_SERVER.getMessage());
        }

        try{
            serverList.forEach(messageHandlerService::sendBannerMessage);
        } catch (Exception e) {
            log.error("{} messageHandlerService error: {}", methodName, e.getMessage(), e);
            if (rollbackFunction != null) {
                rollbackFunction.run();
            }
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.MESSAGE_SEND_FAIL.getMessage());
        }
    }


}
