package com.bonus.canteen.core.auth.user.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bonus.canteen.core.auth.config.LoginProperties;
import com.bonus.canteen.core.auth.enums.RoleCodeV2Enum;
import com.bonus.canteen.core.auth.menu.vo.MgrMenuVO;
import com.bonus.canteen.core.auth.po.AuthOpsExpireMsgPO;
import com.bonus.canteen.core.auth.role.MgrRoleTypeV2Enum;
import com.bonus.canteen.core.auth.role.dto.MgrRoleUserDTO;
import com.bonus.canteen.core.auth.role.dto.MgrRoleUserListDTO;
import com.bonus.canteen.core.auth.role.mapper.MgrRoleMenuMapper;
import com.bonus.canteen.core.auth.role.vo.MgrRoleUserVO;
import com.bonus.canteen.core.auth.user.business.MgrUserBurialPointBusiness;
import com.bonus.canteen.core.auth.user.dto.MgrUserInfo;
import com.bonus.canteen.core.auth.user.dto.MgrUserLoginDTO;
import com.bonus.canteen.core.auth.user.entity.MgrUser;
import com.bonus.canteen.core.auth.user.entity.MgrUserInSystem;
import com.bonus.canteen.core.auth.user.mapper.MgrUserInSystemMapper;
import com.bonus.canteen.core.auth.user.mapper.MgrUserMapper;
import com.bonus.canteen.core.auth.user.mapper.MgrUserRoleMapper;
import com.bonus.canteen.core.auth.user.vo.MgrUserRoleInfoVO;
import com.bonus.canteen.core.common.constant.LeMqConstant;
import com.bonus.canteen.core.common.enums.LogRecordOperTypeEnum;
import com.bonus.canteen.core.common.utils.TenantContextHolder;
import com.bonus.canteen.core.notice.hawkeye.service.AllocLogRecordService;
import com.bonus.common.houqin.framework.secure.AccessToken;
import com.bonus.canteen.core.customer.model.PigxUser;
import com.bonus.canteen.core.customer.utils.DelFlagEnum;
import com.bonus.canteen.core.data.dataset.Executors;
import com.bonus.canteen.core.merchant.constant.MerchantStatusEnum;
import com.bonus.canteen.core.merchant.vo.MercMerchantEndTimeVO;
import com.bonus.canteen.core.notice.hawkeye.model.AllocLogRecord;
import com.bonus.canteen.core.order.mq.MqUtil;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.houqin.constant.LeConstants;
import com.bonus.common.houqin.framework.secure.SecureManager;
import com.bonus.common.houqin.i18n.I18n;
import com.bonus.common.houqin.utils.AesEncryptUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class MgrUserService extends ServiceImpl<MgrUserMapper, MgrUser> {
   private static final Logger log = LoggerFactory.getLogger(MgrUserService.class);
   private static final String USER_LOCK = "user-lock-";
   private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
   @Autowired
   private MgrUserInSystemMapper mgrUserInSystemMapper;
//   @Autowired
//   private MgrRoleService mgrRoleService;
//   @Autowired
//   @Lazy
//   private CustInfoApi custInfoApi;
   @Autowired
   @Lazy
   private AllocLogRecordService allocLogRecordService;
   @Autowired
   private MgrUserRoleMapper mgrUserRoleMapper;
   @Autowired
   private MgrUserMapper mgrUserMapper;
//   @Autowired
//   private MgrRoleMapper mgrRoleMapper;
   @Autowired
   private StringRedisTemplate stringRedisTemplate;
   @Autowired
   private MgrRoleMenuMapper mgrRoleMenuMapper;
//   @Autowired
//   private MgrRoleAppMenuService mgrRoleAppMenuService;
//   @Autowired
//   private AesEncryptUtil aesEncryptUtil;
   @Autowired
   private LoginProperties loginProperties;
//   @Autowired
//   private CustomBizApi customBizApi;
//   @Autowired
//   @Lazy
//   private AllocCanteenApi allocCanteenApi;
//   @Lazy
//   @Autowired
//   private DeviceApi deviceApi;
//   @Lazy
//   @Autowired
//   private MgrAuthApi mgrAuthApi;
//   @Autowired
//   @Lazy
//   private MercMerchantApi mercMerchantApi;
//   @Autowired
//   @Lazy
//   private CardInfoServiceV4Api cardInfoServiceV4Api;
//   @Autowired
//   @Lazy
//   private BackStaffApi backStaffApi;
   @Autowired
   @Lazy
   private MgrUserBurialPointBusiness mgrUserBurialPointBusiness;

   public MgrUserInfo getUserInfoByUsername(String realUsername) throws Exception {
      MgrUserInSystem mgrUser = (MgrUserInSystem)Executors.readInSystem(() -> {
         return (MgrUserInSystem)this.mgrUserInSystemMapper.selectOne(Wrappers.lambdaQuery(MgrUserInSystem.class)
                 .eq(MgrUserInSystem::getUsername, realUsername).eq(MgrUserInSystem::getDelFlag, DelFlagEnum.DEL_FALSE.key()));
      });
      if (mgrUser == null) {
         log.info("***根据用户名 : {} 未查询到用户的数据, 返回用户名密码错误****************", realUsername);
         throw new ServiceException(I18n.getMessage("auth_error_password", new Object[0]));
      } else if (ObjectUtil.isNotNull(mgrUser.getEndTime()) && LocalDateTime.now().isAfter(mgrUser.getEndTime())) {
         throw new ServiceException("授权已过期，请重新授权");
      } else if (mgrUser.getUserId() == 0L) {
         return this.createMgrUserInfo((MgrUser) BeanUtil.copyProperties(mgrUser, MgrUser.class, new String[0]), (Long)null, (Long)null, (Long)null, (Long)null, CollUtil.newHashSet(new String[]{RoleCodeV2Enum.ROLE_ADMIN.key()}), CollUtil.newHashSet(new String[]{"1"}));
      } else {
         MercMerchantEndTimeVO mercMerchantVO = (MercMerchantEndTimeVO)Executors.readInSystem(() -> {
            return this.mgrUserInSystemMapper.selectMerchantByMerchantId(mgrUser.getTenantId());
         });
         if (mercMerchantVO != null && !LocalDateTime.now().isAfter(mercMerchantVO.getEndTime())) {
            if (!MerchantStatusEnum.NORMAL.key().equals(mercMerchantVO.getStatus())) {
               throw new ServiceException(I18n.getMessage("auth_disable_account", new Object[0]));
            } else {
               TenantContextHolder.setTenantId(mgrUser.getTenantId());
               if (this.checkCustState(mgrUser.getUserId())) {
                  log.info("用户已注销,不可以登录~~~~~~~~");
                  throw new ServiceException(I18n.getMessage("auth_cust_cancel", new Object[0]));
               } else {
                  List<MgrUserRoleInfoVO> userRoleInfoVOList = this.mgrUserRoleMapper.getMgrUserRoleListByUserId(mgrUser.getUserId());
                  if (ObjectUtil.isEmpty(userRoleInfoVOList)) {
                     log.info("用户未配置角色,不可以登录~~~~~~~~");
                     throw new ServiceException(I18n.getMessage("auth_no_role", new Object[0]));
                  } else {
                     Set<String> permissions = (Set)this.mgrRoleMenuMapper.listPermissions(mgrUser.getUserId()).stream().map(MgrMenuVO::getMenuCode).collect(Collectors.toSet());
                     Set<String> roles = (Set)userRoleInfoVOList.stream().map(MgrUserRoleInfoVO::getRoleCode).collect(Collectors.toSet());
                     MgrUserInfo userInfo = this.createMgrUserInfo((MgrUser)BeanUtil.copyProperties(mgrUser, MgrUser.class, new String[0]), -1L, -1L, -1L, mgrUser.getTenantId(), permissions, roles);
                     Iterator var8 = userRoleInfoVOList.iterator();

                     while(true) {
                        MgrUserRoleInfoVO userRoleVO;
                        do {
                           if (!var8.hasNext()) {
                              log.info("商家操作员登录用户详细信息userInfo : {}", userInfo);
                              return userInfo;
                           }

                           userRoleVO = (MgrUserRoleInfoVO)var8.next();
                        } while(!MgrRoleTypeV2Enum.ROLE_ADMIN.getKey().equals(userRoleVO.getRoleType()) && !LeConstants.COMMON_YES.equals(userRoleVO.getIfDefault()));

                        userInfo.setRoleCode(userRoleVO.getRoleCode());
                     }
                  }
               }
            }
         } else {
            throw new Exception(I18n.getMessage("auth_overtime_account", new Object[0]));
         }
      }
   }

   private boolean checkCustState(Long userId) {
      Long custId = this.mgrUserMapper.selectCustIdByUserId(userId);
      return ObjectUtil.isNull(custId);
   }

   private MgrUserInfo createMgrUserInfo(MgrUser mgrUser, Long canteenEffId, Long custEffId, Long dishesEffId, Long tenantId, Set<String> permissions, Set<String> roles) {
      MgrUserInfo userInfo = new MgrUserInfo();
      userInfo.setMgrUser(mgrUser);
      userInfo.setCanteenEffId(canteenEffId);
      userInfo.setCustEffId(custEffId);
      userInfo.setDishesEffId(dishesEffId);
      userInfo.setTenantId(tenantId);
      userInfo.setPermissions(permissions);
      userInfo.setRoles(roles);
      return userInfo;
   }

   public List<MgrRoleUserVO> getAllUserByRoleCode(MgrRoleUserListDTO content) {
      MgrRoleUserDTO roleUserDTO = new MgrRoleUserDTO();
      roleUserDTO.setDelFlag(DelFlagEnum.DEL_FALSE.key());
      roleUserDTO.setRoleCode(content.getRoleCode());
      return ((MgrUserMapper)this.baseMapper).selectAllUserByRoleCode(roleUserDTO);
   }

   public PigxUser login(MgrUserLoginDTO loginDTO) throws Exception {
      String realUsername = this.mgrUserBurialPointBusiness.didBeforeLogin(loginDTO);
      if (CharSequenceUtil.isNotBlank(realUsername)) {
         return this.getPigxUser(realUsername);
      } else {
         realUsername = loginDTO.getUsername();
         ValueOperations<String, String> valueOperations = this.stringRedisTemplate.opsForValue();
         String s = (String)this.stringRedisTemplate.opsForValue().get("user-lock-" + realUsername);
         if (CharSequenceUtil.isNotBlank(s)) {
            if (Integer.parseInt(s) > 4 && Integer.parseInt(s) < 10) {
               valueOperations.increment("user-lock-" + realUsername);
               throw new ServiceException(I18n.getMessage("auth_lock_account", new Object[0]));
            }

            if (Integer.parseInt(s) > 10) {
               throw new ServiceException(I18n.getMessage("auth_lock_account_time", new Object[0]));
            }
         }

         boolean flag = this.matches(loginDTO);
         if (!flag) {
            this.handleLoginError(realUsername);
            throw new ServiceException(I18n.getMessage("auth_error_password_username", new Object[0]));
         } else {
            return this.getPigxUser(realUsername);
         }
      }
   }

   protected PigxUser getPigxUser(String realUsername) throws Exception {
      MgrUserInfo info = this.getUserInfoByUsername(realUsername);
      PigxUser user = PigxUser.builder().username(info.getMgrUser().getUsername()).id(info.getMgrUser().getUserId()).roleCode(info.getRoleCode()).merchantId(info.getTenantId()).canteenEffId(info.getCanteenEffId()).custEffId(info.getCustEffId()).dishesEffId(info.getDishesEffId()).build();

      try {
         AccessToken accessToken = AccessToken.create(user.getId());
         accessToken.setSubjectName(realUsername);
         Map<String, String> subjectData = (Map) JSON.parseObject(JSON.toJSONString(user));
         accessToken.setSubjectData(subjectData);
         accessToken.setScope(user.getMerchantId() == null ? null : String.valueOf(user.getMerchantId()));
         accessToken.bind().authenticate().store().write();
         if (String.valueOf(TenantContextHolder.getTenantId()).equals(info.getMgrUser().getUsername())) {
            AuthOpsExpireMsgPO opsExpireMsgPO = new AuthOpsExpireMsgPO();
            opsExpireMsgPO.setUserId(info.getMgrUser().getUserId());
            opsExpireMsgPO.setAccessTokenId(accessToken.getId());
            MqUtil.sendDelay(JSON.toJSONString(opsExpireMsgPO), LeMqConstant.Topic.AUTH_OPS_USER_EXPIRE, 86400000);
         }

         this.update(Wrappers.lambdaUpdate(MgrUser.class).set(MgrUser::getLoginTime, LocalDateTime.now())
                                                           .set(MgrUser::getUptime, LocalDateTime.now())
                                                           .eq(MgrUser::getUserId, user.getId()));
         if (Objects.nonNull(this.loginProperties.getReservedRecentNum())) {
            SecureManager.revokeAuthenticate(user.getId(), this.loginProperties.getReservedRecentNum());
         }
      } catch (Exception var7) {
         String var10002 = I18n.getMessage("auth_login_fail", new Object[0]);
         throw new ServiceException(var10002 + var7.getMessage());
      }

//      if (!"admin".equals(user.getUsername())) {
//         AllocLogRecord logRecord = new AllocLogRecord(user.getId(), user.getUsername(), "/oauth/token", "人员登录", LogRecordOperTypeEnum.LOGIN.getKey());
//         this.allocLogRecordService.saveLogRecordForCustom(logRecord);
//      }

      return user;
   }

   private boolean matches(MgrUserLoginDTO loginDTO) {
      boolean flag = false;
      String username = loginDTO.getUsername();
      String password = loginDTO.getPassword();
      password = AesEncryptUtil.aesDecode(password);
      MgrUserInSystem user = (MgrUserInSystem) Executors.readInSystem(() -> {
         return (MgrUserInSystem)this.mgrUserInSystemMapper.selectOne(Wrappers.lambdaQuery(MgrUserInSystem.class)
                 .select(MgrUserInSystem::getPassword, MgrUserInSystem::getUserId, MgrUserInSystem::getLockFlag)
                 .eq(MgrUserInSystem::getUsername, username)
                 .eq(MgrUserInSystem::getDelFlag, LeConstants.COMMON_NO));
      });
      if (ObjectUtil.isNotNull(user) && encoder.matches(password, user.getPassword())) {
         flag = true;
      }

      return flag;
   }

   private void handleLoginError(String username) {
      ValueOperations<String, String> valueOperations = this.stringRedisTemplate.opsForValue();
      String value = (String)valueOperations.get("user-lock-" + username);
      if (CharSequenceUtil.isBlank(value)) {
         valueOperations.increment("user-lock-" + username);
         this.stringRedisTemplate.expire("user-lock-" + username, 30L, TimeUnit.SECONDS);
      }

      Long increment = valueOperations.increment("user-lock-" + username);
      if (!ObjectUtil.isNull(increment)) {
         if (5 == increment.intValue()) {
            this.stringRedisTemplate.expire("user-lock-" + username, 2L, TimeUnit.MINUTES);
         }

         if (10 == increment.intValue()) {
            this.stringRedisTemplate.expire("user-lock-" + username, 10L, TimeUnit.MINUTES);
         }

      }
   }

}
