package org.dromara.system.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.constant.OssConstant;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.mapper.*;
import org.dromara.system.service.ISysTenantService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 租户Service业务层处理
 *
 * @author Michelle.Chung
 */
@RequiredArgsConstructor
@Service
public class SysTenantServiceImpl implements ISysTenantService {

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

    @Value("${login.address.user}")
    private String userLoginAddress;

    @Value("${login.address.management}")
    private String managementLoginAddress;

    /**
     * 查询租户
     */
    @Override
    public SysTenantVo queryById(Long id) {
        SysTenantVo vo = baseMapper.selectVoById(id);
        if (StringUtils.isNotBlank(vo.getAgreementFileUrl())) {
            // json字符串转协议文件对象
            vo.setAgreementFileList(JSONUtil.toList(vo.getAgreementFileUrl(), SysOssUploadVo.class));
        }
        return vo;
    }

    /**
     * 基于租户ID查询租户
     */
    @Override
    public SysTenantVo queryByTenantId(String tenantId) {
        // 租户信息查询
        SysTenantVo vo = baseMapper.queryByTenantId(tenantId);
        if (StringUtils.isNotBlank(vo.getAgreementFileUrl())) {
            // json字符串转协议文件对象
            vo.setAgreementFileList(JSONUtil.toList(vo.getAgreementFileUrl(), SysOssUploadVo.class));
        }
        if (StringUtils.isNotBlank(vo.getOrganizationLogoStr())) {
            // logo字符串转list
            vo.setOrganizationLogo(JSONUtil.toList(vo.getOrganizationLogoStr(), SysOssUploadVo.class));
        }
        return vo;
    }

    /**
     * 查询租户列表
     */
    @Override
    public TableDataInfo<SysTenantVo> queryPageList(SysTenantBo bo, PageQuery pageQuery) {
       /* LambdaQueryWrapper<SysTenant> lqw = buildQueryWrapper(bo);
        Page<SysTenantVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);*/
        // 当前登录人非超管
        if(!TenantHelper.getTenantId().equals(TenantConstants.DEFAULT_TENANT_ID)){
            bo.setTenantId(TenantHelper.getTenantId());
        }
        Wrapper<SysTenant> wrapper = buildQueryWrapperOld(bo);
        Page<SysTenantVo> result = baseMapper.selectVoPageList(pageQuery.build(), wrapper);
        return TableDataInfo.build(result);
    }

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

    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.like(StringUtils.isNotBlank(bo.getTenantOrganization()), SysTenant::getTenantOrganization, bo.getTenantOrganization());
        lqw.eq(bo.getPackageId() != null, SysTenant::getPackageId, bo.getPackageId());
        lqw.eq(bo.getAccountCount() != null, SysTenant::getAccountCount, bo.getAccountCount());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenant::getStatus, bo.getStatus());
        lqw.orderByDesc(SysTenant::getId);
        return lqw;
    }

    private Wrapper<SysTenant> buildQueryWrapperOld(SysTenantBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<SysTenant> wrapper = Wrappers.query();
        wrapper.eq("t.del_flag", UserConstants.USER_NORMAL)
            .eq(StringUtils.isNotBlank(bo.getTenantId()),"t.tenant_id",bo.getTenantId())
            .like(ObjectUtil.isNotNull(bo.getCompanyName()), "t.company_name", bo.getCompanyName())
            .like(StringUtils.isNotBlank(bo.getTenantOrganization()), "t.tenant_organization", bo.getTenantOrganization())
            .like(StringUtils.isNotBlank(bo.getOrganizationName()), "o.organization_name", bo.getOrganizationName())
            .orderByDesc("t.id");
        return wrapper;
    }

    /**
     * 新增租户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(SysTenantBo bo) {

        SysTenant add = MapstructUtils.convert(bo, SysTenant.class);
        // 获取所有已有租户编号
        List<String> tenantIds = baseMapper.selectObjs(
            new LambdaQueryWrapper<SysTenant>().select(SysTenant::getTenantId), x -> {
                return Convert.toStr(x);
            });
        // 生成新的租户编号
        String tenantId = generateTenantId(tenantIds);
        add.setTenantId(tenantId);

        if (bo.getAgreementFileList() != null && bo.getAgreementFileList().size() > 0) {
            // 协议文件列表转json字符串
            String jsonStr = JSONUtil.toJsonStr(bo.getAgreementFileList());
            add.setAgreementFileUrl(jsonStr);
        }
        // 客户端登录地址
        add.setAppAccessPath(userLoginAddress + "?tenantId=" + tenantId);
        // 学会管理端地址
        add.setPcAccessPath(managementLoginAddress + "?tenantId=" + tenantId);
        // 租户信息入库
        boolean flag = baseMapper.insert(add) > 0;
        if (!flag) {
            throw new ServiceException("创建租户失败");
        }
        bo.setId(add.getId());

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

        // 创建部门: 公司名是部门名称
        /*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();
        // 用户编号
        String userCode = "H0UID" + DateUtil.format(new DateTime(), "yyyyMMddHHmmss") + "00001";
        user.setUserCode(userCode);
        // 租户ID
        user.setTenantId(tenantId);
        // 手机号作为登录用户名
        user.setUserName(bo.getContactPhone());
        // 用户姓名
        user.setNickName(bo.getContactUserName());
        // 联系电话
        user.setPhonenumber(bo.getContactPhone());
        // 设置初始密码
        user.setPassword(BCrypt.hashpw(Constants.password));
        // 协议确认状态
        user.setConfirmAgreement(UserConstants.UNCONFIRMED);
        // 默认身份-医生
        user.setLoginIdentity(UserConstants.LOGIN_IDENTITY_DOCTOR);
        //user.setDeptId(deptId);
        userMapper.insert(user);
        //新增系统用户后，默认当前用户为部门的负责人
       /* SysDept sd = new SysDept();
        sd.setLeader(user.getUserId());
        sd.setDeptId(deptId);
        deptMapper.updateById(sd);*/

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

        // 病例点评设置
        CaseCommentSetting caseCommentSetting = new CaseCommentSetting();
        caseCommentSetting.setTenantId(tenantId);
        caseCommentSetting.setCaseCommentScore(bo.getCaseCommentScore());
        caseCommentSettingMapper.insert(caseCommentSetting);

        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));
        for (SysConfig config : sysConfigList) {
            config.setConfigId(null);
            config.setTenantId(tenantId);
        }
        configMapper.insertBatch(sysConfigList);

        // 复制OSS配置信息
        List<SysOssConfig> sysOssConfigList = ossConfigMapper.selectList(
            new LambdaQueryWrapper<SysOssConfig>().eq(SysOssConfig::getTenantId, defaultTenantId));
        for (SysOssConfig config : sysOssConfigList) {
            config.setOssConfigId(null);
            config.setTenantId(tenantId);
        }
        ossConfigMapper.insertBatch(sysOssConfigList);

        // 复制SMS短信配置信息
        List<SysSmsConfig> SysSmsConfigList = smsConfigMapper.selectList(
            new LambdaQueryWrapper<SysSmsConfig>().eq(SysSmsConfig::getTenantId, defaultTenantId));
        for (SysSmsConfig config : SysSmsConfigList) {
            config.setId(null);
            config.setTenantId(tenantId);
        }
        smsConfigMapper.insertBatch(SysSmsConfigList);

        // 设置动态租户
        TenantHelper.setDynamic(tenantId);
        // 缓存租户OSS配置
        for (SysOssConfig config : sysOssConfigList) {
            String configKey = config.getConfigKey();
            if ("0".equals(config.getStatus())) {
                RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey);
            }
            CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
        }
        // 缓存租户SMS短信配置
        for (SysSmsConfig config : SysSmsConfigList) {
            CacheUtils.put(CacheNames.SYS_SMS_CONFIG, config.getSmsCode(), JsonUtils.toJsonString(config));
        }
        TenantHelper.clearDynamic();

        return true;
    }

    /**
     * 生成租户id
     *
     * @param tenantIds 已有租户id列表
     * @return 租户id
     */
    private String generateTenantId(List<String> tenantIds) {


        // 获取当前时间并生成编号
        String dateStr = "H0" + DateUtil.format(new DateTime(), "yyyyMMddHHmm");
        String tenantId = "";
        for (int i = 1; i < 1000; i++) {
            // 编号补0
            String countStr = String.format("%03d", i);
            tenantId = dateStr + countStr;
            // 已有编号不包含新编号，跳出循环
            if (!tenantIds.contains(tenantId)) {
                break;
            }
        }
        return tenantId;
    }

    /**
     * 根据租户菜单创建租户角色
     *
     * @param tenantId  租户编号
     * @param packageId 租户套餐id
     * @return 角色id
     */
    private Long createTenantRole(String tenantId, Long packageId) {
        // 获取租户套餐
        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(TenantConstants.TENANT_ADMIN_ROLE_NAME);
        role.setRoleKey(TenantConstants.TENANT_ADMIN_ROLE_KEY);
        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;
    }

    /**
     * 修改租户
     */
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId")
    @Override
    public Boolean updateByBo(SysTenantBo bo) {
        // 租户旧信息
        SysTenant oldTenant = baseMapper.selectById(bo.getId());

        SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class);
        if (bo.getAgreementFileList() != null && bo.getAgreementFileList().size() > 0) {
            // 协议文件列表转json字符串
            String jsonStr = JSONUtil.toJsonStr(bo.getAgreementFileList());
            tenant.setAgreementFileUrl(jsonStr);
        }

        // 联系电话修改
        if (StringUtils.isNotBlank(bo.getContactPhone()) && !oldTenant.getContactPhone().equals(bo.getContactPhone())) {
            // 设置动态租户
            TenantHelper.setDynamic(bo.getTenantId());
            LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
            lqw.eq(SysUser::getPhonenumber, oldTenant.getContactPhone());
            SysUser user = userMapper.selectOne(lqw);

            // 更新租户管理员用户信息
            user.setPhonenumber(bo.getContactPhone());
            user.setUserName(bo.getContactPhone());
            user.setNickName(bo.getContactUserName());
            userMapper.updateById(user);

            // 清除动态租户
            TenantHelper.clearDynamic();
        }

        tenant.setTenantId(null);
        tenant.setPackageId(null);
        return baseMapper.updateById(tenant) > 0;
    }

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

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

    /**
     * 批量删除租户
     */
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, allEntries = true)
    @Override
    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
    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;
    }

    /**
     * 校验联系电话是否唯一
     *
     * @param bo
     */
    @Override
    public boolean checkContactPhoneUnique(SysTenantBo bo) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysTenant>()
            .eq(SysTenant::getContactPhone, bo.getContactPhone())
            .ne(ObjectUtil.isNotNull(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId()));
        return !exist;
    }


    /**
     * 同步租户套餐
     */
    @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_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));
        }
        return true;
    }

    /**
     * 主题色列表
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysTenantVo> themeColorList(SysTenantBo bo, PageQuery pageQuery) {
        Wrapper<SysTenant> wrapper = buildQueryWrapperOld(bo);
        Page<SysTenantVo> result = baseMapper.themeColorList(pageQuery.build(), wrapper);
        List<SysTenantVo> sysTenantVoList = result.getRecords();
        // 列表数据不为空
        if (sysTenantVoList != null && sysTenantVoList.size() > 0) {
            for (SysTenantVo vo : sysTenantVoList
            ) {
                if (StringUtils.isNotBlank(vo.getOrganizationLogoStr())) {
                    // logo字符串转list
                    vo.setOrganizationLogo(JSONUtil.toList(vo.getOrganizationLogoStr(), SysOssUploadVo.class));
                }
            }
        }
        return TableDataInfo.build(result);
    }

    /**
     * 租户主题色设置
     *
     * @param bo
     * @return
     */
    @Override
    public Boolean themeColor(SysTenantBo bo) {
        SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class);
        tenant.setTenantId(null);
        tenant.setPackageId(null);
        return baseMapper.updateById(tenant) > 0;
    }
}
