package ink.wgink.login.base.service.user;

import com.alibaba.fastjson.JSONObject;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.interfaces.expand.login.ILoginHandlerService;
import ink.wgink.interfaces.group.IGroupUserBaseService;
import ink.wgink.interfaces.position.IPositionUserBaseService;
import ink.wgink.interfaces.role.IRoleMenuBaseService;
import ink.wgink.interfaces.role.IRolePermissionBaseService;
import ink.wgink.interfaces.role.IRoleUserBaseService;
import ink.wgink.interfaces.user.IUserDetailCheckService;
import ink.wgink.login.base.consts.IUserCenterConst;
import ink.wgink.login.base.exceptions.UserAuthenticationException;
import ink.wgink.login.base.manager.ConfigManager;
import ink.wgink.pojo.bos.LoginUser;
import ink.wgink.pojo.bos.RoleGrantedAuthorityBO;
import ink.wgink.pojo.pos.DepartmentPO;
import ink.wgink.pojo.pos.GroupPO;
import ink.wgink.pojo.pos.PositionPO;
import ink.wgink.pojo.pos.RolePO;
import ink.wgink.properties.AccessControlProperties;
import ink.wgink.service.department.service.IDepartmentUserService;
import ink.wgink.service.user.pojo.pos.UserPO;
import ink.wgink.service.user.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * @ClassName: UserDetailServiceImpl
 * @Description: User Detail
 * @Author: WangGeng
 * @Date: 2019/2/18 3:06 PM
 * @Version: 1.0
 **/
@Service
public class UserDetailServiceImpl implements UserDetailsService, IUserDetailCheckService {

    private static final Logger LOG = LoggerFactory.getLogger(UserDetailServiceImpl.class);

    @Autowired(required = false)
    private IRoleUserBaseService roleUserBaseService;
    @Autowired(required = false)
    private IRoleMenuBaseService roleMenuBaseService;
    @Autowired(required = false)
    private IRolePermissionBaseService rolePermissionService;
    @Autowired(required = false)
    private IGroupUserBaseService groupUserBaseService;
    @Autowired(required = false)
    private IPositionUserBaseService positionUserBaseService;
    @Autowired(required = false)
    private ILoginHandlerService loginHandler;
    @Autowired
    private IDepartmentUserService departmentUserService;
    @Autowired
    private IUserService userService;
    @Autowired
    private AccessControlProperties accessControlProperties;
    @Autowired
    private HttpSession httpSession;

    public UserDetailServiceImpl() {
        super();
    }

    @Override
    public UserDetails loadUserByUsername(String userInfo) throws UsernameNotFoundException {
        String username;
        try {
            JSONObject userInfoObject = JSONObject.parseObject(userInfo);
            username = userInfoObject.getString("userUsername");
        } catch (Exception e) {
            username = userInfo;
        }

        UserPO userPO = userService.getPOByUsername(username.toLowerCase());
        if (null == userPO) {
            throw new UserAuthenticationException("账号不存在");
        }
        if (userPO.getUserState() == 1) {
            throw new UserAuthenticationException("账号已冻结");
        }

        LOG.debug("设置权限权限");
        LoginUser loginUser;
        Set<GrantedAuthority> grantedAuthorities = new LinkedHashSet<>();
        if (StringUtils.equalsIgnoreCase(ISystemConstant.ADMIN, username)) {
            grantedAuthorities.add(new RoleGrantedAuthorityBO(ISystemConstant.ADMIN));
            loginUser = createUserBO(userPO, grantedAuthorities);
        } else {
            List<RolePO> rolePOs = null;
            // 先处理授权，再创建对象，否则 grantedAuthorities 中无法赋值
            if (roleUserBaseService != null) {
                LOG.debug("设置角色信息");
                rolePOs = roleUserBaseService.listRolePOByUserId(userPO.getUserId());
                removalDuplicateRole(rolePOs);
                grantedAuthorities.addAll(loadRoleAuthority(rolePOs));
                if (grantedAuthorities.isEmpty()) {
                    throw new UserAuthenticationException(username + "用户无任何角色");
                }
            }

            loginUser = createUserBO(userPO, grantedAuthorities);
            if (rolePOs != null) {
                loginUser.setRoles(rolePOs);
            }

            LOG.debug("设置部门状态");
            List<DepartmentPO> departmentPOs = departmentUserService.listDepartmentPOByUserId(userPO.getUserId());
            loginUser.setDepartments(departmentPOs);

            if (groupUserBaseService != null) {
                LOG.debug("设置组信息");
                List<GroupPO> groupPOs = groupUserBaseService.listGroupPOByUserId(userPO.getUserId());
                loginUser.setGroups(groupPOs);
            }
            if (positionUserBaseService != null) {
                LOG.debug("设置职位");
                List<PositionPO> positionBOs = positionUserBaseService.listPositionPOByUserId(userPO.getUserId());
                loginUser.setPositions(positionBOs);
            }
        }
        loginHandler(loginUser);
        // 清空session中的错误信息
        httpSession.removeAttribute(IUserCenterConst.ERROR_MESSAGE);
        return loginUser;
    }

    /**
     * 登录处理
     *
     * @param loginUser
     */
    private void loginHandler(LoginUser loginUser) {
        if (loginHandler != null) {
            try {
                loginHandler.handle(loginUser);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
                throw new UserAuthenticationException("登录异常");
            }
        }
    }

    /**
     * 创建UserBO
     *
     * @param userPO
     * @param grantedAuthorities
     * @return
     */
    private LoginUser createUserBO(UserPO userPO, Set<GrantedAuthority> grantedAuthorities) {
        LoginUser loginUser = new LoginUser(userPO.getUserUsername(), userPO.getUserPassword(),
                userPO.getUserType() == 1 ? true : false,
                isAccountNonExpired(userPO),
                isCredentialsNonExpired(userPO),
                userPO.getUserState() == 0 ? true : false, grantedAuthorities);
        loginUser.setUserId(userPO.getUserId());
        loginUser.setUserName(userPO.getUserName());
        loginUser.setUserPhone(userPO.getUserPhone());
        loginUser.setUserUKey(userPO.getUserUKey());
        loginUser.setLoginType(userPO.getLoginType());
        loginUser.setUserEmail(userPO.getUserEmail());
        loginUser.setUserAvatar(userPO.getUserAvatar());
        return loginUser;
    }

    /**
     * 账号是否没有过期
     *
     * @param userPO
     * @return
     */
    private boolean isAccountNonExpired(UserPO userPO) {
        if (StringUtils.equals(userPO.getUserUsername(), ISystemConstant.ADMIN)) {
            return true;
        }
        if (!StringUtils.isBlank(userPO.getUserExpiredDate())) {
            // 判断用户是否超时失效
            DateTime expiredDateTime = DateTime.parse(userPO.getUserExpiredDate(), DateTimeFormat.forPattern(ISystemConstant.DATE_FORMATTER_YYYY_MM_DD_HH_MM_SS));
            DateTime nowDateTime = DateTime.now();
            if (expiredDateTime.isBefore(nowDateTime)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 密码是否没有过期
     *
     * @param userPO
     * @return
     */
    private boolean isCredentialsNonExpired(UserPO userPO) {
        if (StringUtils.equals(userPO.getUserUsername(), ISystemConstant.ADMIN)) {
            return true;
        }
        Map<String, Object> config = ConfigManager.getInstance().getConfig();
        Object passwordValidity = config.get("passwordValidity");
        // 自定义密码有效期
        if (passwordValidity != null && StringUtils.equals(passwordValidity.toString(), "custom")) {
            Object passwordValidityDays = config.get("passwordValidityDays");
            int validityDays = passwordValidityDays == null ? 0 : (Integer) passwordValidityDays;
            if (validityDays <= 0) {
                return true;
            }
            DateTime passwordModifiedDateTime = DateTime.parse(userPO.getGmtPasswordModified(), DateTimeFormat.forPattern(ISystemConstant.DATE_FORMATTER_YYYY_MM_DD_HH_MM_SS));
            DateTime passwordExpiredDateTime = passwordModifiedDateTime.plusDays(validityDays);
            DateTime nowDateTime = DateTime.now();
            if (passwordExpiredDateTime.isBefore(nowDateTime)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 角色授权列表
     *
     * @param rolePOs
     * @return
     */
    private List<GrantedAuthority> loadRoleAuthority(List<RolePO> rolePOs) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if (null != rolePOs && !rolePOs.isEmpty()) {
            // 设置菜单权限
            rolePOs.forEach((rolePO) -> {
                List<String> menuUrls = roleMenuBaseService.listMenuUrl(rolePO.getRoleId());
                List<String> inserts = new ArrayList<>();
                List<String> deletes = new ArrayList<>();
                List<String> updates = new ArrayList<>();
                List<String> queries = new ArrayList<>();
                if (accessControlProperties.getRolePermission()) {
                    inserts.addAll(rolePermissionService.listPermissionUrl(rolePO.getRoleId(), IRolePermissionBaseService.PERMISSION_INSERT));
                    deletes.addAll(rolePermissionService.listPermissionUrl(rolePO.getRoleId(), IRolePermissionBaseService.PERMISSION_DELETE));
                    updates.addAll(rolePermissionService.listPermissionUrl(rolePO.getRoleId(), IRolePermissionBaseService.PERMISSION_UPDATE));
                    queries.addAll(rolePermissionService.listPermissionUrl(rolePO.getRoleId(), IRolePermissionBaseService.PERMISSION_QUERY));
                }
                grantedAuthorities.add(new RoleGrantedAuthorityBO(rolePO.getRoleId(), rolePO.getRoleName(), menuUrls, inserts, deletes, updates, queries));
            });

        }
        return grantedAuthorities;
    }

    /**
     * 删除重复的角色
     *
     * @param rolePOs
     */
    private void removalDuplicateRole(List<RolePO> rolePOs) {
        if (rolePOs == null || rolePOs.isEmpty()) {
            return;
        }
        for (int i = 0; i < rolePOs.size(); i++) {
            RolePO rolePO = rolePOs.get(i);
            boolean isExist = false;
            for (int j = i + 1; j < rolePOs.size(); j++) {
                if (StringUtils.equals(rolePO.getRoleId(), rolePOs.get(j).getRoleId())) {
                    isExist = true;
                    break;
                }
            }
            if (isExist) {
                rolePOs.remove(i);
                i--;
                break;
            }
        }
    }

}
