package com.dayou.service.impl;

import cn.hutool.Hutool;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.dayou.common.BaseEntity;
import com.dayou.condition.GisLoginCondition;
import com.dayou.condition.LoginCondition;
import com.dayou.constants.JwtConstants;
import com.dayou.dto.LoginDTO;
import com.dayou.dto.UpdatePasswordDTO;
import com.dayou.entity.GisUser;
import com.dayou.entity.GisVisited;
import com.dayou.entity.User;
import com.dayou.exception.ErrorCode;
import com.dayou.mapper.GisUserMapper;
import com.dayou.service.IGisUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dayou.utils.IpUtils;
import com.dayou.utils.JwtTokenUtil;
import com.dayou.utils.LoginContext;
import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.dayou.utils.ExcelUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;
import com.dayou.enums.BatchTaskTypeEnum;

import javax.servlet.http.HttpServletRequest;

import static com.dayou.common.Constants.GIS_USER;
import static com.dayou.exception.ErrorCode.*;

/**
 * <p>
 * GIS用户 服务实现类
 * </p>
 *
 * @author wucl
 * @since 2023-05-17
 */
@Service
public class GisUserServiceImpl extends ServiceImpl<GisUserMapper, GisUser> implements IGisUserService {

    @Autowired
    @Qualifier("verificationCodeCache")
    private Cache<String,String> cache;

    @Autowired
    @Qualifier("gisLoginCache")
    private Cache<String,GisUser> loginCache;

    @Autowired
    private GisUserMapper gisUserMapper;

    @Override
    @SuppressWarnings("unchecked")
    public Page<GisUser> selectPage(Page page,GisUser gisUser){
        return this.page(page, new QueryWrapper<GisUser>(gisUser));
    }


    @Override
    public GisUser detail(Long id){
        return this.getById(id);
    }

    @Override
    public Boolean add(GisUser gisUser){
        return  this.save(gisUser);
    }

    @Override
    public Boolean update(GisUser gisUser){
        return  this.updateById(gisUser);
    }

    @Override
    public Boolean delete(Long id){
        //逻辑删除
        return this.removeById(id);
    }

    @Override
    public LoginDTO login(GisLoginCondition gisLoginCondition) {
        doCheckVerificationCode(gisLoginCondition);

        String account = gisLoginCondition.getAccount();
        String pwd = gisLoginCondition.getPassword();

        GisUser user = doCheckGisUserLogin(account, pwd);

        if (user.getEnable().equals(Boolean.FALSE)) {
            ErrorCode.throwBusinessException(USER_DISABLE);
        }

        Long userId = user.getId();
        String subject = GIS_USER + userId;
        Map<String, Object> claims = Maps.newHashMap();
        String token = JwtTokenUtil.doGenerateGisToken(claims,subject);
        //缓存用户信息
        cacheGisUser(user);

        return LoginDTO.builder()
                .token(token)
                .build();
    }

    @Override
    public GisUser baseInfo(String token) {
        Claims claims = JwtTokenUtil.getClaimFromToken(token, JwtConstants.SECRET);
        String subject = claims.getSubject();
        String id = subject.split(GIS_USER)[1];
        Long gisUserId = Long.valueOf(id);
        GisUser gisUser = this.getOne(new LambdaQueryWrapper<GisUser>()
                .select(GisUser::getName,
                        GisUser::getEnable,
                        GisUser::getAccount,
                        GisUser::getCityName,
                        GisUser::getCountryName,
                        GisUser::getEmail,
                        GisUser::getExpiryDate,
                        GisUser::getLinkman,
                        GisUser::getMobile).eq(BaseEntity::getId, gisUserId));
        return gisUser;
    }

    @Override
    public Boolean isAlive(String token) {
        Claims claimFromToken = JwtTokenUtil.getClaimFromToken(token, JwtConstants.SECRET);
        String subject = claimFromToken.getSubject();
        if (subject.startsWith(GIS_USER)){
            final Date current = new Date();
            claimFromToken.setExpiration(new Date((current.getTime() + JwtConstants.GIS_EXPIRATION * 1000)));
            loginCache.put(subject,loginCache.getIfPresent(subject));
            return Boolean.TRUE;
        }
        ErrorCode.throwBusinessException(ErrorCode.INVALID_TOKEN);
        return Boolean.FALSE;
    }

    @Override
    public Boolean updatePassword(UpdatePasswordDTO passwordDTO,String token) {
        Claims claims = JwtTokenUtil.getClaimFromToken(token, JwtConstants.SECRET);
        String subject = claims.getSubject();
        String id = subject.split(GIS_USER)[1];
        Long gisUserId = Long.valueOf(id);
        GisUser gisUser = this.getById(gisUserId);

        if (!StrUtil.equals(gisUser.getPassword(),passwordDTO.getOriginalPassword())){
            ErrorCode.throwBusinessException(ErrorCode.PASSWORD_ERROR);
        }

        if (!StrUtil.equals(passwordDTO.getCurrentPassword(),passwordDTO.getRepeatPassword())){
            ErrorCode.throwBusinessException(ErrorCode.PASSWORD_DIFFERENT);
        }

        boolean update = this.update(new LambdaUpdateWrapper<GisUser>()
                .set(GisUser::getPassword, passwordDTO.getCurrentPassword())
                .eq(BaseEntity::getId,gisUserId));
        return update;
    }

    private GisUser doCheckGisUserLogin(String account, String pwd) {
        List<GisUser> users = gisUserMapper.selectList(new LambdaQueryWrapper<GisUser>()
                .eq(GisUser::getAccount,account).eq(BaseEntity::getDeleted,false));
        if (CollectionUtils.isEmpty(users)){
            ErrorCode.throwBusinessException(PWD_ERROR);
        }

        GisUser user = users.get(0);
        Long userId = user.getId();
        LambdaQueryWrapper<GisUser> userLambdaQueryWrapper = new LambdaQueryWrapper<GisUser>()
                .eq(GisUser::getId, userId)
                .eq(GisUser::getPassword, pwd)
                .eq(GisUser::getDeleted, false);

        GisUser xUser = this.getOne(userLambdaQueryWrapper);

        if (xUser == null) {
            ErrorCode.throwBusinessException(PWD_ERROR);
        }
        LocalDate now = LocalDate.now();
        LocalDate expiryDate = xUser.getExpiryDate();
        if (now.isAfter(expiryDate)){
            ErrorCode.throwBusinessException(GIS_USER_EXPIRY);
        }
        if (!xUser.getEnable()){
            ErrorCode.throwBusinessException(GIS_USER_DISABLE);
        }

        return xUser;
    }

    private void cacheGisUser(GisUser user) {
        loginCache.put(GIS_USER+user.getId(),user);
    }

    private void doCheckVerificationCode(GisLoginCondition gisLoginCondition) {
        Optional.ofNullable(gisLoginCondition.getNonceId()).ifPresent(x->{
            String code = cache.getIfPresent(x);
            if (StrUtil.isBlank(code)){
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"图形验证码过期，请点击刷新。");
            }
            if (!code.equals(gisLoginCondition.getVerificationCode())){
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"验证码错误，请重新输入");
            }
        });

    }
}
