package org.sky.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.sky.core.cache.utils.CacheUtil;
import org.sky.core.literule.engine.RuleEngineExecutor;
import org.sky.core.literule.provider.LiteRuleResponse;
import org.sky.core.log.exception.ServiceException;
import org.sky.core.mp.base.BaseServiceImpl;
import org.sky.core.mp.enums.StatusType;
import org.sky.core.redis.cache.KdRedis;
import org.sky.core.secure.utils.SecureUtil;
import org.sky.core.tenant.TenantId;
import org.sky.core.tool.constant.KdConstant;
import org.sky.core.tool.jackson.JsonUtil;
import org.sky.core.tool.support.Kv;
import org.sky.core.tool.utils.DesUtil;
import org.sky.core.tool.utils.Func;
import org.sky.system.cache.RegionCache;
import org.sky.system.mapper.RegionMapper;
import org.sky.system.mapper.TenantMapper;
import org.sky.system.pojo.entity.*;
import org.sky.system.rule.context.TenantContext;
import org.sky.system.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static org.sky.common.constant.TenantConstant.DES_KEY;
import static org.sky.core.cache.constant.CacheConstant.SYS_CACHE;
import static org.sky.system.cache.SysCache.TENANT_TENANT_ID;
import static org.sky.system.rule.constant.TenantRuleConstant.TENANT_CHAIN_ID;

/**
 * 服务实现类
 *
 * @author Chill
 */
@Service
@AllArgsConstructor
public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, Tenant> implements ITenantService {

	private final TenantId tenantIdGenerator;
	private final IRoleService roleService;
	private final IMenuService menuService;
	private final IDeptService deptService;
	private final IPostService postService;
	private final IRoleMenuService roleMenuService;
	private final IDictBizService dictBizService;
	private final IUserService userService;
	private final IUserDeptService userDeptService;
	private final RuleEngineExecutor ruleExecutor;
	private final RegionMapper regionMapper;
	private final KdRedis redis;

	@Override
	public IPage<Tenant> selectTenantPage(IPage<Tenant> page, Tenant tenant) {
		List<Tenant> tenants = baseMapper.selectTenantPage(page, tenant);
		for (Tenant t : tenants) {
			if (Func.isNotEmpty(t.getDistrictCode())) {
				Region region = RegionCache.getByCode(t.getDistrictCode());
				if (region != null) {
					t.setRegionName(region.getProvinceName() + "-" + region.getCityName() + "-" + region.getDistrictName());
				}
			}
		}
		return page.setRecords(tenants);
	}

	@Override
	public Tenant getByTenantId(String tenantId) {
		return getOne(Wrappers.<Tenant>query().lambda().eq(Tenant::getTenantId, tenantId));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submitTenant(Tenant tenant) {
		if (Func.isEmpty(tenant.getId())) {
			LiteRuleResponse<TenantContext> resp = ruleExecutor.execute(
				TENANT_CHAIN_ID,
				TenantContext.builder()
					.tenantIdGenerator(tenantIdGenerator)
					.tenant(tenant)
					.menuService(menuService)
					.dictBizService(dictBizService)
					.tenantService(this)
					.build()
			);
			if (resp.isSuccess()) {
				TenantContext tenantContext = resp.getContext();
				Role role = tenantContext.getRole();
				roleService.save(role);

				Long roleId = role.getId();
				List<RoleMenu> roleMenuList = tenantContext.getRoleMenuList();
				roleMenuList.forEach(roleMenu -> roleMenu.setRoleId(roleId));
				roleMenuService.saveBatch(roleMenuList);

				Dept dept = tenantContext.getDept();
				deptService.save(dept);

				Post post = tenantContext.getPost();
				postService.save(post);

				List<DictBiz> dictBizList = tenantContext.getDictBizList();
				dictBizService.saveBatch(dictBizList);

				User user = tenantContext.getUser();
				user.setRoleId(String.valueOf(role.getId()));
				user.setDeptId(String.valueOf(dept.getId()));
				user.setPostId(String.valueOf(post.getId()));
				userService.submit(user);
			} else {
				throw new ServiceException("租户业务数据构建异常");
			}
		}
		CacheUtil.clear(SYS_CACHE, tenant.getTenantId());
		CacheUtil.evict(SYS_CACHE, TENANT_TENANT_ID, tenant.getTenantId(), Boolean.FALSE);
 		redis.del("kd::vip:years:" + tenant.getTenantId());
		return super.saveOrUpdate(tenant);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean recycleTenant(List<Long> ids) {
		List<String> tenantIds = this.list(Wrappers.<Tenant>query().lambda().in(Tenant::getId, ids))
			.stream().map(tenant -> Func.toStr(tenant.getTenantId())).distinct().collect(Collectors.toList());
		if (tenantIds.contains(KdConstant.ADMIN_TENANT_ID)) {
			throw new ServiceException("不可删除管理租户!");
		}
		int disabledType = StatusType.DISABLED.getType();
		int activeType = StatusType.ACTIVE.getType();
		boolean temp = this.changeStatus(ids, disabledType);
		if (temp) {
			// 删除角色至回收站
			roleService.update(Wrappers.<Role>update().lambda().set(Role::getStatus, disabledType).eq(Role::getStatus, activeType).in(Role::getTenantId, tenantIds));
			// 删除部门至回收站
			deptService.update(Wrappers.<Dept>update().lambda().set(Dept::getStatus, disabledType).eq(Dept::getStatus, activeType).in(Dept::getTenantId, tenantIds));
			// 删除岗位至回收站
			postService.update(Wrappers.<Post>update().lambda().set(Post::getStatus, disabledType).eq(Post::getStatus, activeType).in(Post::getTenantId, tenantIds));
			// 删除业务字典至回收站
			dictBizService.update(Wrappers.<DictBiz>update().lambda().set(DictBiz::getStatus, disabledType).eq(DictBiz::getStatus, activeType).in(DictBiz::getTenantId, tenantIds));
			// 获取需要删除的用户主键集合
			List<Long> userIds = userService.list(Wrappers.<User>query().lambda().eq(User::getStatus, activeType).in(User::getTenantId, tenantIds)
				.select(User::getId)).stream().map(User::getId).collect(Collectors.toList());
			// 删除用户部门及拓展部分至回收站
			userService.update(Wrappers.<User>update().lambda().set(User::getStatus, disabledType).in(User::getId, userIds));
			userDeptService.update(Wrappers.<UserDept>update().lambda().set(UserDept::getStatus, disabledType).in(UserDept::getUserId, userIds));
			// 删除用户自定义部分至回收站
			new UserOauth().update(Wrappers.<UserOauth>update().lambda().set(UserOauth::getStatus, disabledType).in(UserOauth::getUserId, userIds));
			new UserWeb().update(Wrappers.<UserWeb>update().lambda().set(UserWeb::getStatus, disabledType).in(UserWeb::getUserId, userIds));
			new UserApp().update(Wrappers.<UserApp>update().lambda().set(UserApp::getStatus, disabledType).in(UserApp::getUserId, userIds));
			new UserOther().update(Wrappers.<UserOther>update().lambda().set(UserOther::getStatus, disabledType).in(UserOther::getUserId, userIds));
			CacheUtil.clear(SYS_CACHE, tenantIds);
			return true;
		} else {
			throw new ServiceException("删除租户失败!");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean passTenant(List<Long> ids) {
		List<String> tenantIds = this.list(Wrappers.<Tenant>query().lambda().in(Tenant::getId, ids))
			.stream().map(tenant -> Func.toStr(tenant.getTenantId())).distinct().collect(Collectors.toList());
		int disabledType = StatusType.DISABLED.getType();
		int activeType = StatusType.ACTIVE.getType();
		boolean temp = this.changeStatus(ids, activeType);
		if (temp) {
			// 恢复角色至正常状态
			roleService.update(Wrappers.<Role>update().lambda().set(Role::getStatus, activeType).eq(Role::getStatus, disabledType).in(Role::getTenantId, tenantIds));
			// 恢复部门至正常状态
			deptService.update(Wrappers.<Dept>update().lambda().set(Dept::getStatus, activeType).eq(Dept::getStatus, disabledType).in(Dept::getTenantId, tenantIds));
			// 恢复岗位至正常状态
			postService.update(Wrappers.<Post>update().lambda().set(Post::getStatus, activeType).eq(Post::getStatus, disabledType).in(Post::getTenantId, tenantIds));
			// 恢复业务字典至正常状态
			dictBizService.update(Wrappers.<DictBiz>update().lambda().set(DictBiz::getStatus, activeType).eq(DictBiz::getStatus, disabledType).in(DictBiz::getTenantId, tenantIds));
			// 获取需要恢复的用户主键集合
			List<Long> userIds = userService.list(Wrappers.<User>query().lambda().eq(User::getStatus, disabledType).in(User::getTenantId, tenantIds)
				.select(User::getId)).stream().map(User::getId).collect(Collectors.toList());
			// 恢复用户部门及拓展部分至正常状态
			userService.update(Wrappers.<User>update().lambda().set(User::getStatus, activeType).in(User::getId, userIds));
			userDeptService.update(Wrappers.<UserDept>update().lambda().set(UserDept::getStatus, activeType).in(UserDept::getUserId, userIds));
			// 恢复用户自定义部分至正常状态
			new UserOauth().update(Wrappers.<UserOauth>update().lambda().set(UserOauth::getStatus, activeType).in(UserOauth::getUserId, userIds));
			new UserWeb().update(Wrappers.<UserWeb>update().lambda().set(UserWeb::getStatus, activeType).in(UserWeb::getUserId, userIds));
			new UserApp().update(Wrappers.<UserApp>update().lambda().set(UserApp::getStatus, activeType).in(UserApp::getUserId, userIds));
			new UserOther().update(Wrappers.<UserOther>update().lambda().set(UserOther::getStatus, activeType).in(UserOther::getUserId, userIds));
			CacheUtil.clear(SYS_CACHE, tenantIds);
			return true;
		} else {
			throw new ServiceException("恢复租户失败!");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeTenant(List<Long> ids) {
		List<String> tenantIds = this.list(Wrappers.<Tenant>query().lambda().in(Tenant::getId, ids))
			.stream().map(tenant -> Func.toStr(tenant.getTenantId())).distinct().collect(Collectors.toList());
		if (tenantIds.contains(KdConstant.ADMIN_TENANT_ID)) {
			throw new ServiceException("不可删除管理租户!");
		}
		boolean temp = this.deleteLogic(ids);
		if (temp) {
			// 删除角色不可再恢复
			roleService.remove(Wrappers.<Role>query().lambda().in(Role::getTenantId, tenantIds));
			// 删除部门不可再恢复
			deptService.remove(Wrappers.<Dept>query().lambda().in(Dept::getTenantId, tenantIds));
			// 删除岗位不可再恢复
			postService.remove(Wrappers.<Post>query().lambda().in(Post::getTenantId, tenantIds));
			// 删除业务字典不可再恢复
			dictBizService.remove(Wrappers.<DictBiz>query().lambda().in(DictBiz::getTenantId, tenantIds));
			// 获取需要删除的用户主键集合
			List<Long> userIds = userService.list(Wrappers.<User>query().lambda().in(User::getTenantId, tenantIds)
				.select(User::getId)).stream().map(User::getId).collect(Collectors.toList());
			// 删除用户部门及拓展部分不可再恢复
			userService.removeByIds(userIds);
			userDeptService.remove(Wrappers.<UserDept>query().lambda().in(UserDept::getUserId, userIds));
			// 删除用户自定义部分不可再恢复
			new UserOauth().delete(Wrappers.<UserOauth>query().lambda().in(UserOauth::getUserId, userIds));
			new UserWeb().delete(Wrappers.<UserWeb>query().lambda().in(UserWeb::getUserId, userIds));
			new UserApp().delete(Wrappers.<UserApp>query().lambda().in(UserApp::getUserId, userIds));
			new UserOther().delete(Wrappers.<UserOther>query().lambda().in(UserOther::getUserId, userIds));
			CacheUtil.clear(SYS_CACHE, tenantIds);
			return true;
		} else {
			throw new ServiceException("删除租户失败!");
		}
	}

	@Override
	public boolean setting(Integer accountNumber, Date expireTime, String ids) {
		List<String> tenantIds = this.list(Wrappers.<Tenant>query().lambda().in(Tenant::getId, ids))
			.stream().map(tenant -> Func.toStr(tenant.getTenantId())).distinct().toList();
		tenantIds.forEach(tenantId -> {
			CacheUtil.clear(SYS_CACHE, tenantId);
			CacheUtil.evict(SYS_CACHE, TENANT_TENANT_ID, tenantId, Boolean.FALSE);
		});
		Func.toLongList(ids).forEach(id -> {
			Kv kv = Kv.create().set("accountNumber", accountNumber).set("expireTime", expireTime).set("id", id);
			String licenseKey = DesUtil.encryptToHex(JsonUtil.toJson(kv), DES_KEY);
			update(
				Wrappers.<Tenant>update().lambda()
					.set(Tenant::getAccountNumber, accountNumber)
					.set(Tenant::getExpireTime, expireTime)
					.set(Tenant::getLicenseKey, licenseKey)
					.eq(Tenant::getId, id)
			);
		});
		return true;
	}

	@Override
	public List<String> getVipYears() {
 		return CacheUtil.get("kd", "vip:years:", SecureUtil.getTenantId(), () -> {
			Tenant tenant = this.baseMapper.selectOne(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, SecureUtil.getTenantId()));
			if (Func.notNull(tenant) && Func.isNotBlank(tenant.getVipYears())) {
				return JSON.parseArray(tenant.getVipYears(), String.class);
			}
			return null;
		}, Boolean.FALSE);
	}

}
