package com.ff.game.api.xk.service.impl;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson2.JSON;
import com.ff.base.constant.CacheConstants;
import com.ff.base.constant.Constants;
import com.ff.base.core.redis.RedisCache;
import com.ff.base.enums.GamePlatforms;
import com.ff.base.enums.GameStatus;
import com.ff.base.enums.XKGameType;
import com.ff.base.exception.base.BaseException;
import com.ff.base.system.service.ISysConfigService;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.JsonUtil;
import com.ff.base.utils.MessageUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.http.HttpClientSslUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.common.domain.TenantSecretKey;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.*;
import com.ff.game.api.xk.client.XKClient;
import com.ff.game.api.xk.dto.*;
import com.ff.game.domain.*;
import com.ff.game.service.*;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * XK 游戏 impl
 *
 * @author shi
 * @date 2024/11/12
 */
@Service("XKService")
@Slf4j
public class GamesXKServiceImpl implements IGamesService {


    @Resource
    private ISysConfigService configService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private IGameExchangeMoneyService gameExchangeMoneyService;


    @Resource
    private IGamePlatformService gamePlatformService;


    @Resource
    private IGameService gameService;


    @Resource
    private IMemberService memberService;

    @Resource
    private IGameFreeRecordService gameFreeRecordService;
    @Resource
    private IGameSecretKeyService gameSecretKeyService;

    @Resource
    private XKClient xkClient;


    @Resource
    private KeyConfig keyConfig;

    @Resource
    private IGameBettingDetailsService gameBettingDetailsService;


    /**
     * 获得就是成功
     *
     * @param errorCode 错误代码
     * @return {@link Boolean }
     */
    private Boolean getIsSuccess(Integer errorCode) {
        return 0 == errorCode;
    }


    /**
     * 获取密钥
     *
     * @param gamesBaseRequestDTO 游戏请求dto
     * @return {@link String }
     */
    @Override
    public String getKey(GamesBaseRequestDTO gamesBaseRequestDTO) {
        Random random = new Random();
        //取出对应的key跟密钥跟请求参数
        String agentKey = gamesBaseRequestDTO.getAgentKey();
        String agentId = gamesBaseRequestDTO.getAgentId();
        String query = gamesBaseRequestDTO.getQuery();

        String now = DateUtils.getFormattedDate();
        String keyG = Md5Utils.md5New(now + agentId + agentKey);

        String md5string = Md5Utils.md5New(query + keyG);
        Integer randomText1 = 100000 + random.nextInt(900000);
        Integer randomText2 = 100000 + random.nextInt(900000);
        String key = randomText1 + md5string + randomText2;
        return key;
    }

    /**
     * 创建成员
     *
     * @param createMemberRequestDTO 创建成员请求dto
     * @return {@link Boolean }
     */
    @Override
    public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
        log.info("GamesXKServiceImpl [createMember] 请求参数 {}", createMemberRequestDTO);

        Map<String, Object> params = new LinkedHashMap<>();
        params.put("account", createMemberRequestDTO.getAccount());
        params.put("agentId", createMemberRequestDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        createMemberRequestDTO.setQuery(query);
        String key = this.getKey(createMemberRequestDTO);
        params.put("key", key);
        XKCreateMemberResponseDTO xkCreateMemberResponseDTO = xkClient.createMember(params);
        Boolean isSuccess = this.getIsSuccess(xkCreateMemberResponseDTO.getCode());
        if (!isSuccess) {
            throw new BaseException(xkCreateMemberResponseDTO.getMsg());
        }
        //判断是否获取成功
        return isSuccess;
    }


    /**
     * 获取会员信息
     *
     * @param memberInfoRequestDTO 会员信息请求dto
     * @return {@link MemberInfoResponseDTO }
     */
    @Override
    public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO) {
        log.info("GamesXKServiceImpl [getMemberInfo] 请求参数 {}", memberInfoRequestDTO);
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("accounts", memberInfoRequestDTO.getAccounts());
        params.put("agentId", memberInfoRequestDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        memberInfoRequestDTO.setQuery(query);
        String key = this.getKey(memberInfoRequestDTO);
        params.put("key", key);
        XKMemberInfoDTO xkMemberInfoDTO = xkClient.getMemberInfo(params);
        //判断是否获取成功
        if (this.getIsSuccess(xkMemberInfoDTO.getCode())) {
            List<MemberInfoResponseDTO> memberInfoResponseDTOS = new ArrayList<>();
            xkMemberInfoDTO.getData().forEach(e -> {
                memberInfoResponseDTOS.add(MemberInfoResponseDTO.builder()
                        .status(e.getStatus())
                        .balance(e.getBalance())
                        .account(e.getAccount())
                        .build());
            });
            return memberInfoResponseDTOS.get(0);
        } else {
            throw new BaseException(xkMemberInfoDTO.getMsg());
        }
    }

    /**
     * 无重定向登录
     *
     * @param gamesLogin 游戏登录
     * @return {@link String }
     */
    @Override
    public String loginWithoutRedirect(GamesLogin gamesLogin) {
        log.info("GamesXKServiceImpl [loginWithoutRedirect] 请求参数 {}", gamesLogin);

        Map<String, Object> params = new LinkedHashMap<>();
        params.put("account", gamesLogin.getAccount());
        params.put("gameId", gamesLogin.getGameId());
        params.put("lang", gamesLogin.getLang());
        params.put("agentId", gamesLogin.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        gamesLogin.setQuery(query);
        String key = this.getKey(gamesLogin);
        params.put("key", key);
        params.put("disableFullScreen", gamesLogin.getDisableFullScreen());
        params.put("homeUrl", gamesLogin.getHomeUrl());
        params.put("platform", gamesLogin.getPlatform());
        XKLoginWithoutRedirectResponseDTO xkLoginWithoutRedirectResponseDTO = xkClient.loginWithoutRedirect(params);
        //判断是否获取成功
        if (this.getIsSuccess(xkLoginWithoutRedirectResponseDTO.getCode())) {
            return xkLoginWithoutRedirectResponseDTO.getData();
        } else {
            throw new BaseException(xkLoginWithoutRedirectResponseDTO.getMsg());
        }
    }


    /**
     * 获取游戏列表
     *
     * @param gamesBaseRequestDTO 游戏请求dto
     * @return {@link String }
     */
    @Transactional
    @Override
    public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {

        List<XKGamesDTO.DataBean> gamesDatas = redisCache.getCacheList(CacheConstants.XK_GAMES);
        if (!CollectionUtils.isEmpty(gamesDatas)) {
            return CacheConstants.XK_GAMES;
        }


        Map<String, Object> params = new LinkedHashMap<>();
        params.put("agentId", gamesBaseRequestDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        gamesBaseRequestDTO.setQuery(query);
        String key = this.getKey(gamesBaseRequestDTO);
        params.put("key", key);

        XKGamesDTO xkGamesDTO = xkClient.getGameList(params);
        //判断是否获取成功
        if (this.getIsSuccess(xkGamesDTO.getCode())) {

            for (XKGamesDTO.DataBean gamesDataDTO : xkGamesDTO.getData()) {
                GamePlatform gamePlatform = GamePlatform.builder()
                        .platformType(XKGameType.findSystemByCode(gamesDataDTO.getGameCategoryId()))
                        .platformCode(GamePlatforms.XK.getCode())
                        .build();
                List<GamePlatform> gamePlatforms = gamePlatformService.selectGamePlatformList(gamePlatform);
                //没有此平台就新增一个平台
                if (CollectionUtils.isEmpty(gamePlatforms)) {
                    gamePlatform.setPlatformName(GamePlatforms.XK.getInfo() + XKGameType.findInfoByCode(gamesDataDTO.getGameCategoryId()));
                    gamePlatform.setSortNo(gamePlatformService.selectMaxSortNo() + 1);
                    gamePlatform.setCreateBy(Constants.SYSTEM);
                    gamePlatformService.insertGamePlatform(gamePlatform);
                } else {
                    gamePlatform = gamePlatforms.get(0);
                }
                Game game = Game.builder()
                        .platformId(gamePlatform.getId())
                        .gameCode(gamesDataDTO.getGameId())
                        .build();
                List<Game> games = gameService.selectGameList(game);
                //不存在这个游戏
                if (CollectionUtils.isEmpty(games)) {
                    game.setGameSourceType(gamesDataDTO.getGameCategoryId());
                    game.setFreespin(gamesDataDTO.isFreeSpin());
                    game.setSortNo(gameService.selectMaxSortNoByPlatformId(gamePlatform.getId()) + 1);
                    game.setGameName(gamesDataDTO.getName());
                    game.setCreateBy(Constants.SYSTEM);
                    gameService.insertGame(game);
                } else {
                    game = games.get(0);
                }
                gamesDataDTO.setSystemGameId(game.getId());


            }

            redisCache.deleteObject(CacheConstants.XK_GAMES);
            redisCache.setCacheList(CacheConstants.XK_GAMES, xkGamesDTO.getData());
            redisCache.expire(CacheConstants.XK_GAMES, 5L, TimeUnit.HOURS);
        } else {
            throw new BaseException(xkGamesDTO.getMsg());

        }

        return CacheConstants.XK_GAMES;
    }

    /**
     * 按代理id进行交换转账
     *
     * @param exchangeTransferMoneyRequestDTO 外汇转账moeny dto
     * @return {@link Long }
     */
    @Override
    @Transactional
    public String exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) {
        log.info("GamesXKServiceImpl [exchangeTransferByAgentId] 请求参数 {}", exchangeTransferMoneyRequestDTO);
        String systemByCode = gameSecretKeyService.findSystemByCode(exchangeTransferMoneyRequestDTO.getAgentId(), GamePlatforms.XK.getInfo());
        Member member = memberService.selectMemberByGameAccount(exchangeTransferMoneyRequestDTO.getAccount());
        String transactionId = GamePlatforms.XK.getCode() + IdUtils.simpleUUID();
        TenantSecretKey tenantSecretKey = keyConfig.get();
        List<GameExchangeMoney> gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList(
                GameExchangeMoney.builder()
                        .tenantKey(tenantSecretKey.getTenantKey())
                        .orderId(exchangeTransferMoneyRequestDTO.getOrderId())
                        .build()
        );
        Assert.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), "订单号重复");
        //获取下一个自增id
        GameExchangeMoney exchangeMoney = GameExchangeMoney
                .builder()
                .tenantKey(tenantSecretKey.getTenantKey())
                .quota(exchangeTransferMoneyRequestDTO.getQuota())
                .balance(exchangeTransferMoneyRequestDTO.getAmount())
                .exchangeType(exchangeTransferMoneyRequestDTO.getTransferType())
                .currencyCode(systemByCode)
                .memberId(member.getId())
                .transactionId(transactionId)
                .orderId(exchangeTransferMoneyRequestDTO.getOrderId())
                .platformCode(GamePlatforms.XK.getCode())
                .build();
        exchangeMoney.setCreateBy(Constants.SYSTEM);
        //接口限制限制50字符
        exchangeMoney.setTransactionId(GamePlatforms.XK.getCode() + IdUtils.simpleUUID());
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("account", exchangeTransferMoneyRequestDTO.getAccount());
        params.put("transactionId", exchangeMoney.getTransactionId());
        params.put("amount", exchangeTransferMoneyRequestDTO.getAmount().stripTrailingZeros().toString());
        params.put("transferType", exchangeTransferMoneyRequestDTO.getTransferType());
        params.put("agentId", exchangeTransferMoneyRequestDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        exchangeTransferMoneyRequestDTO.setQuery(query);
        String key = this.getKey(exchangeTransferMoneyRequestDTO);
        params.put("key", key);
        XKExchangeMoneyResponseDTO exchangeMoneyResponse = xkClient.exchangeTransferByAgentId(params);
        //判断是否转移成功
        if (this.getIsSuccess(exchangeMoneyResponse.getCode())) {
            XKExchangeMoneyResponseDTO.DataBean exchangeMoneyResponseData = exchangeMoneyResponse.getData();
            //更新数据
            exchangeMoney.setBalance(NumberUtil.sub(exchangeMoneyResponseData.getCurrencyAfter(), exchangeMoneyResponseData.getCurrencyBefore()).abs());
            exchangeMoney.setCoinBefore(exchangeMoneyResponseData.getCoinBefore());
            exchangeMoney.setCoinAfter(exchangeMoneyResponseData.getCoinAfter());
            exchangeMoney.setCurrencyBefore(exchangeMoneyResponseData.getCurrencyBefore());
            exchangeMoney.setCurrencyAfter(exchangeMoneyResponseData.getCurrencyAfter());
            exchangeMoney.setStatus(exchangeMoneyResponseData.getStatus());
            gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney);
        } else {
            log.error("GamesXKServiceImpl [exchangeTransferByAgentId] 金额转移失败,错误代码{},错误信息{}", exchangeMoneyResponse.getCode(), exchangeMoneyResponse.getMsg());
            throw new BaseException(MessageUtils.message("game.account.balance.transfer.failed"));
        }

        return transactionId;
    }


    /**
     * 按时间获取投注记录
     *
     * @param betRecordByTimeDTO 按时间dto投注记录
     * @return {@link List }<{@link GameBettingDetails }>
     */
    @Override
    public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
        List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
        //请求参数
        log.info("GamesXKServiceImpl [getBetRecordByTime] 请求参数 {}", betRecordByTimeDTO);
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("startTime", betRecordByTimeDTO.getStartTime());
        params.put("endTime", betRecordByTimeDTO.getEndTime());
        params.put("page", betRecordByTimeDTO.getPage());
        params.put("pageLimit", betRecordByTimeDTO.getPageLimit());
        params.put("agentId", betRecordByTimeDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        betRecordByTimeDTO.setQuery(query);
        String key = this.getKey(betRecordByTimeDTO);
        params.put("key", key);
        XKBetRecordResponseDTO xkBetRecordResponseDTO = xkClient.getBetRecordByTime(params);

        //判断是否获取成功
        if (this.getIsSuccess(xkBetRecordResponseDTO.getCode())) {
            //数据组装
            XKBetRecordResponseDTO.DataBean dataBean = xkBetRecordResponseDTO.getData();
            this.batchInsert(xkBetRecordResponseDTO);

            //获取下一页数据
            while (!Objects.equals(dataBean.getCurrentPage(), dataBean.getTotalPages()) && dataBean.getTotalPages() > 0) {
                betRecordByTimeDTO.setPage(dataBean.getCurrentPage() + 1);
                //请求参数
                params = new LinkedHashMap<>();
                params.put("startTime", betRecordByTimeDTO.getStartTime());
                params.put("endTime", betRecordByTimeDTO.getEndTime());
                params.put("page", betRecordByTimeDTO.getPage());
                params.put("pageLimit", betRecordByTimeDTO.getPageLimit());
                params.put("agentId", betRecordByTimeDTO.getAgentId());
                query = JsonUtil.mapToQueryString(params);
                betRecordByTimeDTO.setQuery(query);
                key = this.getKey(betRecordByTimeDTO);
                params.put("key", key);
                xkBetRecordResponseDTO = xkClient.getBetRecordByTime(params);
                this.batchInsert(xkBetRecordResponseDTO);
            }


            return Boolean.TRUE;
        } else {
            log.error("GamesXKServiceImpl [getBetRecordByTime] 获取投注记录失败,错误代码{},错误信息{}", xkBetRecordResponseDTO.getCode(), xkBetRecordResponseDTO.getMsg());
            throw new BaseException(xkBetRecordResponseDTO.getMsg());
        }

    }

    /**
     * 赠送免费局数
     *
     * @param createFreeSpinRequest 创建自由旋转请求
     * @return {@link Boolean }
     */
    @Override
    public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) {
        return Boolean.FALSE;
    }

    /**
     * 获取游戏详细信息
     *
     * @param getGameDetailRequestDTO 获取游戏详细信息请求dto
     * @return {@link GetGameDetailResponseDTO }
     */
    @Override
    public GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO) {
        return new GetGameDetailResponseDTO();
    }

    /**
     * 强制会员从游戏注销
     *
     * @param kickMemberRequestDTO 踢会员请求dto
     * @return {@link Boolean }
     */
    @Override
    public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) {
        log.info("GamesXKServiceImpl [kickMember] 请求参数 {}", kickMemberRequestDTO);
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("account", kickMemberRequestDTO.getAccount());
        params.put("agentId", kickMemberRequestDTO.getAgentId());
        String query = JsonUtil.mapToQueryString(params);
        kickMemberRequestDTO.setQuery(query);
        String key = this.getKey(kickMemberRequestDTO);
        params.put("key", key);
        XKKickMemberDTO xkKickMemberDTO = xkClient.kickMember(params);
        //判断是否获取成功
        if (this.getIsSuccess(xkKickMemberDTO.getCode())) {
            return Boolean.TRUE;
        } else {
            throw new BaseException(xkKickMemberDTO.getMsg());
        }
    }

    /**
     * 踢成员全部
     *
     * @param kickMemberAllDTO 踢成员全部dto
     * @return {@link Boolean }
     */
    @Override
    public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
        throw new BaseException("当前平台不支持全部踢出");
    }

    /**
     * 免费游戏玩家使用的纪录
     *
     * @param getFreeSpinDashflowRequestDTO 获取自由旋转dashflow请求dto
     * @return {@link List }<{@link GameFreeRecord }>
     */
    @Override
    public List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) {
        return new ArrayList<>();
    }

    /**
     * 取消赠送免费局数
     *
     * @param cancelFreeSpinRequestDTO 取消免费旋转请求
     * @return {@link Boolean }
     */
    @Override
    public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) {
        return Boolean.TRUE;
    }


    /**
     * 批量插入
     *
     * @param xkBetRecordResponseDTO xk下注记录响应dto
     */
    private void batchInsert(XKBetRecordResponseDTO xkBetRecordResponseDTO) {
        List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
        List<Long> wagersIds = new ArrayList<>();
        //数据组装
        XKBetRecordResponseDTO.DataBean dataBean = xkBetRecordResponseDTO.getData();
        //数据转化
        for (XKBetRecordResponseDTO.DataBean.ResultBean bean : dataBean.getResult()) {
            GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder().data(bean).build());
            if (!ObjectUtils.isEmpty(bettingDetails)) {
                gameBettingDetails.add(bettingDetails);
            }
            wagersIds.add(bean.getWagersId());
        }
        if (!CollectionUtils.isEmpty(gameBettingDetails)) {
            //查询重复数据id
            List<Long> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds);
            //用steam流清除list中与wagersIds集合相同的数据
            gameBettingDetails = gameBettingDetails.stream()
                    .filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(gameBettingDetails)) {
                gameBettingDetailsService.batchInsert(gameBettingDetails);
            }
        }

    }

    /**
     * 数据构建
     *
     * @param gamesDataBuildDTO 数据
     * @return {@link GameBettingDetails }
     */
    @Override
    public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
        //转化类
        XKBetRecordResponseDTO.DataBean.ResultBean resultBean = (XKBetRecordResponseDTO.DataBean.ResultBean) gamesDataBuildDTO.getData();
        String systemByCode = gameSecretKeyService.findSystemByCode(resultBean.getAgentId(), GamePlatforms.XK.getInfo());
        Member member = memberService.selectMemberByGameAccount(resultBean.getAccount());
        List<XKGamesDTO.DataBean> gamesDatas = redisCache.getCacheList(CacheConstants.XK_GAMES);
        Map<Integer, XKGamesDTO.DataBean> dataDTOMap = gamesDatas.stream().collect(Collectors.toMap(XKGamesDTO.DataBean::getGameId, e -> e));
        XKGamesDTO.DataBean gamesDataDTO = dataDTOMap.get(resultBean.getGameId());
        BigDecimal payoffAmount = BigDecimal.ZERO;

        if (GameStatus.WIN.getCode().equals(resultBean.getStatus())) {
            payoffAmount = NumberUtil.sub(resultBean.getPayoffAmount(), resultBean.getTurnover());
        } else if (GameStatus.FAIL.getCode().equals(resultBean.getStatus())) {
            payoffAmount = NumberUtil.sub(resultBean.getPayoffAmount(), resultBean.getTurnover()).negate();
        }
        //数据构造
        GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
                //保存我们的币种id
                .currencyCode(systemByCode)
                .memberId(member.getId())
                .gameCode(resultBean.getGameId())
                .gameType(XKGameType.findSystemByCode(resultBean.getGameCategoryId()))
                .platformCode(GamePlatforms.XK.getCode())
                .gameId(gamesDataDTO.getSystemGameId())
                .gameName(gamesDataDTO.getName())
                .gameStatus(resultBean.getStatus())
                .gameStatusType(resultBean.getType())
                .gameCurrencyCode(resultBean.getAgentId())
                .account(String.valueOf(resultBean.getAccount()))
                .wagersId(resultBean.getWagersId())
                .wagersTime(resultBean.getWagersTime())
                .betAmount(resultBean.getBetAmount().abs())
                .payoffTime(resultBean.getPayoffTime())
                .payoffAmount(payoffAmount)
                .settlementTime(resultBean.getSettlementTime())
                .turnover(resultBean.getTurnover())
                .orderNo(String.valueOf(resultBean.getRoundIndex()))
                .build();
        gameBettingDetails.setCreateBy(Constants.SYSTEM);
        gameBettingDetails.setCreateTime(DateUtils.getNowDate());
        return gameBettingDetails;
    }
}
