package com.sonic.frog.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sonic.common.utils.RedisLock;
import com.sonic.frog.dao.SignInRecordDao;
import com.sonic.frog.domain.entity.SignInRecord;
import com.sonic.frog.domain.entity.SignInStat;
import com.sonic.frog.service.SignInRecordService;
import com.sonic.frog.service.SignInStatService;
import com.sonic.frog.utils.DateConvertUtils;
import com.sonic.frog.utils.RedisKeyUtils;
import com.sonic.lion.lib.client.PayClient;
import com.sonic.lion.lib.input.PlatformGiftInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 签到明细表
 * @author zzhan
 */
@Slf4j
@Service
public class SignInRecordServiceImpl extends ServiceImpl<SignInRecordDao, SignInRecord> implements SignInRecordService {

    @Autowired
    private SignInStatService signInStatService;

    @Autowired
    private RedisLock.RedisWrapper redisWrapper;

    @Autowired
    private RedisKeyUtils redisKeyUtils;
    @Autowired
    private PayClient payClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean signIn(Long currentUserId) {
        AtomicBoolean atomicBoolean = new AtomicBoolean(false);
        //加锁处理
        RedisLock redisLock = new RedisLock(redisKeyUtils.signInLockKey(currentUserId), redisWrapper);
        redisLock.tryAcquireRun(30 * 1000L, () -> {
            atomicBoolean.set(signInHandler(currentUserId));
            //判断是否签到成功
            if(atomicBoolean.get()) {
                //签到成功，给用户发放签到奖励 coin
                PlatformGiftInput platformGiftInput = new PlatformGiftInput();
                platformGiftInput.setPlatform("1");
                platformGiftInput.setUid(currentUserId);
                platformGiftInput.setName(PlatformGiftInput.Type.SIGN_IN_GIFT.getBizType().getDesc());
                platformGiftInput.setCreateTime(LocalDateTime.now());
                platformGiftInput.setAmount(500L);
                platformGiftInput.setType(PlatformGiftInput.Type.SIGN_IN_GIFT);
                payClient.platformGift(platformGiftInput);
            }
            return true;
        });
        return atomicBoolean.get();
    }

    @Override
    public SignInStat init(Long currentUserId) {
        LocalDateTime pstNowTime = DateConvertUtils.getDateLaTime();
        return init(currentUserId, pstNowTime);
    }

    /**
     * 签到具体业务处理逻辑
     * @param currentUserId
     */
    private boolean signInHandler(Long currentUserId) {
        LocalDateTime pstNowTime = DateConvertUtils.getDateLaTime();
        //看当前PST时间是否在这个时间段内
        String currentDay = DateConvertUtils.formatYearMonthDay(pstNowTime);
        //处理数据初始化
        SignInStat signInStat = init(currentUserId, pstNowTime);

        //写入当前签到数据，并且统计连续签到天数，然后更新到统计表中
        boolean saveBl = saveSignInRecord(currentUserId, pstNowTime);
        if(!saveBl) {
            //保存数据出错，直接快速返回
            return false;
        }
        //统计签到总天数
        int count = count(Wrappers.<SignInRecord>lambdaQuery()
                .eq(SignInRecord::getUserId, currentUserId)
                .ge(SignInRecord::getDayStr, signInStat.getStartDay())
                .le(SignInRecord::getDayStr, currentDay));
        //更新连续签到天数
        signInStatService.updateStat(signInStat.getId(), count);
        return true;
    }

    /**
     * 签到周期数据初始化
     * @param currentUserId
     * @param pstNowTime
     * @return
     */
    private SignInStat init(Long currentUserId, LocalDateTime pstNowTime) {
        //查询统计表的基础数据
        SignInStat signInStat = signInStatService.get(currentUserId);
        //没有数据的话需要初始化一条数据进去，写入签到的周期从今天开始
        if(signInStat == null) {
            signInStat = signInStatService.init(currentUserId);
        }
        //判断结束时间是否在当前时间之前，如果是的话需要重新初始化周期数据
        if(DateConvertUtils.convertStringToDateTime(signInStat.getEndDay()).isBefore(DateConvertUtils.getDateMinTime())) {
            signInStat.setStartDay(DateConvertUtils.formatYearMonthDay(DateConvertUtils.getDateLaTime()));
            signInStat.setEndDay(DateConvertUtils.formatYearMonthDay(DateConvertUtils.getDateLaTime().plusDays(6)));
            //重新初始化周期，今天为开始日期
            signInStatService.updateStartEndDayAndDays(signInStat.getId(), signInStat.getStartDay(), signInStat.getEndDay());
        }
        //获取起止日期
        String startDay = signInStat.getStartDay();
        //如果今天是第一天开始时间则不用判断昨天是否签到，否则需要进行判断
        if(DateConvertUtils.convertStringToDateTime(startDay).isBefore(pstNowTime.minusDays(1))) {
            //昨天漏了一天的情况，今天的话是不算的，所以需要查询昨天的签到数据是否存在
            SignInRecord yesterdaySignInRecord = getOne(Wrappers.<SignInRecord>lambdaQuery()
                    .select(SignInRecord::getId, SignInRecord::getDayStr)
                    .eq(SignInRecord::getUserId, currentUserId)
                    .eq(SignInRecord::getDayStr, DateConvertUtils.formatYearMonthDay(pstNowTime.minusDays(1))));
            if(yesterdaySignInRecord == null) {
                //重新初始化数据
                signInStat.setStartDay(DateConvertUtils.formatYearMonthDay(DateConvertUtils.getDateLaTime()));
                signInStat.setEndDay(DateConvertUtils.formatYearMonthDay(DateConvertUtils.getDateLaTime().plusDays(6)));
                //重新初始化周期，今天为开始日期
                signInStatService.updateStartEndDayAndDays(signInStat.getId(), signInStat.getStartDay(), signInStat.getEndDay());
            }
        }
        return signInStat;
    }


    /**
     * 保存签到明细数据
     * @param currentUserId
     * @param pstNowTime
     * @return
     */
    private boolean saveSignInRecord(Long currentUserId, LocalDateTime pstNowTime) {
        try {
            SignInRecord signInRecord = new SignInRecord();
            signInRecord.setUserId(currentUserId);
            signInRecord.setDayStr(DateConvertUtils.formatYearMonthDay(pstNowTime));
            signInRecord.setCreateTime(LocalDateTime.now());
            save(signInRecord);
            return true;
        } catch (Exception e) {
            //触发唯一所以得数据写入操作，直接快速返回掉
            return false;
        }
    }

}
