package com.dsk.system.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dsk.common.constant.CacheNames;
import com.dsk.common.constant.Constants;
import com.dsk.common.constant.TenantConstants;
import com.dsk.common.core.domain.PageQuery;
import com.dsk.common.core.domain.entity.SysDictData;
import com.dsk.common.core.domain.entity.SysDictType;
import com.dsk.common.core.page.TableDataInfo;
import com.dsk.common.enums.UserStatus;
import com.dsk.common.exception.ServiceException;
import com.dsk.common.utils.PasswordUtils;
import com.dsk.common.utils.StringUtils;
import com.dsk.common.utils.redis.RedisUtils;
import com.dsk.system.domain.*;
import com.dsk.system.domain.bo.SysTenantAdminBo;
import com.dsk.system.domain.bo.SysTenantBo;
import com.dsk.system.domain.vo.SysTenantVo;
import com.dsk.system.mapper.*;
import com.dsk.system.service.ISysTenantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 企业Service业务层处理
 *
 * @author sxk
 * @date 2023.08.22
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class ISysTenantServiceImpl implements ISysTenantService {

    private final SysTenantMapper baseMapper;
    private final SysTenantPackageMapper tenantPackageMapper;
    private final SysRoleMapper roleMapper;
    private final SysRoleMenuMapper roleMenuMapper;
    private final SysDeptMapper deptMapper;
    private final SysRoleDeptMapper roleDeptMapper;
    private final SysUserMapper userMapper;
    private final SysUserRoleMapper userRoleMapper;
    private final SysDictTypeMapper dictTypeMapper;
    private final SysDictDataMapper dictDataMapper;
    private final SysConfigMapper configMapper;

    /**
     * 查询租户列表
     */
    @Override
    public TableDataInfo<SysTenantVo> queryPageList(SysTenantBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysTenant> lqw = buildQueryWrapper(bo);
        pageQuery.setIsAsc("desc");
        pageQuery.setOrderByColumn("createTime");
        Page<SysTenantVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        for (SysTenantVo sysTenantVo : result.getRecords()) {
            SysTenantPackage sysTenantPackage = tenantPackageMapper.selectById(sysTenantVo.getPackageId());
            sysTenantVo.setPackageName(sysTenantPackage.getPackageName());
        }
        return TableDataInfo.build(result);
    }

    /**
     * 禁用所有已过期租户
     */
    @Override
    public void handleExpiredTenant() {
        Date now = new Date();
        //查询所有已过期租户
        List<SysTenant> tenantList = baseMapper.selectList(new LambdaQueryWrapper<SysTenant>()
                .eq(SysTenant::getStatus, TenantConstants.NORMAL)
                .gt(SysTenant::getStartTime, now)
                .or()
                .lt(SysTenant::getExpireTime, now));
        //批量禁用已过期租户
        if (!tenantList.isEmpty()) {
            tenantList.forEach(sysTenant -> sysTenant.setStatus(TenantConstants.DISABLE));
            baseMapper.updateBatchById(tenantList);
        }
    }

//    /**
//     * 查询租户列表
//     */
//    @Override
//    public List<SysTenantVo> queryList(SysTenantBo bo) {
//        LambdaQueryWrapper<SysTenant> lqw = buildQueryWrapper(bo);
//        return baseMapper.selectVoList(lqw);
//    }

    /**
     * 基于租户ID查询租户
     */
    @Cacheable(cacheNames = CacheNames.SYS_TENANT, key = "#tenantId")
    @Override
    public SysTenantVo queryByTenantId(String tenantId) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<SysTenant>().eq(SysTenant::getTenantId, tenantId));
    }

    @Override
    public List<SysTenantVo> selectTenantList(String username) {
        return baseMapper.selectTenantList(username);
    }

    private LambdaQueryWrapper<SysTenant> buildQueryWrapper(SysTenantBo bo) {
        LambdaQueryWrapper<SysTenant> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId());
        lqw.like(StringUtils.isNotBlank(bo.getContactUserName()), SysTenant::getContactUserName, bo.getContactUserName());
        lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), SysTenant::getContactPhone, bo.getContactPhone());
        lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), SysTenant::getCompanyName, bo.getCompanyName());
        lqw.eq(StringUtils.isNotBlank(bo.getLicenseNumber()), SysTenant::getLicenseNumber, bo.getLicenseNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), SysTenant::getAddress, bo.getAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getIntro()), SysTenant::getIntro, bo.getIntro());
        lqw.like(StringUtils.isNotBlank(bo.getDomain()), SysTenant::getDomain, bo.getDomain());
        lqw.eq(bo.getPackageId() != null, SysTenant::getPackageId, bo.getPackageId());
        lqw.eq(bo.getExpireTime() != null, SysTenant::getExpireTime, bo.getExpireTime());
        lqw.eq(bo.getAccountCount() != null, SysTenant::getAccountCount, bo.getAccountCount());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenant::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 查询租户
     */
    @Override
    public SysTenantVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 校验企业名称是否唯一
     */
    @Override
    public boolean checkCompanyNameUnique(SysTenantBo bo) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysTenant>()
                .eq(SysTenant::getCompanyName, bo.getCompanyName())
                .ne(ObjectUtil.isNotNull(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId()));
        return !exist;
    }

    /**
     * 新增租户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(SysTenantBo bo) {
        SysTenant add = BeanUtil.toBean(bo, SysTenant.class);

        // 获取所有租户编号
        List<String> tenantIds = baseMapper.selectObjs(
                new LambdaQueryWrapper<SysTenant>().select(SysTenant::getTenantId), Convert::toStr);
        String tenantId = generateTenantId(tenantIds);
        add.setTenantId(tenantId);
        boolean flag = baseMapper.insert(add) > 0;
        if (!flag) {
            throw new ServiceException("创建租户失败");
        }
        bo.setId(add.getId());

        // 根据套餐创建角色
        Long roleId = createTenantRole(tenantId, bo.getPackageId(),
                TenantConstants.TENANT_SUPER_ADMIN_ROLE_NAME,
                TenantConstants.TENANT_SUPER_ADMIN_ROLE_KEY);

        // 创建部门: 公司名是部门名称
        SysDept dept = new SysDept();
        dept.setTenantId(tenantId);
        dept.setDeptName(bo.getCompanyName());
        dept.setParentId(Constants.TOP_PARENT_ID);
        dept.setAncestors(Constants.TOP_PARENT_ID.toString());
        deptMapper.insert(dept);
        Long deptId = dept.getDeptId();

        // 角色和部门关联表
        SysRoleDept roleDept = new SysRoleDept();
        roleDept.setRoleId(roleId);
        roleDept.setDeptId(deptId);
        roleDeptMapper.insert(roleDept);

        // 创建系统用户
        SysUser user = new SysUser();
        user.setTenantId(tenantId);
        user.setPhonenumber(bo.getContactPhone());
        //默认新增用户名为手机号
        user.setUserName(bo.getContactPhone());
        user.setNickName(bo.getContactUserName());
        //生成8位随机密码
        String password = PasswordUtils.generatePwd(8);
        user.setPassword(BCrypt.hashpw(password));
        user.setDeptId(deptId);
        user.setCreateTime(new DateTime());
        userMapper.insert(user);
        //新增系统用户后，默认当前用户为部门的负责人
        SysDept sd = new SysDept();
        sd.setLeader(String.valueOf(user.getUserId()));
        sd.setDeptId(deptId);
        deptMapper.updateById(sd);

        // 用户和角色关联表
        SysUserRole userRole = new SysUserRole();
        userRole.setUserId(user.getUserId());
        userRole.setRoleId(roleId);
        userRoleMapper.insert(userRole);

        String defaultTenantId = TenantConstants.DEFAULT_TENANT_ID;
        List<SysDictType> dictTypeList = dictTypeMapper.selectList(
                new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getTenantId, defaultTenantId));
        List<SysDictData> dictDataList = dictDataMapper.selectList(
                new LambdaQueryWrapper<SysDictData>().eq(SysDictData::getTenantId, defaultTenantId));
        for (SysDictType dictType : dictTypeList) {
            dictType.setDictId(null);
            dictType.setTenantId(tenantId);
        }
        for (SysDictData dictData : dictDataList) {
            dictData.setDictCode(null);
            dictData.setTenantId(tenantId);
        }
        dictTypeMapper.insertBatch(dictTypeList);
        dictDataMapper.insertBatch(dictDataList);

        List<SysConfig> sysConfigList = configMapper.selectList(
                new LambdaQueryWrapper<SysConfig>()
                        .eq(SysConfig::getTenantId, defaultTenantId)
                        .eq(SysConfig::getConfigType, "Y"));
        for (SysConfig config : sysConfigList) {
            config.setConfigId(null);
            config.setTenantId(tenantId);
        }
        configMapper.insertBatch(sysConfigList);

        ////此处暂用钉钉机器人模拟发送短信
        //String content = "【短信通知】："
        //        + bo.getContactUserName()
        //        + ",您好，您已经成功开通数字化经营管理系统，请使用手机号码登录，初始密码为"
        //        + password
        //        + "。友情提示：为了您的账号安全，请勿泄露密码。";
        //DingTalkUtil.sendDingTalkMsg(content);

        //租户新增成功,发送短信通知租户
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
        map.put("company", bo.getContactUserName());
        map.put("pwd", password);
        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
        SmsResponse smsResponse = smsBlend.sendMessage(bo.getContactPhone(), "SMS_463175230", map);
        if (!"OK".equals(smsResponse.getCode())) {
            log.error("新增租户通知短信发送异常 => {}", smsResponse);
        }
        return true;
    }

    /**
     * 新增企业普通管理员账号
     *
     * @param tenantAdminBo 用户信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addTenantAdmin(SysTenantAdminBo tenantAdminBo) {
        String tenantId = tenantAdminBo.getTenantId();
        // 根据套餐创建角色
        Long roleId = createTenantRole(tenantId, tenantAdminBo.getPackageId(),
                TenantConstants.TENANT_ADMIN_ROLE_NAME,
                TenantConstants.TENANT_ADMIN_ROLE_KEY);

        //获取部门ID
        Long deptId = deptMapper.selectOne(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getTenantId, tenantId)).getDeptId();

        // 角色和部门关联表
        SysRoleDept roleDept = new SysRoleDept();
        roleDept.setRoleId(roleId);
        roleDept.setDeptId(deptId);
        roleDeptMapper.insert(roleDept);

        // 创建系统用户
        SysUser user = new SysUser();
        user.setTenantId(tenantId);
        user.setPhonenumber(tenantAdminBo.getPhonenumber());
        //默认新增用户名为手机号
        user.setUserName(tenantAdminBo.getPhonenumber());
        user.setNickName(tenantAdminBo.getNickName());
        //生成8位随机密码
        String password = PasswordUtils.generatePwd(8);
        user.setPassword(BCrypt.hashpw(password));
        user.setDeptId(deptId);
        user.setCreateTime(new DateTime());
        userMapper.insert(user);

        // 用户和角色关联表
        SysUserRole userRole = new SysUserRole();
        userRole.setUserId(user.getUserId());
        userRole.setRoleId(roleId);
        userRoleMapper.insert(userRole);

        String defaultTenantId = TenantConstants.DEFAULT_TENANT_ID;
        //List<SysDictType> dictTypeList = dictTypeMapper.selectList(
        //        new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getTenantId, defaultTenantId));
        //List<SysDictData> dictDataList = dictDataMapper.selectList(
        //        new LambdaQueryWrapper<SysDictData>().eq(SysDictData::getTenantId, defaultTenantId));
        //for (SysDictType dictType : dictTypeList) {
        //    dictType.setDictId(null);
        //    dictType.setTenantId(tenantId);
        //}
        //for (SysDictData dictData : dictDataList) {
        //    dictData.setDictCode(null);
        //    dictData.setTenantId(tenantId);
        //}
        //dictTypeMapper.insertBatch(dictTypeList);
        //dictDataMapper.insertBatch(dictDataList);

        List<SysConfig> sysConfigList = configMapper.selectList(
                new LambdaQueryWrapper<SysConfig>()
                        .eq(SysConfig::getTenantId, defaultTenantId)
                        .eq(SysConfig::getConfigType, "Y"));
        for (SysConfig config : sysConfigList) {
            config.setConfigId(null);
            config.setTenantId(tenantId);
        }
        configMapper.insertBatch(sysConfigList);

        //管理员新增成功,发送短信通知用户
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
        map.put("company", tenantAdminBo.getNickName());
        map.put("pwd", password);
        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
        SmsResponse smsResponse = smsBlend.sendMessage(tenantAdminBo.getPhonenumber(), "SMS_463175230", map);
        if (!"OK".equals(smsResponse.getCode())) {
            log.error("新增租户通知短信发送异常 => {}", smsResponse);
        }
        return true;
    }

    /**
     * 生成租户id
     *
     * @param tenantIds 已有租户id列表
     * @return 租户id
     */
    private String generateTenantId(List<String> tenantIds) {
        // 随机生成6位
        String numbers = RandomUtil.randomNumbers(6);
        // 判断是否存在，如果存在则重新生成
        if (tenantIds.contains(numbers)) {
            generateTenantId(tenantIds);
        }
        return numbers;
    }

    /**
     * 根据租户菜单创建租户角色
     *
     * @param tenantId  租户编号
     * @param packageId 租户套餐id
     * @return 角色id
     */
    private Long createTenantRole(String tenantId, Long packageId, String roleName, String roleKey) {
        // 获取租户套餐
        SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId);
        if (ObjectUtil.isNull(tenantPackage)) {
            throw new ServiceException("套餐不存在");
        }
        // 获取套餐菜单id
        List<Long> menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong);

        // 创建角色
        SysRole role = new SysRole();
        role.setTenantId(tenantId);
        role.setRoleName(roleName);
        role.setRoleKey(roleKey);
        role.setRoleSort(1);
        role.setStatus(TenantConstants.NORMAL);
        roleMapper.insert(role);
        Long roleId = role.getRoleId();

        // 创建角色菜单
        List<SysRoleMenu> roleMenus = new ArrayList<>(menuIds.size());
        menuIds.forEach(menuId -> {
            SysRoleMenu roleMenu = new SysRoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menuId);
            roleMenus.add(roleMenu);
        });
        roleMenuMapper.insertBatch(roleMenus);

        return roleId;
    }

    /**
     * 校验租户是否允许操作
     *
     * @param tenantId 租户ID
     */
    @Override
    public void checkTenantAllowed(String tenantId) {
        if (ObjectUtil.isNotNull(tenantId) && TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
            throw new ServiceException("不允许操作管理租户");
        }
    }

    /**
     * 修改租户
     */
    @Override
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId")
    public Boolean updateByBo(SysTenantBo bo) {
        SysTenant tenant = BeanUtil.toBean(bo, SysTenant.class);
        if (TenantConstants.DISABLE.equals(bo.getStatus())) {
            tenant.setExpireTime(new DateTime());
        }
        tenant.setTenantId(null);
        return baseMapper.updateById(tenant) > 0;
    }

//    /**
//     * 修改租户状态
//     *
//     * @param bo 租户信息
//     * @return 结果
//     */
//    @Override
//    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId")
//    public int updateTenantStatus(SysTenantBo bo) {
//        SysTenant tenant = MapStructUtils.convert(bo, SysTenant.class);
//        return baseMapper.updateById(tenant);
//    }

    /**
     * 校验并批量删除租户信息
     */
    @Override
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, allEntries = true)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // 做一些业务上的校验,判断是否需要校验
            if (ids.contains(TenantConstants.SUPER_ADMIN_ID)) {
                throw new ServiceException("超管租户不能删除");
            }
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean syncTenantPackage(String tenantId, Long packageId) {
        SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId);
        List<SysRole> roles = roleMapper.selectList(
                new LambdaQueryWrapper<SysRole>().eq(SysRole::getTenantId, tenantId));
        List<Long> roleIds = new ArrayList<>(roles.size() - 1);
        List<Long> menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong);
        roles.forEach(item -> {
            if (TenantConstants.TENANT_SUPER_ADMIN_ROLE_KEY.equals(item.getRoleKey())) {
                List<SysRoleMenu> roleMenus = new ArrayList<>(menuIds.size());
                menuIds.forEach(menuId -> {
                    SysRoleMenu roleMenu = new SysRoleMenu();
                    roleMenu.setRoleId(item.getRoleId());
                    roleMenu.setMenuId(menuId);
                    roleMenus.add(roleMenu);
                });
                roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, item.getRoleId()));
                roleMenuMapper.insertBatch(roleMenus);
            } else {
                roleIds.add(item.getRoleId());
            }
        });
        if (!roleIds.isEmpty()) {
            roleMenuMapper.delete(
                    new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, roleIds).notIn(!menuIds.isEmpty(), SysRoleMenu::getMenuId, menuIds));
        }
        try {
            //清除企业用户登录信息
            List<SysUser> tenantUsers = userMapper.selectList(Wrappers.<SysUser>lambdaQuery()
                    .eq(SysUser::getTenantId, tenantId).eq(SysUser::getStatus, UserStatus.OK.getCode()));
            if (!CollectionUtils.isEmpty(tenantUsers)) {
                for (SysUser tenantUser : tenantUsers) {
                    String key = "global:Authorization:login:session:00:" + tenantUser.getUserId();
                    if (RedisUtils.hasKey(key)) {
                        SaSession session = RedisUtils.getCacheObject(key);
                        List<TokenSign> tokenSignList = session.getTokenSignList();
                        tokenSignList.forEach(sign -> StpUtil.kickoutByTokenValue(sign.getValue()));
                    }
                }
            }
        } catch (Exception e) {
            log.error("清除企业用户登录信息操作失败！error={}", e.getMessage());
        }

        return true;
    }


}
