package com.sckw.auth.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.sckw.auth.model.vo.req.*;
import com.sckw.auth.model.vo.res.DeptInfoResVo;
import com.sckw.auth.model.vo.res.EntInfoResVo;
import com.sckw.auth.model.vo.res.LoginResVo;
import com.sckw.auth.util.AsyncFactory;
import com.sckw.core.model.enums.SystemTypeEnum;
import com.sckw.core.utils.StringUtils;
import com.sckw.core.web.model.EntCertificateInfo;
import com.sckw.core.web.model.LoginEntInfo;
import com.sckw.core.web.model.LoginUserInfo;
import com.sckw.message.api.dubbo.SmsInfoService;
import com.sckw.system.api.model.dto.req.ForgetPasswordReqDto;
import com.sckw.system.api.model.dto.req.UpdatePasswordReqDto;
import com.sckw.system.api.model.dto.req.UserLoginReqDto;
import com.sckw.system.api.model.dto.res.KwsRoleResDto;
import com.sckw.core.exception.SystemException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.utils.BeanUtils;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.utils.EncryUtil;
import com.sckw.core.web.constant.HttpStatus;
import com.sckw.redis.utils.RedissonUtils;
import com.sckw.system.api.model.dto.req.RegisterReqDto;
import com.sckw.system.api.model.dto.res.*;
import com.sckw.auth.service.IAuthService;
import com.sckw.system.api.RemoteUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @desc: 用户权限实现类
 * @author: czh
 * @date: 2023/6/19
 */
@Slf4j
@Service
public class AuthServiceImpl implements IAuthService {

    @DubboReference(version = "2.0.0", group = "design", check = false)
    private RemoteUserService remoteUserService;

    @DubboReference(version = "2.0.0", group = "design", check = false)
    private SmsInfoService smsInfoService;


    @Override
    public LoginResVo login(LoginReqVo reqDto) throws SystemException {
        /*1、根据账号密码或者手机号校验码登录, 手机号后面处理*/
        KwsUserResDto kwsUser = checkLogin(reqDto);
        LoginResVo loginResVo = new LoginResVo();
        loginResVo.setClientType(reqDto.getClientType());
        BeanUtils.copyProperties(kwsUser, loginResVo);

        //平台管理员直接过了
        if (kwsUser.getIsMain() == Global.YES && kwsUser.getSystemType() == SystemTypeEnum.MANAGE.getCode()) {
            afterProcessor(loginResVo);
            return loginResVo;
        }
        /*2、登录成功，查询角色信息*/
        Long userId = kwsUser.getId();
        List<KwsRoleResDto> kwsRoleResDtos = remoteUserService.queryRoleInfoByUserId(userId);
        loginResVo.setRoleInfo(kwsRoleResDtos);


        /*3、查询用户机构*/
        List<KwsUserDeptResDto> kwsUserDeptResDtos = remoteUserService.queryUserDeptByUserId(userId);
        if (CollectionUtils.isEmpty(kwsUserDeptResDtos)) {
            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.USER_DEPT_NOT_EXISTS);
        }

        List<Long> deptIds = kwsUserDeptResDtos.stream().map(KwsUserDeptResDto::getDeptId).toList();
        List<KwsDeptResDto> kwsDepts = remoteUserService.queryDeptByIds(deptIds);
        if (CollectionUtils.isEmpty(kwsDepts)) {
            //数据不全，直接返回
            afterProcessor(loginResVo);
            return loginResVo;
        }

        /*3、查企业*/
        //目前一个人只能归属于一个企业，所以这里取第一个就行
        Long entId = kwsDepts.get(0).getEntId();
        KwsEnterpriseResDto kwsEnterpriseResDto = remoteUserService.queryEnterpriseById(entId);
        if (Objects.isNull(kwsEnterpriseResDto)) {
            //只要查出了机构，这个if其实不会进的，做保险起见还是加上判空
            afterProcessor(loginResVo);
            return loginResVo;
        }

        EntInfoResVo entInfoResVo = new EntInfoResVo();
        BeanUtils.copyProperties(kwsEnterpriseResDto, entInfoResVo);
        entInfoResVo.setDeptInfo(BeanUtils.copyToList(kwsDepts, DeptInfoResVo.class));

        //查企业类型
        List<EntTypeResDto> entTypeResDtos = remoteUserService.queryEntTypeById(entId);
        if (CollectionUtils.isNotEmpty(entTypeResDtos)) {
            entInfoResVo.setEntTypes(String.join(Global.COMMA, entTypeResDtos.stream().map(EntTypeResDto::getType).map(String::valueOf).distinct().toList()));
        }
        loginResVo.setEntInfo(entInfoResVo);

        /* 4、查资质*/
        List<EntCertificateResDto> entCertificateResDtoList = remoteUserService.queryCertificateByEntId(entId);
        entInfoResVo.setCertificateInfo(entCertificateResDtoList);

        //企业资质有效性判断  1、当前资质没过期，且状态是已认证  2、只要有认证过一次就算有效  参数判断
        String checkType = "2";
        if (checkType.equals("1")) {
            for (EntCertificateResDto entCertificateResDto : entCertificateResDtoList) {
                Date expireTime = entCertificateResDto.getExpireTime();
                entInfoResVo.setValid(DateUtil.compare(new Date(), expireTime) <= 0);
            }
        } else {
            List<EntCheckTrackResDto> entCheckTrackResDtoList = remoteUserService.checkTrackByEntId(entId);
            if (CollectionUtils.isEmpty(entCheckTrackResDtoList)) {
                entInfoResVo.setValid(false);
                afterProcessor(loginResVo);
                return loginResVo;
            }
            entInfoResVo.setValid(entCheckTrackResDtoList.stream().anyMatch(item -> item.getStatus() == Global.NO));
        }

        /*5、生成token，一些信息存redis*/
        afterProcessor(loginResVo);
        return loginResVo;
    }

    private KwsUserResDto checkLogin(LoginReqVo reqDto) throws SystemException {
        List<KwsUserResDto> kwsUsers = remoteUserService.checkUserBase(reqDto.getAccount(), reqDto.getSystemType());
        KwsUserResDto kwsUser = kwsUsers.get(0);
        //密码校验、验证码
        if (StringUtils.isNotBlank(reqDto.getPassword())) {
            remoteUserService.checkPassword(reqDto.getPassword(), kwsUser.getPassword());
        } else {
            String key = Global.USER_LOGIN_CAPTCHA + reqDto.getSystemType() + Global.COLON + kwsUser.getAccount();
            String captcha = RedissonUtils.getString(key);
            if (StringUtils.isBlank(captcha) || StringUtils.isBlank(reqDto.getCaptcha()) || !reqDto.getCaptcha().equals(captcha)) {
                throw new SystemException(HttpStatus.PARAMETERS_MISSING_CODE, HttpStatus.CAPCHA_ERROR);
            }
            RedissonUtils.delete(key);
        }
        return kwsUser;
    }

    /**
     * @param loginResVo 登录的返参
     * @desc: 生成token，存redis
     * @author: czh
     * @date: 2023/6/12
     */
    private void afterProcessor(LoginResVo loginResVo) {
        try {
            //设置token
            loginResVo.setToken(EncryUtil.encry(Global.PRI_KEY, String.valueOf(loginResVo.getId())));

            //异步存redis
            AsyncFactory.execute(new AsyncProcess(loginResVo, remoteUserService));
        } catch (Exception e) {
            throw new SystemException(HttpStatus.GLOBAL_EXCEPTION_CODE, HttpStatus.GLOBAL_EXCEPTION_MESSAGE);
        }
    }

    @Override
    @Transactional(rollbackFor = {})
    public void register(RegisterReqVo reqVo) {
        /*校验验证码*/
        String key = Global.USER_LOGIN_CAPTCHA + reqVo.getPhone() + Global.COLON + reqVo.getSystemType();
        String sms = RedissonUtils.getString(key);
//        if (!reqVo.getCaptcha().equals(sms)) {
//            throw new SystemException(HttpStatus.CODE_10301, HttpStatus.CAPTCHA_ERROR);
//        }
        if (!"12345".equals(reqVo.getCaptcha())) {
            throw new SystemException(HttpStatus.CODE_10301, HttpStatus.CAPTCHA_ERROR);
        }

        RegisterReqDto registerReqDto = new RegisterReqDto();
        BeanUtils.copyProperties(reqVo, registerReqDto);
        remoteUserService.register(registerReqDto);
        RedissonUtils.delete(key);
    }


    @Override
    public void forgetPassword(ForgetPasswordReqVo reqDto) throws SystemException {
        ForgetPasswordReqDto forgetPasswordReqDto = new ForgetPasswordReqDto();
        BeanUtils.copyProperties(reqDto, forgetPasswordReqDto);
        remoteUserService.forgetPassword(forgetPasswordReqDto);
    }

    @Override
    public void updatePassword(UpdatePasswordReqVo reqVo) throws SystemException {
        UpdatePasswordReqDto updatePassword = new UpdatePasswordReqDto();
        BeanUtils.copyProperties(reqVo, updatePassword);
        remoteUserService.updatePassword(updatePassword);
    }

    @Override
    public void resetPassword(Long id) {
        remoteUserService.resetPassword(id);
    }

    @Override
    public String getRegisterSms(GetRegisterSmsReqVo reqVo) {
        String code = "";
        remoteUserService.checkRegisterParam(reqVo.getEntName(), reqVo.getPhone(), reqVo.getSystemType());
        String key = Global.USER_LOGIN_CAPTCHA + reqVo.getPhone() + Global.COLON + reqVo.getSystemType();
        RedissonUtils.put(key, code, 30L);
        return code;
    }


    static class AsyncProcess implements Runnable {

        private final LoginResVo loginResVo;

        private final RemoteUserService remoteUserService;

        public AsyncProcess(LoginResVo loginResVo, RemoteUserService remoteUserService) {
            this.loginResVo = loginResVo;
            this.remoteUserService = remoteUserService;
        }

        @Override
        public void run() {
            //存基础信息
            SaveBaseToCache(loginResVo);

            //存菜单权限
            SaveToCache(loginResVo);

            //存登录记录信息
            SaveLoginInfo(loginResVo);
        }


        /**
         * @param loginResVo 返参
         * @desc: 存基础信息
         * @author: czh
         * @date: 2023/6/28
         */
        private void SaveBaseToCache(LoginResVo loginResVo) {
            //存用户登录信息
            saveUserLoginInfo();

            EntInfoResVo entInfo = loginResVo.getEntInfo();
            if (Objects.isNull(entInfo)) {
                return;
            }
            //单独存用户企业
//            RedissonUtils.putString(Global.getFullUserEntKey(loginResVo.getSystemType(), loginResVo.getId()), JSON.toJSONString(entInfo), Global.PC_TOKEN_EXPIRE);

            //存企业信息
            saveEntLoginInfo(entInfo);
        }

        /**
         * @param entInfo 企业信息
         * @desc: 存企业信息
         * @author: czh
         * @date: 2023/7/3
         */
        private void saveEntLoginInfo(EntInfoResVo entInfo) {
            LoginEntInfo loginEntInfo = new LoginEntInfo();
            BeanUtils.copyProperties(entInfo, loginEntInfo);
            loginEntInfo.setCertificateInfo(BeanUtils.copyToList(entInfo.getCertificateInfo(), EntCertificateInfo.class));
            RedissonUtils.putString(Global.REDIS_ENTERPRISE_PREFIX + entInfo.getId(), JSON.toJSONString(loginEntInfo), Global.PC_TOKEN_EXPIRE);
        }

        /**
         * @desc: 存用户登录信息
         * @author: czh
         * @date: 2023/6/29
         */
        private void saveUserLoginInfo() {
            //存用户登录信息
            LoginUserInfo loginUserInfo = new LoginUserInfo();
            loginUserInfo.setId(loginResVo.getId());
            loginUserInfo.setSystemType(loginResVo.getSystemType());
            loginUserInfo.setAccount(loginResVo.getAccount());
            loginUserInfo.setUserName(loginResVo.getName());
            loginUserInfo.setPhone(loginResVo.getPhone());
            loginUserInfo.setIsMain(loginResVo.getIsMain());
            loginUserInfo.setStatus(loginResVo.getStatus());
            loginUserInfo.setClientType(loginResVo.getClientType());
            EntInfoResVo entInfo = loginResVo.getEntInfo();
            if (!Objects.isNull(entInfo)) {
                loginUserInfo.setEntId(entInfo.getId());
                loginUserInfo.setEntName(entInfo.getFirmName());
                List<DeptInfoResVo> deptInfo = entInfo.getDeptInfo();
                if (!CollectionUtils.isEmpty(deptInfo)) {
                    loginUserInfo.setDeptIds(String.join(",", deptInfo.stream().map(item -> String.valueOf(item.getId())).toList()));
                }
            }
            RedissonUtils.putString(Global.getFullUserLoginKey(loginResVo.getSystemType(), loginResVo.getId(), loginResVo.getClientType()), JSON.toJSONString(loginUserInfo), Global.PC_TOKEN_EXPIRE);

        }

        /**
         * @param loginResVo 登录返参
         * @desc: 存缓存
         * @author: czh
         * @date: 2023/6/28
         */
        private void SaveToCache(LoginResVo loginResVo) {
            //存权限菜单
            long id = loginResVo.getId();
            List<UserAccessMenuInfoResDto> userAccessMenuInfo = remoteUserService.queryUserAccessMenu(id);
            if (CollectionUtils.isEmpty(userAccessMenuInfo)) {
                RedissonUtils.delete(Global.REDIS_SYS_MENU_PREFIX + loginResVo.getSystemType() + Global.COLON + id);
                log.error("未查询到用户{}的菜单权限", id);
                return;
            }

            List<String> menus = new ArrayList<>();
            for (UserAccessMenuInfoResDto userAccessMenuInfoResDto : userAccessMenuInfo) {
                String links = userAccessMenuInfoResDto.getLinks();
                if (StringUtils.isNotBlank(links)) {
                    menus.addAll(Arrays.asList(userAccessMenuInfoResDto.getLinks().split(",")));
                }
            }
            RedissonUtils.putSet(Global.REDIS_SYS_MENU_PREFIX + id, menus);
        }

        /**
         * @param loginResVo 登录返参
         * @desc: 存登录记录信息
         * @author: czh
         * @date: 2023/6/28
         */
        private void SaveLoginInfo(LoginResVo loginResVo) {
            long userId = loginResVo.getId();
            UserLoginReqDto currentDayLogin = remoteUserService.currentDayLogin(userId);
            UserLoginReqDto userLoginReqDto = new UserLoginReqDto();
            userLoginReqDto.setPid(Objects.isNull(currentDayLogin) ? "" : String.valueOf(currentDayLogin.getId()));
            userLoginReqDto.setIp("");
            userLoginReqDto.setUserAgent("");
            userLoginReqDto.setUserId(loginResVo.getId());
            userLoginReqDto.setToken(loginResVo.getToken());
            userLoginReqDto.setValidTime("");
            userLoginReqDto.setFirst(Objects.isNull(currentDayLogin) ? Global.YES : Global.NO);
            userLoginReqDto.setLock(Global.NO);
            userLoginReqDto.setType(Global.NO);
            userLoginReqDto.setSource(loginResVo.getClientType());
            remoteUserService.saveUserLogin(userLoginReqDto);
        }

    }

}
