package com.caliverse.admin.domain.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import com.caliverse.admin.domain.RabbitMq.MessageHandlerService;
import com.caliverse.admin.domain.entity.EReqType;
import com.caliverse.admin.domain.entity.FriendRequest;
import com.caliverse.admin.domain.entity.SEARCHTYPE;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.request.MailRequest;
import com.caliverse.admin.dynamodb.domain.atrrib.AccountBaseAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.MailAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.MailJsonAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.MoneyAttrib;
import com.caliverse.admin.dynamodb.domain.doc.MailDoc;
import com.caliverse.admin.dynamodb.dto.PageResult;
import com.caliverse.admin.dynamodb.service.*;
import com.caliverse.admin.global.common.annotation.BusinessProcess;
import com.caliverse.admin.global.common.annotation.RequestLog;
import com.caliverse.admin.global.common.constants.CommonConstants;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.caliverse.admin.domain.datacomponent.MetaDataHandler;
import com.caliverse.admin.domain.request.UsersRequest;
import com.caliverse.admin.domain.response.UsersResponse;
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.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;

import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class UsersService {

    private final DynamodbService dynamodbService;
    private final UserGameSessionService userGameSessionService;
    private final RedisUserInfoService redisUserInfoService;
    private final DynamodbUserService dynamodbUserService;
    private final DynamodbItemService dynamodbItemService;
    private final DynamodbMailService dynamodbMailService;
    private final DynamodbQuestService dynamodbQuestService;
    private final DynamodbMyHomeService dynamodbMyHomeService;
    private final DynamodbFriendService dynamodbFriendService;

    private final MessageHandlerService messageHandlerService;
    private final ReqIdService reqIdService;

    @Autowired
    private MetaDataHandler metaDataHandler;

    // 닉네임 변경
    @BusinessProcess(action = LogAction.NICKNAME_CHANGE)
    @RequestLog
    public UsersResponse changeNickname(UsersRequest usersRequest){
        String guid = usersRequest.getGuid();
        String nickname = usersRequest.getNickname();
        String newNickname = usersRequest.getNewNickname();

        //신규 닉네임 유효성 체크
        ErrorCode check = CommonUtils.isValidNickname(newNickname);
        if(!check.equals(ErrorCode.SUCCESS)){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), check.toString() );
        }

        userGameSessionService.kickUserSession(guid, String.format("%s User Nickname Changes", nickname));
        dynamodbUserService.changesNickname(guid, nickname, newNickname);

        log.info("Nickname Changed complete guid: {}", guid);

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

    }

    // GM 권한 변경
    @BusinessProcess(action = LogAction.ADMIN_LEVEL)
    @RequestLog
    public UsersResponse changeAdminLevel(UsersRequest usersRequest){
        String guid = usersRequest.getGuid();

        dynamodbUserService.updateAdminLevel(guid, usersRequest.getAdminLevel());

        log.info("Admin Level Changed Complete guid: {}", guid);

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

    }

    // 유저 킥
    @BusinessProcess(action = LogAction.KICK_USER)
    @RequestLog
    public UsersResponse userKick(UsersRequest usersRequest){
        String guid = usersRequest.getGuid();
        String adminUser = CommonUtils.getAdmin().getEmail();

        userGameSessionService.kickUserSession(guid, String.format("admin %s kick out", adminUser));

        log.info("User Kicked Complete guid: {}", guid);

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

    }

    // 유정 정보 조회 닉네임,GUID,Account ID
    @RequestLog
    public UsersResponse findUsers(Map requestParam){

        String searchType = requestParam.get("search_type").toString();
        String searchKey = requestParam.get("search_key").toString();

        Map<String,String> resultMap = new HashMap<>();
        if(searchType.equals(SEARCHTYPE.NAME.name())){
            String name_guid = dynamodbUserService.getNameByGuid(searchKey.toLowerCase());
            if(!name_guid.isEmpty()){
                resultMap.put("guid", name_guid); //nickname은 무조건 소문자
                resultMap.put("nickname", searchKey);
            }
        }else if(searchType.equals(SEARCHTYPE.GUID.name())){
            String guid_name = dynamodbUserService.getGuidByName(searchKey);
            if(!guid_name.isEmpty()){
                resultMap.put("guid", searchKey);
                resultMap.put("nickname", guid_name);
            }
        }else if(searchType.equals(SEARCHTYPE.ACCOUNT.name())){
            String account_guid = dynamodbUserService.getAccountIdByGuid(searchKey);
            if(!account_guid.isEmpty()){
                resultMap.put("guid", account_guid);
                resultMap.put("nickname", dynamodbUserService.getAccountIdByName(searchKey));
            }
        }

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

    //유저 기본 정보
    @RequestLog
    public UsersResponse getBasicInfo(String guid){

        String account_id = dynamodbUserService.getGuidByAccountId(guid);

        AccountBaseAttrib accountBaseAttrib = dynamodbUserService.getAccountInfo(account_id);
        UsersResponse.UserInfo userInfo = UsersResponse.UserInfo.builder()
                .aid(accountBaseAttrib.getUserGuid())
                .userId(accountBaseAttrib.getAccountId())
                .nation(accountBaseAttrib.getLanguageType())
                .membership("")
                .friendCode("")
                .createDt(accountBaseAttrib.getCreatedDateTime())
                .accessDt(accountBaseAttrib.getLoginDateTime())
                .endDt(accountBaseAttrib.getLogoutDateTime())
                .walletUrl("")
                .adminLevel(accountBaseAttrib.getAuthAdminLevelType())
                .spareSlot("")
                .build();

        // charInfo
        MoneyAttrib moneyAttrib = dynamodbUserService.getMoneyInfo(guid);
        UsersResponse.CharInfo charInfo = UsersResponse.CharInfo.builder()
                .characterName(dynamodbUserService.getGuidByName(guid))
                .level("")
                .goldCali(moneyAttrib.getGold())
                .redCali(moneyAttrib.getCalium())
                .blackCali(moneyAttrib.getRuby())
                .blueCali(moneyAttrib.getSapphire())
                .build();

        boolean userSession = userGameSessionService.userSession(guid);

        return UsersResponse.builder()
                .resultData(
                    UsersResponse.ResultData.builder()
                            .charInfo(charInfo)
                            .userInfo(userInfo)
                            .userSession(userSession)
                            .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //아바타 정보
    @RequestLog
    public UsersResponse getAvatarInfo(String guid){

        //avatarInfo
        Map<String, Object> charInfo = dynamodbUserService.getAvatarInfo(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                            .avatarInfo((UsersResponse.AvatarInfo) charInfo.get("avatarInfo"))
                            .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //의상 정보
    @RequestLog
    public UsersResponse getClothInfo(String guid){
        Map<String, Object> charInfo = dynamodbItemService.getClothItems(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                        .clothInfo((UsersResponse.ClothInfo) charInfo.get("clothInfo"))
                        .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //도구 정보
    @RequestLog
    public UsersResponse getToolSlotInfo(String guid){
        Map<String, Object> toolSlot = dynamodbItemService.getTools(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                        .slotInfoList((UsersResponse.SlotInfo) toolSlot.get("toolSlotInfo"))
                        .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //인벤토리 정보
    @RequestLog
    public UsersResponse getInventoryInfo(String guid){
        UsersResponse.InventoryInfo inventoryInfo = dynamodbItemService.getInvenItems(guid);
        log.info("getInventoryInfo Inventory Items: {}", inventoryInfo);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                        .inventoryInfo(inventoryInfo)
                        .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //인벤토리 아이템 삭제
    @BusinessProcess(action = LogAction.ITEM)
    @RequestLog
    public UsersResponse deleteInventoryItem(Map<String, String> requestParams){
        String guid = requestParams.get("guid");
        String item_guid = requestParams.get("item_guid");
        int current_cnt = Integer.parseInt(requestParams.get("current_cnt"));
        int update_cnt = Integer.parseInt(requestParams.get("cnt"));

        userGameSessionService.kickUserSession(guid, "Item delete");
        if(update_cnt >= current_cnt){
            dynamodbItemService.deleteItem(guid, item_guid);

            log.info("Item delete complete guid: {}", guid);
            return UsersResponse.builder()
                    .status(CommonCode.SUCCESS.getHttpStatus())
                    .result(CommonCode.SUCCESS.getResult())
                    .resultData(UsersResponse.ResultData.builder()
                            .message(SuccessCode.DELETE.getMessage())
                            .build())
                    .build();
        }else{
            int cnt = current_cnt - update_cnt;
            dynamodbItemService.updateItemStack(guid, item_guid, cnt);

            log.info("Item update complete guid: {}", guid);
            return UsersResponse.builder()
                    .status(CommonCode.SUCCESS.getHttpStatus())
                    .result(CommonCode.SUCCESS.getResult())
                    .resultData(UsersResponse.ResultData.builder()
                            .message(SuccessCode.UPDATE.getMessage())
                            .build())
                    .build();
        }
    }

    //우편 정보
    @RequestLog
    public UsersResponse getMail(UsersRequest usersRequest){
        PageResult<MailDoc> mailPageResult = dynamodbMailService.getMail(usersRequest.getMailType(), usersRequest.getGuid(), "", usersRequest.getPageKey());
        List<UsersResponse.Mail> mailList = new ArrayList<>();

        mailPageResult.getItems().forEach(doc -> {
            try {
                MailAttrib attrib = doc.getAttribValue();
                List<UsersResponse.MailItem> itemList = new ArrayList<>();
                if(attrib == null){
                    MailJsonAttrib mailJsonAttrib = dynamodbMailService.getMailJsonAttrib(doc.getPK(),doc.getSK());
                    mailJsonAttrib.getItemList().forEach(item -> {
                        UsersResponse.MailItem mailItem = new UsersResponse.MailItem();
                        mailItem.setItemId(CommonUtils.objectToString(item.getItemId()));
                        mailItem.setCount(item.getCount());
                        String item_nm = metaDataHandler.getMetaItemNameData(item.getItemId());
                        mailItem.setItemName(metaDataHandler.getTextStringData(item_nm));
                        itemList.add(mailItem);
                    });

                    UsersResponse.Mail mail = UsersResponse.Mail.builder()
                            .mailGuid(mailJsonAttrib.getMailGuid())
                            .title(mailJsonAttrib.getTitle())
                            .content(mailJsonAttrib.getText())
                            .senderNickname(mailJsonAttrib.getSenderNickName())
                            .receiveNickname(mailJsonAttrib.getReceiverNickName())
                            .status(mailJsonAttrib.isRead())
                            .isSystemMail(mailJsonAttrib.isSystemMail())
                            .isGetItem(mailJsonAttrib.isGetItem())
                            .createDt(mailJsonAttrib.getCreateTime())
                            .mailItemList(itemList)
                            .build();
                    mailList.add(mail);
                }else{
                    attrib.getItemList().forEach(item -> {
                        UsersResponse.MailItem mailItem = new UsersResponse.MailItem();
                        mailItem.setItemId(CommonUtils.objectToString(item.getItemId()));
                        mailItem.setCount(item.getCount());
                        String item_nm = metaDataHandler.getMetaItemNameData(item.getItemId());
                        mailItem.setItemName(metaDataHandler.getTextStringData(item_nm));
                        itemList.add(mailItem);
                    });

                    UsersResponse.Mail mail = UsersResponse.Mail.builder()
                            .mailGuid(attrib.getMailGuid())
                            .title(attrib.getTitle())
                            .content(attrib.getText())
                            .senderNickname(attrib.getSenderNickName())
                            .receiveNickname(attrib.getReceiverNickName())
                            .status(Boolean.parseBoolean(attrib.getIsRead()))
                            .isSystemMail(Boolean.parseBoolean(attrib.getIsSystemMail()))
                            .isGetItem(Boolean.parseBoolean(attrib.getIsGetItem()))
                            .createDt(attrib.getCreateTime())
                            .mailItemList(itemList)
                            .build();
                    mailList.add(mail);
                }
            } catch (Exception e) {
                log.error(e.getMessage());
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), e.getMessage());
            }
        });
        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                        .mailList(mailList)
                        .pageKey(mailPageResult.getLastEvaluatedKey() == null ?
                                null :
                                mailPageResult.getLastEvaluatedKey().entrySet().stream()
                                .collect(Collectors.toMap(
                                        Map.Entry::getKey,
                                        entry -> entry.getValue().s()
                                ))
                        )
                        .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //우편 삭제
    @BusinessProcess(action = LogAction.MAIL)
    @RequestLog
    public UsersResponse deleteMail(Map<String, String> requestParams){
        String guid = requestParams.get("guid");
        String mail_guid = requestParams.get("mail_guid");
        String type = requestParams.get("type");

        userGameSessionService.kickUserSession(guid, "delete mail");
        dynamodbMailService.deleteMail(type, guid, mail_guid);

        log.info("Delete mail complete guid: {}", guid);

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

    //우편 아이템 삭제
    @BusinessProcess(action = LogAction.MAIL_ITEM)
    @RequestLog
    public UsersResponse deleteMailItem(MailRequest.DeleteMailItem deleteMailItem){
        userGameSessionService.kickUserSession(deleteMailItem.getGuid(), "delete mail item");
        dynamodbMailService.deleteMailItem(deleteMailItem.getType(), deleteMailItem.getGuid(),
                deleteMailItem.getMailGuid(), deleteMailItem.getItemId(), deleteMailItem.getParrentCount(), deleteMailItem.getCount());

        log.info("Delete mail item complete guid: {}", deleteMailItem.getGuid());

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

    //마이홈 정보
    @RequestLog
    public UsersResponse getMyhome(String guid){
        List<UsersResponse.Myhome> myhome = dynamodbMyHomeService.getMyHome(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                                .myhomeInfo(myhome)
                                .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //친구 목록
    @RequestLog
    public UsersResponse getFriendList(String guid){

        List<UsersResponse.Friend> friendList = dynamodbFriendService.getFriend(guid);
        List<UsersResponse.Friend> friendBlockList = dynamodbFriendService.getBlockUser(guid);
        List<UsersResponse.Friend> friendSendList = new ArrayList<>();
        List<UsersResponse.Friend> friendReceiveList = new ArrayList<>();

        List<FriendRequest> sendRequestList = redisUserInfoService.getFriendRequestInfo("send", guid);
        List<FriendRequest> receiveRequestList = redisUserInfoService.getFriendRequestInfo("receive", guid);
        AtomicInteger idx = new AtomicInteger(1);
        for(FriendRequest friendReq : sendRequestList){
            UsersResponse.Friend friend = new UsersResponse.Friend();
            friend.setRowNum(idx.getAndIncrement());
            String receive_guid = friendReq.getReceiverGuid();
            friend.setFriendGuid(receive_guid);
            friend.setFriendName(dynamodbUserService.getGuidByName(receive_guid));
            friend.setReceiveDt(friendReq.getRequestTime());
            friend.setLanguage(dynamodbUserService.getUserLanguage(receive_guid));

            friendSendList.add(friend);
        }
        idx = new AtomicInteger(1);
        for(FriendRequest friendReq : receiveRequestList){
            UsersResponse.Friend friend = new UsersResponse.Friend();
            friend.setRowNum(idx.getAndIncrement());
            String send_guid = friendReq.getSenderGuid();
            friend.setFriendGuid(send_guid);
            friend.setFriendName(dynamodbUserService.getGuidByName(send_guid));
            friend.setReceiveDt(friendReq.getRequestTime());
            friend.setLanguage(dynamodbUserService.getUserLanguage(send_guid));

            friendReceiveList.add(friend);
        }

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                        .friendList(friendList)
                        .friendSendList(friendSendList)
                        .friendReceiveList(friendReceiveList)
                        .friendBlockList(friendBlockList)
                        .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();
    }

    //타투 정보
    @RequestLog
    public UsersResponse getTattoo(String guid){
        List<UsersResponse.Tattoo> resTatto = dynamodbItemService.getTattoo(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                                .tattooList(resTatto)
                                .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //퀘스트 정보
    @RequestLog
    public UsersResponse getQuest(String guid){
        List<UsersResponse.QuestInfo> questList = dynamodbQuestService.getQuestItems(guid);

        return UsersResponse.builder()
                .resultData(
                        UsersResponse.ResultData.builder()
                                .questList(questList)
                                .build()
                )
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    @BusinessProcess(action = LogAction.QUEST_TASK)
    @RequestLog
    public UsersResponse CompleteQuestTask(UsersRequest usersRequest){
        String serverName = redisUserInfoService.getFirstChannel();
        if(serverName.isEmpty()){
            log.error("CompleteQuestTask serverList is empty");
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_FOUND_SERVER.getMessage());
        }
        String guid = usersRequest.getGuid();

        userGameSessionService.kickUserSession(guid, "Force complete quest task");

        String accountId = dynamodbUserService.getGuidByAccountId(guid);

        try{
            messageHandlerService.sendQuestTaskCompleteMessage(
                    serverName,
                    accountId,
                    reqIdService.generateNextReqId(
                            EReqType.QUEST_TASK,
                            CommonUtils.getAdmin() == null ? CommonConstants.SYSTEM : CommonUtils.getAdmin().getId().toString(),
                            "퀘스트 TASK 강제 완료 요청"
                            ),
                    usersRequest.getQuestKey(),
                    usersRequest.getTaskNo()
            );
        } catch (Exception e) {
            log.error("CompleteQuestTask messageHandlerService error: {}", e.getMessage(), e);
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.MESSAGE_SEND_FAIL.getMessage());
        }

        log.info("CompleteQuestTask Quest Task forced completion guid: {}, quest: {}", guid, usersRequest.getQuestKey());

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

}
