package com.diagbot.facade;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.diagbot.client.AuthServiceClient;
import com.diagbot.dto.GetConsoleUserInfoDTO;
import com.diagbot.dto.LoginDTO;
import com.diagbot.dto.RespDTO;
import com.diagbot.dto.UserOrgDTO;
import com.diagbot.entity.JWT;
import com.diagbot.entity.Organization;
import com.diagbot.entity.User;
import com.diagbot.entity.UserAuthentication;
import com.diagbot.entity.UserOrganization;
import com.diagbot.entity.UserRole;
import com.diagbot.entity.wrapper.MenuWrapper;
import com.diagbot.exception.CommonErrorCode;
import com.diagbot.exception.CommonException;
import com.diagbot.exception.ServiceErrorCode;
import com.diagbot.service.impl.UserServiceImpl;
import com.diagbot.util.BeanUtil;
import com.diagbot.util.EntityUtil;
import com.diagbot.util.ListUtil;
import com.diagbot.util.RegexValidateUtil;
import com.diagbot.util.StringUtil;
import com.diagbot.util.UserUtils;
import com.diagbot.vo.ImgVerVerVO;
import com.diagbot.vo.UserSaveVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 用户业务层
 * @author: gaodm
 * @time: 2018/8/6 9:00
 */
@Component
public class UserFacade extends UserServiceImpl {

    private static final String CACHE_NAME = "UserInfo";

    @Autowired
    AuthServiceClient authServiceClient;
    @Autowired
    VerFacade verFacade;
    @Autowired
    OrganizationFacade organizationFacade;
    @Autowired
    UserOrganizationFacade userOrganizationFacade;
    @Autowired
    UserRoleFacade userRoleFacade;
    @Autowired
    MenuFacade menuFacade;
    @Autowired
    UserAuthenticationFacade userAuthenticationFacade;
    @Autowired
    PermissionFacade permissionFacade;

    /**
     * 注册
     * @param userSaveVO 参数
     * @return 用户信息
     */
    public User createUser(UserSaveVO userSaveVO){
        User user = new User();
        BeanUtil.copyProperties(userSaveVO, user);
        user.setGmtCreate(new Date());
        ImgVerVerVO imgVerVerVO = new ImgVerVerVO();
        BeanUtil.copyProperties(userSaveVO, imgVerVerVO);
        User bean = findByName(user.getUsername());
        if(bean != null) {
            throw new CommonException(ServiceErrorCode.USER_EXIST);
        }
        if(!RegexValidateUtil.checkMobileNumber(userSaveVO.getUsername())) {
            throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "请输入正确的手机号!");
        }

        //TODO 测试注释了图形验证和短信验证，后面要开启
//        verFacade.verifyImgVerification(imgVerVerVO); //保存时再次校验图形验证码，主要是为了防止跳过前端校验直接调用接口

        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        String entryPassword= passwordEncoder.encode(user.getPassword());
        user.setPassword(entryPassword);
        this.save(user);

        // 机构相关业务
        doOrganization(userSaveVO, user);

        //权限相关业务
        doPermisson(userSaveVO, user);

        //用户认证业务
        doUserAuthentication(userSaveVO, user);
        return  user;
    }


    /**
     * @param page 分页参数
     * @param userSaveVO 参数
     * @return 用户列表
     */
    public RespDTO<IPage<User>> index(Page page, UserSaveVO userSaveVO){
        User user = new User();
        BeanUtil.copyProperties(userSaveVO, user);
        IPage<User> res = this.indexPage(page, user);
        return RespDTO.onSuc(res);
    }


    /**
     * 添加一条默认用户认证信息
     * @param userSaveVO 参数
     * @param user 参数
     */
    public void doUserAuthentication(UserSaveVO userSaveVO, User user) {
        UserAuthentication ua = new UserAuthentication();
        ua.setGmtCreate(new Date());
        ua.setUserId(user.getId());
        userAuthenticationFacade.save(ua);
    }


    /**
     * 如果机构已存在，只要添加用户与机构的映射即可;如果机构不存在，先添加机构信息
     * @param userSaveVO 参数
     * @param user 参数
     */
    public void doPermisson(UserSaveVO userSaveVO, User user) {
        UserRole  userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(1L);
        userRole.setGmtCreate(new Date());
        userRoleFacade.save(userRole);
    }


    /**
     * 如果机构已存在，只要添加用户与机构的映射即可;如果机构不存在，先添加机构信息
     * @param userSaveVO 参数
     * @param user 参数
     */
    public void doOrganization(UserSaveVO userSaveVO, User user) {
        Map<String, Object> paramMap = new HashMap<>();
        String name = userSaveVO.getOrganization();
        paramMap.put("name", name);
        Long orgId = 0L;
        List<Organization> list = organizationFacade.selectByMap(paramMap);
        if(ListUtil.isEmpty(list)) {
            Organization org = new Organization();
            org.setName(name);
            org.setGmtCreate(new Date());
            organizationFacade.save(org);
            orgId = org.getId();
        } else {
            orgId = list.get(0).getId();
            //查询机构下是否已存在用户信息，如已存在，就抛异常,需求待核实
            Map paramMap1 = new HashMap();
            paramMap1.put("organization_id", orgId);
            paramMap1.put("is_deleted", "N");
            List<UserOrganization> res1 = userOrganizationFacade.selectByMap(paramMap1);
            if(ListUtil.isNotEmpty(res1)) {
                throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "该机构下已存在用户，不能再添加别的用户！");
            }
        }

        UserOrganization userOrganization = new UserOrganization();
        userOrganization.setUserId(user.getId());
        userOrganization.setOrganizationId(orgId);
        userOrganization.setGmtCreate(new Date());
        userOrganizationFacade.save(userOrganization);
    }

    /**
     * 根据用户名获取用户信息
     * @param username 用户名
     * @return 用户信息
     */
    //@Cacheable(value = "UserName", key = "'username:'+#p0")
    public User  getUserInfo(String username){
        return this.findByName(username);
    }


    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     * @return 登录相关信息
     */
    public RespDTO login(String username , String password){
        User user= this.findByName(username);
        if(null==user){
            throw new CommonException(ServiceErrorCode.USER_NOT_FOUND);
        }
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        if(!passwordEncoder.matches(password,user.getPassword())){
            throw new CommonException(ServiceErrorCode.USER_PASSWORD_ERROR);
        }
        JWT jwt = authServiceClient.getToken("Basic dWFhLXNlcnZpY2U6MTIzNDU2", "password", username, password);
        // 获得用户菜单
        if(null==jwt){
            throw new CommonException(ServiceErrorCode.GET_TOKEN_FAIL);
        }
        LoginDTO loginDTO=new LoginDTO();
        //添加用户
        loginDTO.setUser(user);
        //添加token
        loginDTO.setToken(jwt.getAccess_token());
        //添加机构信息
        Organization org = organizationFacade.getById(user.getId());
        loginDTO.setOrganization(org);
        //添加菜单信息
        List<MenuWrapper> menuList = menuFacade.getByRole(user.getId());
        getMenuStruct(menuList);
        loginDTO.setMenuWrappers(menuList);
        return RespDTO.onSuc(loginDTO);
    }


    /**
     * 递归获取菜单结构
     * @param menuList 菜单参数
     */
    public void getMenuStruct(List<MenuWrapper> menuList) {
        for(MenuWrapper m : menuList) {
            m.setSubMenuList(recursion(m));
        }
    }


    /**
     * 递归查询菜单信息
     * @param m 参数
     * @return 菜单信息
     */
    public List<MenuWrapper> recursion(MenuWrapper m) {
        List<MenuWrapper> subMenu = menuFacade.getSubMenuById(m.getId());
        for (MenuWrapper sub : subMenu) {
            sub.setSubMenuList(recursion(sub));
        }
        return subMenu;
    }


    /**
     * 获取用户相关信息和机构信息
     * @param userIds 用户ids
     * @return 用户相关信息和机构信息
     */
    public Map<Long, UserOrgDTO> getUserAndOrg(List<Long> userIds) {
        Map<Long, UserOrgDTO> data = new HashMap();
        if(ListUtil.isEmpty(userIds)) {
            return data;
        }
        List<UserOrgDTO> list = this.getUserOrgByIds(userIds);
//        for(UserOrgDTO bean : list) {
//            data.put(bean.getUserId(), bean);
//        }
        data = EntityUtil.makeEntityMap(list,"userId");
        return data;
    }


    /**
     * 重置密码
     * @param username 用户名
     * @param password 密码
     * @param diffPassword 重置的密码
     * @return 重置密码是否成功
     */
    public RespDTO resetPassword(String username, String password, String diffPassword) {
        if (StringUtil.isBlank(username)){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "请输入手机号!");
        }
        if (StringUtil.isBlank(password)){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "请输入密码!");
        }
        User user= this.findByName(username);
        if(null==user){
            throw new CommonException(ServiceErrorCode.USER_NOT_FOUND);
        }
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        if(!StringUtil.isBlank(diffPassword) && "Y".equals(diffPassword)) {
            if(passwordEncoder.matches(password,user.getPassword())){
                throw new CommonException(CommonErrorCode.PARAM_IS_ERROR, "修改后密码不能与修改前相同!");
            }
        }
        String entryPassword= passwordEncoder.encode(password);
        user.setPassword(entryPassword);
        user.setGmtModified(new Date());
        this.updateById(user);
        return RespDTO.onSuc(true);
    }


    //---------------缓存演示开始-------------------------------------
    /**
     * 新建用户
     * @param user 新建用户信息输入参数
     * @return 用户信息
     */
    @CachePut(value = CACHE_NAME, key = "'user:'+#p0['id']")
    public User saveUser(User user){
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        String entryPassword= passwordEncoder.encode(user.getPassword());
        user.setPassword(entryPassword);
        user.setGmtCreate(new Date());
        user.setCreator(UserUtils.getCurrentPrincipleID());
        super.save(user);
        return user;
    }

    /**
     * 更新用户信息
     * @param user  更新用户信息输入参数
     * @return 用户信息
     */
    @CachePut(value = CACHE_NAME, key = "'user:'+#p0['id']")
    public User updateUser(User user){
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        String entryPassword= passwordEncoder.encode(user.getPassword());
        user.setPassword(entryPassword);
        user.setGmtModified(new Date());
        user.setModifier(UserUtils.getCurrentPrincipleID());
        super.updateById(user);
        return user;
    }

    /**
     * 根据用户ID获取用户信息
     * @param id 用户ID
     * @return 用户信息
     */
    @Override
    @Cacheable(value = CACHE_NAME, key = "'user:'+#p0")
    public User getById(Serializable id){
        return super.getById(id);
    }

    /**
     * 根据用户ID删除用户(物理删除)
     * @param id 用户ID
     * @return 是否成功
     */
    @Override
    @CacheEvict(value = CACHE_NAME, key = "'user:'+#p0")
    public boolean removeById(Serializable id){
        return super.removeById(id);
    }

	public GetConsoleUserInfoDTO getConsoleUserInfo() {
		Long userId = Long.parseLong(UserUtils.getCurrentPrincipleID());
	    User user = this.getById(userId);
	    Organization organization = organizationFacade.getByUserId(userId);
	    UserAuthentication userAuthentication = userAuthenticationFacade.getByUserId(userId);
	        
	    GetConsoleUserInfoDTO getConsoleUserInfoDTO = new GetConsoleUserInfoDTO();
	    getConsoleUserInfoDTO.setUser(user);
	    getConsoleUserInfoDTO.setOrganization(organization);
	    getConsoleUserInfoDTO.setUserAuthentication(userAuthentication);
	      
	    return getConsoleUserInfoDTO;
	}
    //---------------缓存演示结束-------------------------------------


    /**
     * 远程调用，根据机构名称获取用户信息
     * @param orgName 机构信息
     * @return 用户ids
     */
    public List<Long> getUserByOrgNameFac(String orgName) {
        List<Long> userIdList = new ArrayList<>();
        if(StringUtil.isEmpty(orgName)) {
            return userIdList;
        }
        List<User> userList = this.getUserByOrgName(orgName);
        for(User bean : userList) {
            userIdList.add(bean.getId());
        }
        return userIdList;
    }
}
