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

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.login.base.consts.IUserCenterConst;
import ink.wgink.login.base.enums.LoginTypeEnum;
import ink.wgink.login.base.exceptions.UserAuthenticationException;
import ink.wgink.login.base.service.user.UserLoginService;
import ink.wgink.pojo.bos.LoginUser;
import ink.wgink.pojo.bos.RoleGrantedAuthorityBO;
import ink.wgink.pojo.dtos.department.DepartmentSimpleDTO;
import ink.wgink.pojo.dtos.group.GroupSimpleDTO;
import ink.wgink.pojo.dtos.position.PositionSimpleDTO;
import ink.wgink.pojo.dtos.role.RoleSimpleDTO;
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 org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: UserAuthenticationProvider
 * @Description: 用户认证逻辑
 * @Author: WangGeng
 * @Date: 2020/5/25 1:52 下午
 * @Version: 1.0
 **/
public class UserAuthenticationProvider implements AuthenticationProvider {
    private static final Logger LOG = LoggerFactory.getLogger(UserAuthenticationProvider.class);
    private UserDetailsService userDetailsService;
    private PasswordEncoder passwordEncoder;
    private UserLoginService userLoginService;
    private IRoleUserBaseService roleUserBaseService;
    private IRoleMenuBaseService roleMenuBaseService;
    private IRolePermissionBaseService rolePermissionService;
    private IGroupUserBaseService groupUserBaseService;
    private IPositionUserBaseService positionUserBaseService;
    private ILoginHandlerService loginHandler;
    private IDepartmentUserService departmentUserService;
    private AccessControlProperties accessControlProperties;
    private HttpSession httpSession;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UserAuthenticationToken userAuthenticationToken = (UserAuthenticationToken) authentication;
        String username = userAuthenticationToken.getPrincipal().toString();
        httpSession.setAttribute(IUserCenterConst.LOGIN_USERNAME, username);
        LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(username);
        if (!passwordEncoder.matches(userAuthenticationToken.getCredentials().toString(), loginUser.getPassword())) {
            throw new UserAuthenticationException("用户名或密码错误");
        }
        // 清空session中的错误信息
        httpSession.removeAttribute(IUserCenterConst.ERROR_MESSAGE);
        httpSession.removeAttribute(IUserCenterConst.LOGIN_USERNAME);
        LOG.debug("设置权限权限");
        Set<GrantedAuthority> grantedAuthorities = new LinkedHashSet<>();
        if (StringUtils.equalsIgnoreCase(ISystemConstant.ADMIN, loginUser.getUsername())) {
            grantedAuthorities.add(new RoleGrantedAuthorityBO(ISystemConstant.ADMIN));
        } else {
            List<RolePO> rolePOs = null;
            // 先处理授权，再创建对象，否则 grantedAuthorities 中无法赋值
            if (roleUserBaseService != null) {
                LOG.debug("设置角色信息");
                rolePOs = roleUserBaseService.listRolePOByUserId(loginUser.getUserId());
                removalDuplicateRole(rolePOs);
                grantedAuthorities.addAll(loadRoleAuthority(rolePOs));
                if (grantedAuthorities.isEmpty()) {
                    throw new UserAuthenticationException(loginUser.getUsername() + "用户无任何角色");
                }
            }

            if (rolePOs != null) {
                List<RoleSimpleDTO> roleSimpleDTOs = roleUserBaseService.listSimple(rolePOs);
                loginUser.setRoles(roleSimpleDTOs);
            }

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

            if (groupUserBaseService != null) {
                LOG.debug("设置组信息");
                List<GroupPO> groupPOs = groupUserBaseService.listGroupPOByUserId(loginUser.getUserId());
                List<GroupSimpleDTO> groupSimpleDTOs = groupUserBaseService.listSimple(groupPOs);
                loginUser.setGroups(groupSimpleDTOs);
            }
            if (positionUserBaseService != null) {
                LOG.debug("设置职位");
                List<PositionPO> positionPOs = positionUserBaseService.listPositionPOByUserId(loginUser.getUserId());
                List<PositionSimpleDTO> positionSimpleDTOs = positionUserBaseService.listSimple(positionPOs);
                loginUser.setPositions(positionSimpleDTOs);
            }
        }
        // 设置权限
        loginUser.setAuthorities(grantedAuthorities);
        loginHandler(loginUser);
        // 设置数据权限
        if (!StringUtils.equalsIgnoreCase(ISystemConstant.ADMIN, loginUser.getUsername())) {
            userLoginService.setUserDataAuthority(loginUser);
        }
        // 更新登录信息
        userLoginService.updateUserLoginInfo(loginUser.getUserId(), loginUser.getUserName(), LoginTypeEnum.USERNAME_AND_PASSWORD.getValue());
        UserAuthenticationToken userAuthenticationTokenResult = new UserAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        userAuthenticationTokenResult.setDetails(userAuthenticationToken.getDetails());
        return userAuthenticationTokenResult;
    }

    /**
     * 登录处理
     *
     * @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("登录异常");
            }
        }
    }


    /**
     * 角色授权列表
     *
     * @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;
            }
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UserAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    public void setUserLoginService(UserLoginService userLoginService) {
        this.userLoginService = userLoginService;
    }

    public void setRoleUserBaseService(IRoleUserBaseService roleUserBaseService) {
        this.roleUserBaseService = roleUserBaseService;
    }

    public void setRoleMenuBaseService(IRoleMenuBaseService roleMenuBaseService) {
        this.roleMenuBaseService = roleMenuBaseService;
    }

    public void setRolePermissionService(IRolePermissionBaseService rolePermissionService) {
        this.rolePermissionService = rolePermissionService;
    }

    public void setGroupUserBaseService(IGroupUserBaseService groupUserBaseService) {
        this.groupUserBaseService = groupUserBaseService;
    }

    public void setPositionUserBaseService(IPositionUserBaseService positionUserBaseService) {
        this.positionUserBaseService = positionUserBaseService;
    }

    public void setLoginHandler(ILoginHandlerService loginHandler) {
        this.loginHandler = loginHandler;
    }

    public void setDepartmentUserService(IDepartmentUserService departmentUserService) {
        this.departmentUserService = departmentUserService;
    }

    public void setAccessControlProperties(AccessControlProperties accessControlProperties) {
        this.accessControlProperties = accessControlProperties;
    }

    public void setHttpSession(HttpSession httpSession) {
        this.httpSession = httpSession;
    }
}
