package com.webchat.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.webchat.common.bean.APIPageResponseBean;
import com.webchat.common.enums.CommonStatusEnum;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.util.IDGenerateUtil;
import com.webchat.common.util.JsonUtil;
import com.webchat.common.util.ListUtil;
import com.webchat.domain.vo.request.SlideVerificationVO;
import com.webchat.repository.dao.ISlideVerificationEntityDAO;
import com.webchat.repository.entity.SlideVerificationEntity;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @Author 程序员七七
 * @webSite https://www.coderutil.com
 * @Date 2024/9/18 23:31
 * @description
 */
@Service
public class SlideVerificationService {


    @Autowired
    private ISlideVerificationEntityDAO slideVerificationEntityDAO;

    @Autowired
    private RedisService redisService;

    private static final int OFFSET = 5;

    private static final int[] X_ARR = new int[] {100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280};
    private static final int[] Y_ARR = new int[] {40, 50, 60, 70, 80, 90, 100, 110, 120};


    public Long save(SlideVerificationVO slideVerificationVO) {
        SlideVerificationEntity slideVerificationEntity = convert(slideVerificationVO);
        slideVerificationEntity = slideVerificationEntityDAO.save(slideVerificationEntity);
        return slideVerificationEntity.getId();
    }

    public void updateStatus(Long id, Integer status) {
        SlideVerificationEntity slideVerificationEntity= slideVerificationEntityDAO.findById(id).orElse(null);
        Assert.isTrue(slideVerificationEntity != null , "滑块验证信息编辑失败：未查找到！");
        slideVerificationEntity.setStatus(status);
        slideVerificationEntityDAO.save(slideVerificationEntity);
        /**
         * 刷新缓存
         */
        this.refreshSlideVerificationCache();
    }

    public SlideVerificationVO get() {
        String key = RedisKeyEnum.SLIDE_VERIFICATION_CACHE.getKey();
        if (!redisService.exists(key)) {
            this.refreshSlideVerificationCache();
        }
        String cache = redisService.get(key);
        if (StringUtils.isBlank(cache)) {
            return null;
        }
        List<String> cacheList = JsonUtil.fromJson(cache, new TypeReference<List<String>>() { });
        ListUtil.shuffle(cacheList);
        String image = cacheList.get(0);
        SlideVerificationVO slideVerificationVO = new SlideVerificationVO();
        slideVerificationVO.setImage(image);
        slideVerificationVO.setId(IDGenerateUtil.createId(""));
        slideVerificationVO.setX(X_ARR[new Random().nextInt(X_ARR.length)]);
        slideVerificationVO.setY(Y_ARR[new Random().nextInt(Y_ARR.length)]);

        /**
         * 缓存token
         */
        this.cacheToken(slideVerificationVO);

        return slideVerificationVO;
    }

    public boolean validate(String accessToken) {
        String id = accessToken.substring(0, 32);
        int x = Integer.parseInt(accessToken.substring(32, accessToken.length()));
        SlideVerificationVO slideVerification = getToken(id);
        Assert.isTrue(slideVerification != null, "滑块验证失败");
        Assert.isTrue(Math.abs(x - slideVerification.getX()) < OFFSET, "滑块验证失败");
        // 验证成功
        cacheTokenValidateResult(id);
        return true;
    }

    public void validateTokenResult(String id) {
        String tokenKey = RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_VALIDATE_RESULT_CACHE.getKey(id);
        String cache = redisService.get(tokenKey);
        Assert.isTrue(ObjectUtils.equals(cache, "1"), "滑块验证失败");
    }

    private void cacheTokenValidateResult(String id) {
        String tokenKey = RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_VALIDATE_RESULT_CACHE.getKey(id);
        redisService.set(tokenKey, "1", RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_VALIDATE_RESULT_CACHE.getExpireTime());
    }

    public SlideVerificationVO getToken(String tokenId) {
        String tokenKey = RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_CACHE.getKey(tokenId);
        String cache = redisService.get(tokenKey);
        Assert.isTrue(StringUtils.isNotBlank(cache), "滑动验证失效，请刷新重新验证！");
        return JsonUtil.fromJson(cache, SlideVerificationVO.class);
    }

    private void cacheToken(SlideVerificationVO slideVerificationVO) {
        String tokenId = slideVerificationVO.getId();
        String tokenKey = RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_CACHE.getKey(tokenId);
        redisService.set(tokenKey, JsonUtil.toJsonString(slideVerificationVO), RedisKeyEnum.SLIDE_VERIFICATION_TOKEN_CACHE.getExpireTime());
    }


    public APIPageResponseBean<SlideVerificationVO> pageList(Integer status, int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
        Page<SlideVerificationEntity> page;
        if (status != null) {
            page = slideVerificationEntityDAO.findAllByStatus(status, pageable);
        } else {
            page = slideVerificationEntityDAO.findAll(pageable);
        }
        List<SlideVerificationEntity> slideVerificationEntities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(page.getContent())) {
            slideVerificationEntities = page.getContent();
        }
        return APIPageResponseBean.success(pageNo, pageSize, page.getTotalElements(), slideVerificationEntities);
    }

    private void refreshSlideVerificationCache() {
        List<SlideVerificationEntity> list = slideVerificationEntityDAO.findAllByStatus(CommonStatusEnum.PUBLISHED.getStatusCode());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<String> voList = list.stream().map(SlideVerificationEntity::getImage).collect(Collectors.toList());
        String key = RedisKeyEnum.SLIDE_VERIFICATION_CACHE.getKey();
        redisService.remove(key);
        redisService.set(key, JsonUtil.toJsonString(voList), RedisKeyEnum.SLIDE_VERIFICATION_CACHE.getExpireTime());
    }

    private SlideVerificationEntity convert(SlideVerificationVO slideVerificationVO) {
        SlideVerificationEntity slideVerificationEntity = new SlideVerificationEntity();
        slideVerificationEntity.setStatus(CommonStatusEnum.NEW.getStatusCode());
        slideVerificationEntity.setImage(slideVerificationVO.getImage());
        return slideVerificationEntity;
    }

}
