/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package com.fjhx.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
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.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fjhx.base.Condition;
import com.fjhx.mapper.TenantMapper;
import com.fjhx.params.TenantVo;
import com.fjhx.service.*;
import com.fjhx.utils.Assert;
import com.fjhx.utils.JwtUtil;
import lombok.AllArgsConstructor;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.ParamCache;
import org.springblade.system.entity.*;
import org.springblade.system.user.entity.User;
import org.springblade.system.user.entity.UserDept;
import org.springblade.system.user.enums.UserEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static org.springblade.common.constant.TenantConstant.*;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;

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

    private final IRoleService roleService;
    private final IDeptService deptService;
    private final IPostService postService;
    private final IRoleMenuService roleMenuService;
    private final IDictBizService dictBizService;
    private final IMenuService menuService;
    private final IUserService userService;
    private final IUserDeptService userDeptService;


    private final ExRegionService exRegionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void submitTenant(Tenant tenant) {
        if (Func.isEmpty(tenant.getId())) {

            String tenantId = tenant.getTenantId();
            Assert.notEmpty(tenantId, "租户id不能为空");
            Long count = count(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenant));
            Assert.eqZero(count, "租户id已存在");
            tenant.setTenantId(tenantId);

            // 获取参数配置的账号额度
            // int accountNumber = Func.toInt(ParamCache.getValue(ACCOUNT_NUMBER_KEY), DEFAULT_ACCOUNT_NUMBER);
            // tenant.setAccountNumber(accountNumber);

            // 新建租户对应的默认角色
            Long roleId = createRole(tenantId);

            // 新建租户对应的角色菜单权限
            createRoleMenus(roleId);

            // 新建租户对应的默认部门
            Long deptId = createDept(tenantId, tenant.getTenantName());

            // 新建租户对应的默认岗位
            Long postId = createPost(tenantId);

            // 新建租户对应的默认业务字典
            createDictBizList(tenantId);

            // 新建租户对应的默认管理用户
            createUser(tenantId, "admin", "admin", "admin",
                    Func.toStr(ParamCache.getValue(PASSWORD_KEY), DEFAULT_PASSWORD), roleId, deptId, postId);

            save(tenant);


        } else {
            CacheUtil.clear(SYS_CACHE, tenant.getTenantId());
            saveOrUpdate(tenant);
        }
    }

    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {

        LambdaQueryWrapper<Tenant> wrapper = Wrappers.lambdaQuery();

        String tenantId = condition.getStr("tenantId");
        String tenantName = condition.getStr("tenantName");
        String linkman = condition.getStr("linkman");
        String contactNumber = condition.getStr("contactNumber");
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Date expireBeginTime = condition.getBeginTime("expireBeginTime");
        Date expireEndTime = condition.getBeginTime("expireEndTime");
        String countryId = condition.getStr("countryId");
        String provinceId = condition.getStr("provinceId");
        String cityId = condition.getStr("cityId");

        wrapper.like(ObjectUtil.isNotEmpty(tenantId), Tenant::getTenantId, tenantId)
                .like(ObjectUtil.isNotEmpty(tenantName), Tenant::getTenantName, tenantName)
                .like(ObjectUtil.isNotEmpty(linkman), Tenant::getLinkman, linkman)
                .like(ObjectUtil.isNotEmpty(contactNumber), Tenant::getContactNumber, contactNumber)
                .between(ObjectUtil.isNotEmpty(beginTime), BaseEntity::getCreateTime, beginTime, endTime)
                .between(ObjectUtil.isNotEmpty(expireBeginTime), Tenant::getExpireTime, expireBeginTime, expireEndTime)
                .eq(ObjectUtil.isNotEmpty(countryId), Tenant::getCountryId, countryId)
                .eq(ObjectUtil.isNotEmpty(provinceId), Tenant::getProvinceId, provinceId)
                .eq(ObjectUtil.isNotEmpty(cityId), Tenant::getCityId, cityId);


        Page<Map<String, Object>> page = pageMaps(condition.getPage(), wrapper);
        List<Map<String, Object>> records = page.getRecords();

        // 省市县id集合
        Set<String> regionSet = new HashSet<>();

        for (Map<String, Object> record : records) {
            // 查询国省市
            regionSet.add(Convert.toStr(record.get("countryId")));
            regionSet.add(Convert.toStr(record.get("provinceId")));
            regionSet.add(Convert.toStr(record.get("cityId")));
        }

        Map<String, String> regionMap = exRegionService.getChineseNameById(regionSet);

        for (Map<String, Object> record : records) {
            // 赋值国省市
            record.put("countryName", regionMap.get(Convert.toStr(record.get("countryId"))));
            record.put("provinceName", regionMap.get(Convert.toStr(record.get("provinceId"))));
            record.put("cityName", regionMap.get(Convert.toStr(record.get("cityId"))));
        }

        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public TenantVo thirdPartyCreate(String token) {
        TenantVo tenantVo = JwtUtil.parseToken(token, TenantVo.class);

        String tenantId = tenantVo.getTenantId();
        Assert.notEmpty(tenantId, "租户id不能为空");

        String tenantName = tenantVo.getTenantName();
        Assert.notEmpty(tenantName, "租户名称不能为空");

        String account = tenantVo.getAccount();
        Assert.notEmpty(account, "用户账号不能为空");

        tenantVo.setLinkman(tenantVo.getLinkman() == null ? tenantName : tenantVo.getLinkman());
        tenantVo.setName(tenantVo.getName() == null ? tenantName : tenantVo.getName());
        tenantVo.setRealName(tenantVo.getRealName() == null ? tenantName : tenantVo.getRealName());
        tenantVo.setPassword(tenantVo.getPassword() == null ? RandomUtil.randomString(6) : tenantVo.getPassword());

        Tenant tenant = BeanUtil.toBean(tenantVo, Tenant.class);
        Long count = count(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenant));
        Assert.eqZero(count, "租户id已存在");

        // 指定账号额度20
        tenant.setAccountNumber(20);

        // 新建租户对应的默认角色
        Long roleId = createRole(tenantId);

        // 新建租户对应的角色菜单权限
        createRoleMenus(roleId);

        // 新建租户对应的默认部门
        Long deptId = createDept(tenantId, tenant.getTenantName());

        // 新建租户对应的默认岗位
        Long postId = createPost(tenantId);

        // 新建租户对应的默认业务字典
        createDictBizList(tenantId);

        createUser(tenantId, tenantVo.getName(), tenantVo.getRealName(), account,
                tenantVo.getPassword(), roleId, deptId, postId);

        save(tenant);

        return tenantVo;
    }

    /**
     * 新建租户对应的默认角色
     *
     * @param tenantId 租户id
     * @return 角色id
     */
    private Long createRole(String tenantId) {
        Role role = new Role();
        role.setTenantId(tenantId);
        role.setParentId(BladeConstant.TOP_PARENT_ID);
        role.setRoleName("管理员");
        role.setRoleAlias("admin");
        role.setSort(2);
        role.setIsDeleted(BladeConstant.DB_NOT_DELETED);
        roleService.save(role);
        return role.getId();
    }

    /**
     * 新建租户对应的角色菜单权限
     *
     * @param roleId 角色id
     */
    private void createRoleMenus(Long roleId) {
        LinkedList<Menu> userMenus = new LinkedList<>();
        // 获取参数配置的默认菜单集合，逗号隔开
        List<String> menuCodes = Func.toStrList(ParamCache.getValue(ACCOUNT_MENU_CODE_KEY));
        List<Menu> menus = getMenus((menuCodes.size() > 0 ? menuCodes : MENU_CODES), userMenus);
        List<RoleMenu> roleMenus = new ArrayList<>();
        menus.forEach(menu -> {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setMenuId(menu.getId());
            roleMenu.setRoleId(roleId);
            roleMenus.add(roleMenu);
        });
        roleMenuService.saveBatch(roleMenus);
    }


    /**
     * 新建租户对应的默认部门
     *
     * @param tenantId   租户id
     * @param tenantName 租户名称
     * @return 部门id
     */
    private Long createDept(String tenantId, String tenantName) {
        Dept dept = new Dept();
        dept.setTenantId(tenantId);
        dept.setParentId(BladeConstant.TOP_PARENT_ID);
        dept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
        dept.setDeptName(tenantName);
        dept.setFullName(tenantName);
        dept.setDeptCategory(1);
        dept.setSort(2);
        dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
        deptService.save(dept);
        return dept.getId();
    }

    /**
     * 新建租户对应的默认岗位
     *
     * @param tenantId 租户id
     * @return 岗位id
     */
    private Long createPost(String tenantId) {
        Post post = new Post();
        post.setTenantId(tenantId);
        post.setCategory(1);
        post.setPostCode("ceo");
        post.setPostName("首席执行官");
        post.setSort(1);
        postService.save(post);
        return post.getId();
    }

    /**
     * createDictBizs
     *
     * @param tenantId 租户id
     */
    private void createDictBizList(String tenantId) {
        // 新建租户对应的默认业务字典
        List<DictBiz> dictBizList = getDictBizs(tenantId, new LinkedList<>());
        dictBizService.saveBatch(dictBizList);
    }

    /**
     * 新建租户对应的默认管理用户
     *
     * @param tenantId 租户id
     * @param name     昵称
     * @param realName 真名
     * @param account  账号
     * @param password 密码
     * @param roleId   角色id
     * @param deptId   部门id
     * @param postId   岗位id
     */
    private void createUser(String tenantId, String name, String realName, String account, String password,
                            Long roleId, Long deptId, Long postId) {
        User user = new User();
        user.setTenantId(tenantId);
        user.setName(name);
        user.setRealName(realName);
        user.setAccount(account);
        user.setPassword(DigestUtil.encrypt(password));
        user.setRoleId(String.valueOf(roleId));
        user.setDeptId(String.valueOf(deptId));
        user.setPostId(String.valueOf(postId));
        user.setBirthday(new Date());
        user.setSex(1);
        user.setUserType(UserEnum.WEB.getCategory());
        user.setIsDeleted(BladeConstant.DB_NOT_DELETED);
        userService.save(user);

        UserDept userDept = new UserDept();
        userDept.setUserId(user.getId());
        userDept.setDeptId(deptId);
        userDeptService.save(userDept);
    }


    private List<Menu> getMenus(List<String> codes, LinkedList<Menu> menus) {
        codes.forEach(code -> {
            Menu menu = menuService.getOne(Wrappers.<Menu>query().lambda().eq(Menu::getCode, code).eq(Menu::getIsDeleted, BladeConstant.DB_NOT_DELETED));
            if (menu != null) {
                menus.add(menu);
                recursionMenu(menu.getId(), menus);
            }
        });
        return menus;
    }

    private void recursionMenu(Long parentId, LinkedList<Menu> menus) {
        List<Menu> menuList = menuService.list(Wrappers.<Menu>query().lambda().eq(Menu::getParentId, parentId).eq(Menu::getIsDeleted, BladeConstant.DB_NOT_DELETED));
        menus.addAll(menuList);
        menuList.forEach(menu -> recursionMenu(menu.getId(), menus));
    }

    private List<DictBiz> getDictBizs(String tenantId, LinkedList<DictBiz> dictBizs) {
        List<DictBiz> dictBizList = dictBizService.list(Wrappers.<DictBiz>query().lambda().eq(DictBiz::getParentId, BladeConstant.TOP_PARENT_ID).eq(DictBiz::getIsDeleted, BladeConstant.DB_NOT_DELETED));
        dictBizList.forEach(dictBiz -> {
            Long oldParentId = dictBiz.getId();
            Long newParentId = IdWorker.getId();
            dictBiz.setId(newParentId);
            dictBiz.setTenantId(tenantId);
            dictBizs.add(dictBiz);
            recursionDictBiz(tenantId, oldParentId, newParentId, dictBizs);
        });
        return dictBizs;
    }

    private void recursionDictBiz(String tenantId, Long oldParentId, Long newParentId, LinkedList<DictBiz> dictBizs) {
        List<DictBiz> dictBizList = dictBizService.list(Wrappers.<DictBiz>query().lambda().eq(DictBiz::getParentId, oldParentId).eq(DictBiz::getIsDeleted, BladeConstant.DB_NOT_DELETED));
        dictBizList.forEach(dictBiz -> {
            Long oldSubParentId = dictBiz.getId();
            Long newSubParentId = IdWorker.getId();
            dictBiz.setId(newSubParentId);
            dictBiz.setTenantId(tenantId);
            dictBiz.setParentId(newParentId);
            dictBizs.add(dictBiz);
            recursionDictBiz(tenantId, oldSubParentId, newSubParentId, dictBizs);
        });
    }

}
