package com.bonus.core.customer.utils;

import cn.hutool.core.map.MapUtil;
import com.bonus.core.config.SecureProperties;
import com.bonus.core.config.WebContext;
import com.bonus.core.customer.service.AuthorizingService;
import com.bonus.core.secure.PmsCache;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.Instant;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.bonus.core.config.AccessToken;

public class SecureManager {
   private static final Logger log = LoggerFactory.getLogger(SecureManager.class);
   private static SecureProperties secureProperties;
   private static StringRedisTemplate redisTemplate;
   private static AuthorizingService authorizingService;
   private static ObjectMapper objectMapper;

   private SecureManager() {
   }

   public static void setSecureProperties(SecureProperties secureProperties) {
      if (SecureManager.secureProperties == null) {
         SecureManager.secureProperties = secureProperties;
      }
   }

   public static void setRedisTemplate(StringRedisTemplate redisTemplate) {
      if (SecureManager.redisTemplate == null) {
         SecureManager.redisTemplate = redisTemplate;
      }
   }

   public static void setAuthorizingService(AuthorizingService authorizingService) {
      if (SecureManager.authorizingService == null) {
         SecureManager.authorizingService = authorizingService;
      }
   }

   public static void setObjectMapper(ObjectMapper objectMapper) {
      if (SecureManager.objectMapper == null) {
         SecureManager.objectMapper = objectMapper;
      }
   }

   private static String getPmsKey(long subjectId) {
      return "permissions:subject_id:" + subjectId;
   }

   private static String getRoleKey(long subjectId) {
      return "roles:subject_id:" + subjectId;
   }

   public static void clearAllRoleAndPermission() {
      redisTemplate.delete(secureProperties.getPermissionKey());
   }

   public static void clearRoleAndPermission(long subjectId) {
      redisTemplate.boundHashOps(secureProperties.getPermissionKey()).delete(new Object[]{getRoleKey(subjectId), getPmsKey(subjectId)});
   }

   public static void clearRole(long subjectId) {
      redisTemplate.boundHashOps(secureProperties.getPermissionKey()).delete(new Object[]{getRoleKey(subjectId)});
   }

   public static void clearPermission(long subjectId) {
      redisTemplate.boundHashOps(secureProperties.getPermissionKey()).delete(new Object[]{getPmsKey(subjectId)});
   }

   public static Set<String> getPermission() {
      return getPms(SecureManager::getPmsKey, (subjectId) -> {
         return authorizingService.permissions(subjectId);
      });
   }

   public static Set<String> getRole() {
      return getPms(SecureManager::getRoleKey, (subjectId) -> {
         return authorizingService.roles(subjectId);
      });
   }

   public static boolean hasRole(String... role) {
      return getRole().containsAll(Arrays.asList(role));
   }

   public static boolean hasAnyRole(String... role) {
      Stream var10000 = Stream.of(role);
      Set var10001 = getRole();
      Objects.requireNonNull(var10001);
      return var10000.anyMatch(var10001::contains);
   }

   public static boolean hasPermission(String... pms) {
      return getPermission().containsAll(Arrays.asList(pms));
   }

   public static boolean hasAnyPermission(String... pms) {
      Stream var10000 = Stream.of(pms);
      Set var10001 = getPermission();
      Objects.requireNonNull(var10001);
      return var10000.anyMatch(var10001::contains);
   }

   public static boolean isLogin() {
      return (Boolean) WebContext.get().getAccessToken().map(AccessToken::isAuthenticated).orElse(false);
   }

   public static Optional<Long> getSubjectId() {
      return WebContext.get().getAccessToken().map(AccessToken::getSubjectId);
   }

   public static Optional<String> getSubjectName() {
      return WebContext.get().getAccessToken().map(AccessToken::getSubjectName);
   }

   public static Map<String, String> getSubjectData() {
      return (Map)WebContext.get().getAccessToken().map(AccessToken::getSubjectData).orElse(Maps.newHashMap());
   }

   public static Map<String, String> attachData(Map<String, String> data) {
      try {
         if (MapUtil.isEmpty(data)) {
            return Maps.newHashMapWithExpectedSize(0);
         } else {
            AccessToken accessToken = (AccessToken)WebContext.get().getAccessToken().orElse(AccessToken.create());
            Objects.requireNonNull(accessToken);
            data.forEach(accessToken::setData);
            accessToken.bind().store();
            return accessToken.getSubjectData();
         }
      } catch (Throwable var2) {
         throw var2;
      }
   }

   public static Map<String, String> attachData(String key, String value) {
      try {
         return attachData(Collections.singletonMap(key, value));
      } catch (Throwable var3) {
         throw var3;
      }
   }

   public static Map<String, String> removeData(String... key) {
      try {
         Optional<AccessToken> accessToken = WebContext.get().getAccessToken();
       //  return (Map)(accessToken.isEmpty() ? Maps.newHashMap() : ((AccessToken)accessToken.get()).removeData(key).getSubjectData());
         return (Map)(accessToken == null? Maps.newHashMap() : ((AccessToken)accessToken.get()).removeData(key).getSubjectData());
      } catch (Throwable var2) {
         throw var2;
      }
   }

   public static Set<String> getPms(Function<Long, String> keySupplier, Function<Long, Set<String>> pmsSupplier) {
   //   if (WebContext.get().getAccessToken().isEmpty()) {
      if (WebContext.get().getAccessToken() == null) {
         return Sets.newHashSetWithExpectedSize(0);
      } else {
         Long subjectId = ((AccessToken)WebContext.get().getAccessToken().get()).getSubjectId();
         if (subjectId == null) {
            return Sets.newHashSetWithExpectedSize(0);
         } else {
            BoundHashOperations<String, String, String> pmsStore = redisTemplate.boundHashOps(secureProperties.getPermissionKey());
            String pmsKey = (String)keySupplier.apply(subjectId);
            if (BooleanUtils.isNotTrue(pmsStore.hasKey(pmsKey))) {
               Set<String> pms = (Set)pmsSupplier.apply(subjectId);
               if (pms == null) {
                  pms = Sets.newHashSetWithExpectedSize(0);
               }

               try {
                  pmsStore.put(pmsKey, objectMapper.writeValueAsString(new PmsCache(Instant.now().plusSeconds(secureProperties.getPermissionTTL()).getEpochSecond(), (Set)pms)));
               } catch (JsonProcessingException var8) {
                  log.error("Save permission error", var8);
               }

               return (Set)pms;
            } else {
               String permissionValue = (String)pmsStore.get(pmsKey);
               if (permissionValue == null) {
                  return getPms(keySupplier, pmsSupplier);
               } else {
                  PmsCache cachedPms;
                  try {
                     cachedPms = (PmsCache)objectMapper.readValue(permissionValue, PmsCache.class);
                  } catch (JsonProcessingException var9) {
                     log.error("Read permission error", var9);
                     return Sets.newHashSet();
                  }

                  if (cachedPms.isExpired()) {
                     log.info("Permission cache expired, read new");
                     redisTemplate.boundHashOps(secureProperties.getPermissionKey()).delete(new Object[]{pmsKey});
                     return getPms(keySupplier, pmsSupplier);
                  } else {
                     return cachedPms.getPms();
                  }
               }
            }
         }
      }
   }

   public static void logout() {
      WebContext.get().getAccessToken().ifPresent(AccessToken::clear);
   }

   public static void revokeAuthenticate() {
      WebContext.get().getAccessToken().ifPresent((accessToken) -> {
         accessToken.revokeAuthenticate().store();
      });
   }

   public static void revokeAuthenticate(long subjectId, int reservedRecentNum) {
      StringRedisTemplate var10000 = redisTemplate;
      String var10001 = secureProperties.getServer().getSubjectRefTokenKey();
      Set<String> keys = var10000.keys(var10001 + ":" + subjectId + ":*");
      if (CollectionUtils.size(keys) > reservedRecentNum) {
         assert keys != null;

         Map<Long, List<String>> createTimeAsc_keys = (Map)keys.stream().collect(Collectors.groupingBy((key) -> {
            return Long.parseLong(key.split(":")[4]);
         }, TreeMap::new, Collectors.toList()));
      //   List<String> keysAsc = createTimeAsc_keys.values().stream().flatMap(Collection::stream).toList();
         List<String> keysAsc = createTimeAsc_keys.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
         List<String> beDeleteRefKeys = keysAsc.subList(0, keysAsc.size() - reservedRecentNum);
//         List<String> beDeleteTokenKeys = beDeleteRefKeys.stream().map((dk) -> {
//            String var10000 = secureProperties.getServer().getStoreKey();
//            return var10000 + ":" + dk.split(":")[3];
//         }).toList();
         List<String> beDeleteTokenKeys = beDeleteRefKeys.stream()
                 .map(dk -> secureProperties.getServer().getStoreKey() + ":" + dk.split(":")[3])
                 .collect(Collectors.toList());
         redisTemplate.delete(ListUtils.sum(beDeleteRefKeys, beDeleteTokenKeys));
      }
   }

   public boolean equals(final Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof SecureManager)) {
         return false;
      } else {
         SecureManager other = (SecureManager)o;
         return other.canEqual(this);
      }
   }

   protected boolean canEqual(final Object other) {
      return other instanceof SecureManager;
   }

   public int hashCode() {
    //  int result = true;
      return 1;
   }

   public String toString() {
      return "SecureManager()";
   }
}
