package com.gxwebsoft.common.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.utils.CacheClient;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.*;
import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.common.system.param.UserParam;
import com.gxwebsoft.common.system.service.*;
import com.gxwebsoft.love.entity.UserPlanEquity;
import com.gxwebsoft.love.entity.UserProfile;
import com.gxwebsoft.love.param.UserProfileParam;
import com.gxwebsoft.love.service.UserPlanEquityService;
import com.gxwebsoft.love.service.UserProfileService;
import com.gxwebsoft.oa.entity.App;
import com.gxwebsoft.oa.service.AppService;
import com.gxwebsoft.shop.entity.Merchant;
import com.gxwebsoft.shop.entity.MerchantClerk;
import com.gxwebsoft.shop.service.MerchantClerkService;
import com.gxwebsoft.shop.service.MerchantService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.gxwebsoft.common.core.constants.RedisConstants.TEN_ANT_SETTING_KEY;
import static com.gxwebsoft.common.core.constants.RedisConstants.USER_RANKING_BY_APPS;

/**
 * 用户Service实现
 *
 * @author WebSoft
 * @since 2018-12-24 16:10:14
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
  @Resource
  private UserRoleService userRoleService;
  @Resource
  private RoleMenuService roleMenuService;
  @Resource
  private BCryptPasswordEncoder bCryptPasswordEncoder;
  @Resource
  private MerchantService merchantService;
  @Resource
  private MerchantClerkService merchantClerkService;
  @Resource
  private CacheClient cacheClient;
  @Resource
  private AppService appService;
  @Resource
  private TenantService tenantService;
  @Resource
  private CompanyService companyService;
  @Resource
  private StringRedisTemplate stringRedisTemplate;
  @Resource
  private RedisUtil redisUtil;
  @Resource
  private UserProfileService userProfileService;

  @Resource
  private UserPlanEquityService userPlanEquityService;

  @Override
  public PageResult<User> pageRel(UserParam param) {
    PageParam<User, UserParam> page = new PageParam<>(param);
    page.setDefaultOrder("create_time desc");
    List<User> list = baseMapper.selectPageRel(page, param);
    // 查询用户的角色
    selectUserRoles(list);
    // 查询用户详细资料
    if (param.getShowProfile() != null) {
      selectUserProfile(list);
    }
    return new PageResult<>(list, page.getTotal());
  }

  @Override
  public List<User> listRel(UserParam param) {
    List<User> list = baseMapper.selectListRel(param);
    // 查询用户的角色
    selectUserRoles(list);
    // 排序
    PageParam<User, UserParam> page = new PageParam<>(param);
    page.setDefaultOrder("create_time desc");
    return page.sortRecords(list);
  }

  @Override
  public void listRanking(UserParam param) {
    List<User> list = baseMapper.selectListRel(param);
    Map<String, String> map = new HashMap<>();
    list.forEach(d -> {
      int count = appService.count(new LambdaQueryWrapper<App>()
        .eq(App::getUserId, d.getUserId()));
      // 更新全部用户的插件数量
      cacheClient.zAdd(USER_RANKING_BY_APPS, d.getUserId(), (double) count);
    });
  }

  @Override
  public User getByIdRel(Integer userId) {
    UserParam param = new UserParam();
    param.setUserId(userId);
    User user = param.getOne(baseMapper.selectListRel(param));
    if (user != null) {
      user.setRoles(userRoleService.listByUserId(user.getUserId()));
      user.setAuthorities(roleMenuService.listMenuByUserId(user.getUserId(), null));
      // 系统配置信息
      Map<String, Object> map = new HashMap<>();
      // 1)基本信息
      final String setting = redisUtil.get("setting:setting:" + user.getTenantId());
      if(setting != null){
        final JSONObject site = JSONObject.parseObject(setting);
        map.put("siteName",site.getString("siteName"));
        map.put("remarks",site.getString("remarks"));
        map.put("keyword",site.getString("keyword"));
        map.put("icp",site.getString("icp"));
        map.put("copyright",site.getString("copyright"));
        map.put("company",site.getString("company"));
        map.put("address",site.getString("address"));
        map.put("phone",site.getString("phone"));
        map.put("email",site.getString("email"));
        map.put("domain",site.getString("domain"));
        map.put("support",site.getString("support"));
        map.put("logo",site.getString("logo"));
      }
      // 2)云存储
      String key = "setting:upload:" + user.getTenantId();
      final String upload = redisUtil.get(key);
      if(upload != null){
        final JSONObject object = JSONObject.parseObject(upload);
        map.put("uploadMethod",object.getString("uploadMethod"));
        map.put("bucketDomain",object.getString("bucketDomain"));
        map.put("fileUrl",object.getString("fileUrl") + "/");
        user.setSystem(map);
      }
      // 企业信息
      String key2 = "company:authoritative:" + user.getTenantId();
      final Company company = redisUtil.get(key2, Company.class);
      user.setCompanyInfo(company);
      user.setSystem(map);
      // 商户编号
      Merchant merchant = merchantService.getOne(new QueryWrapper<Merchant>()
        .eq("merchant_owner", user.getUserId()), false);
      if (merchant != null) {
        user.setMerchantCode(merchant.getMerchantCode());
        user.setMerchantName(merchant.getMerchantName());
      } else {
        MerchantClerk clerk = merchantClerkService.getOne(new QueryWrapper<MerchantClerk>().eq("user_id", user.getUserId()), false);
        if (clerk != null) {
          user.setMerchantCode(clerk.getMerchantCode());
        }
      }

      // 我的权益
      List<UserPlanEquity> userPlanEquityList = userPlanEquityService.list(new LambdaQueryWrapper<UserPlanEquity>()
        .eq(UserPlanEquity::getUserId, userId)
        .gt(UserPlanEquity::getExpirationTime, LocalDateTime.now()));
      user.setUserPlanEquityList(userPlanEquityList);

    }
    return user;
  }

  @Override
  public User getByUsername(String username) {
    return getByUsername(username, null);
  }

  @Override
  public User getByUsername(String username, Integer tenantId) {
    if (StrUtil.isBlank(username)) {
      return null;
    }
    User user = baseMapper.selectByUsername(username, tenantId);
    if (user != null) {
      user.setRoles(userRoleService.listByUserId(user.getUserId()));
      user.setAuthorities(roleMenuService.listMenuByUserId(user.getUserId(), null));
    }
    return user;
  }

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return getByUsername(username);
  }

  @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.SERIALIZABLE)
  @Override
  public boolean saveUser(User user) {
    if (StrUtil.isNotEmpty(user.getUsername()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getUsername, user.getUsername())) > 0) {
      throw new BusinessException("账号已存在");
    }
    if (StrUtil.isNotEmpty(user.getPhone()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getPhone, user.getPhone())) > 0) {
      throw new BusinessException("手机号已存在");
    }
    if (StrUtil.isNotEmpty(user.getEmail()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getEmail, user.getEmail())) > 0) {
      throw new BusinessException("邮箱已存在");
    }
    boolean result = baseMapper.insert(user) > 0;
    if (result && user.getRoles() != null && user.getRoles().size() > 0) {
      List<Integer> roleIds = user.getRoles().stream().map(Role::getRoleId).collect(Collectors.toList());
      if (userRoleService.saveBatch(user.getUserId(), roleIds) < roleIds.size()) {
        throw new BusinessException("用户角色添加失败");
      }
    }
    return result;
  }

  @Transactional(rollbackFor = {Exception.class})
  @Override
  public boolean updateUser(User user) {
    if (StrUtil.isNotEmpty(user.getUsername()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getUsername, user.getUsername())
      .ne(User::getUserId, user.getUserId())) > 0) {
      throw new BusinessException("账号已存在");
    }
    if (StrUtil.isNotEmpty(user.getPhone()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getPhone, user.getPhone())
      .ne(User::getUserId, user.getUserId())) > 0) {
      throw new BusinessException("手机号已存在");
    }
    if (StrUtil.isNotEmpty(user.getEmail()) && baseMapper.selectCount(new LambdaQueryWrapper<User>()
      .eq(User::getEmail, user.getEmail())
      .ne(User::getUserId, user.getUserId())) > 0) {
      throw new BusinessException("邮箱已存在");
    }
    boolean result = baseMapper.updateById(user) > 0;
    if (result && user.getRoles() != null && user.getRoles().size() > 0) {
      userRoleService.remove(new LambdaUpdateWrapper<UserRole>().eq(UserRole::getUserId, user.getUserId()));
      List<Integer> roleIds = user.getRoles().stream().map(Role::getRoleId).collect(Collectors.toList());
      if (userRoleService.saveBatch(user.getUserId(), roleIds) < roleIds.size()) {
        throw new BusinessException("用户角色添加失败");
      }
    }
    return result;
  }

  @Override
  public boolean comparePassword(String dbPassword, String inputPassword) {
    return bCryptPasswordEncoder.matches(inputPassword, dbPassword);
  }

  @Override
  public String encodePassword(String password) {
    return password == null ? null : bCryptPasswordEncoder.encode(password);
  }

  @Override
  public User getByPhone(String phone) {
    return query().eq("phone", phone).one();
  }

  @Override
  public User getByUnionId(UserParam param) {
    return param.getOne(baseMapper.getOne(param));
  }

  @Override
  public User getByOauthId(UserParam userParam) {
    return userParam.getOne(baseMapper.getOne(userParam));
  }

  @Override
  public List<User> listStatisticsRel(UserParam param) {
    List<User> list = baseMapper.selectListStatisticsRel(param);
    return list;
  }

  /**
   * 更新用户信息(跨租户)
   *
   * @param user 用户信息
   */
  @Override
  public void updateByUserId(User user) {
    baseMapper.updateByUserId(user);
  }

  @Override
  public boolean updateBalanceByUserId(Integer userId, BigDecimal balance) {
    UserParam userParam = new UserParam();
    userParam.setUserId(userId);
    userParam.setBalance(balance);
    int updated = baseMapper.updateBalanceByUserId(userParam);
    return updated > 0;
  }

  /**
   * 批量查询用户的角色
   *
   * @param users 用户集合
   */
  private void selectUserRoles(List<User> users) {
    if (users != null && users.size() > 0) {
      List<Integer> userIds = users.stream().map(User::getUserId).collect(Collectors.toList());
      List<Role> userRoles = userRoleService.listByUserIds(userIds);
      for (User user : users) {
        List<Role> roles = userRoles.stream().filter(d -> user.getUserId().equals(d.getUserId()))
          .collect(Collectors.toList());
        user.setRoles(roles);
      }
    }
  }

  /**
   * 批量查询用户的详细资料
   *
   * @param users 用户集合
   */
  private void selectUserProfile(List<User> users) {
    if (users != null && users.size() > 0) {
      Set<Integer> userIds = users.stream().map(User::getUserId).collect(Collectors.toSet());
      final UserProfileParam userProfileParam = new UserProfileParam();
      userProfileParam.setUserIds(userIds);
      final List<UserProfile> userProfiles = userProfileService.listRel(userProfileParam);
      final Map<Integer, List<UserProfile>> collect = userProfiles.stream().collect(Collectors.groupingBy(UserProfile::getUserId));
      for (User user: users){
        final List<UserProfile> list = collect.get(user.getUserId());
        if (!CollectionUtils.isEmpty(list)) {
          final UserProfile profile = collect.get(user.getUserId()).get(0);
          user.setUserProfile(profile);
        }
      }
    }
  }

}
