package ink.wgink.pojo.bos;

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 org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;

/**
 * @ClassName: UserBO
 * @Description: 用户
 * @Author: WangGeng
 * @Date: 2019/2/20 2:22 PM
 * @Version: 1.0
 **/
public class LoginUser implements UserDetails, CredentialsContainer {

    /**
     * org.springframework.security.core.userdetails.user的属性
     */
    private static final long serialVersionUID = 1399973719726626183L;
    private String password;
    private String username;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private Set<GrantedAuthority> authorities;
    private boolean enabled;

    private String userId;
    private String userName;
    private String userPhone;
    private String userAvatar;
    private String userEmail;
    private Integer userState;
    private Integer userType;
    private String userUKey;
    private Integer loginType;
    private String dataRight;
    private List<String> baseDepartmentIds;
    private List<String> dataRightUserIds;
    private List<RoleSimpleDTO> roles;
    private List<GroupSimpleDTO> groups;
    private List<DepartmentSimpleDTO> departments;
    private List<PositionSimpleDTO> positions;
    /**
     * 拓展数据，用于自定义登录用户所拥有的权限加载
     */
    private Object expandData;

    public LoginUser() {

    }

    public LoginUser(String username, String password) {
        this(username, password, true, true, true, true);
    }

    public LoginUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked) {
        if (username != null && !"".equals(username) && password != null) {
            this.username = username;
            this.password = password;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
        } else {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAccountNonExpired(boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    public void setAccountNonLocked(boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public void setCredentialsNonExpired(boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public void setAuthorities(Set<GrantedAuthority> authorities) {
        // this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        this.authorities = sortAuthorities(authorities);
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Collection<GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    public String getPassword() {
        return this.password;
    }

    public String getUsername() {
        return this.username;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }

    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }

    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }

    public void eraseCredentials() {
        this.password = null;
    }

    private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
        SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet(new LoginUser.AuthorityComparator());
        Iterator iterator = authorities.iterator();

        while (iterator.hasNext()) {
            GrantedAuthority grantedAuthority = (GrantedAuthority) iterator.next();
            sortedAuthorities.add(grantedAuthority);
        }

        return sortedAuthorities;
    }

    public boolean equals(Object rhs) {
        return rhs instanceof User ? this.username.equals(((LoginUser) rhs).username) : false;
    }

    public int hashCode() {
        return this.username.hashCode();
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"password\":\"")
                .append(password).append('\"');
        sb.append(",\"username\":\"")
                .append(username).append('\"');
        sb.append(",\"accountNonExpired\":")
                .append(accountNonExpired);
        sb.append(",\"accountNonLocked\":")
                .append(accountNonLocked);
        sb.append(",\"credentialsNonExpired\":")
                .append(credentialsNonExpired);
        sb.append(",\"authorities\":")
                .append(authorities);
        sb.append(",\"enabled\":")
                .append(enabled);
        sb.append(",\"userId\":\"")
                .append(userId).append('\"');
        sb.append(",\"userName\":\"")
                .append(userName).append('\"');
        sb.append(",\"userPhone\":\"")
                .append(userPhone).append('\"');
        sb.append(",\"userAvatar\":\"")
                .append(userAvatar).append('\"');
        sb.append(",\"userEmail\":\"")
                .append(userEmail).append('\"');
        sb.append(",\"userState\":")
                .append(userState);
        sb.append(",\"userType\":")
                .append(userType);
        sb.append(",\"userUKey\":\"")
                .append(userUKey).append('\"');
        sb.append(",\"loginType\":")
                .append(loginType);
        sb.append(",\"dataRight\":\"")
                .append(dataRight).append('\"');
        sb.append(",\"baseDepartmentIds\":")
                .append(baseDepartmentIds);
        sb.append(",\"dataRightUserIds\":")
                .append(dataRightUserIds);
        sb.append(",\"roles\":")
                .append(roles);
        sb.append(",\"groups\":")
                .append(groups);
        sb.append(",\"departments\":")
                .append(departments);
        sb.append(",\"positions\":")
                .append(positions);
        sb.append(",\"expandData\":")
                .append(expandData);
        sb.append('}');
        return sb.toString();
    }

    public static LoginUser.UserBuilder withUsername(String username) {
        return builder().username(username);
    }

    public static LoginUser.UserBuilder builder() {
        return new LoginUser.UserBuilder();
    }

    public static LoginUser.UserBuilder withUserDetails(UserDetails userDetails) {
        return withUsername(userDetails.getUsername()).password(userDetails.getPassword()).accountExpired(!userDetails.isAccountNonExpired()).accountLocked(!userDetails.isAccountNonLocked()).authorities(userDetails.getAuthorities()).credentialsExpired(!userDetails.isCredentialsNonExpired()).disabled(!userDetails.isEnabled());
    }

    public String getUserId() {
        return userId == null ? "" : userId.trim();
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName == null ? "" : userName.trim();
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPhone() {
        return userPhone == null ? "" : userPhone.trim();
    }

    public void setUserPhone(String userPhone) {
        this.userPhone = userPhone;
    }

    public String getUserAvatar() {
        return userAvatar == null ? "" : userAvatar;
    }

    public void setUserAvatar(String userAvatar) {
        this.userAvatar = userAvatar;
    }

    public String getUserEmail() {
        return userEmail == null ? "" : userEmail;
    }

    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
    }

    public Integer getUserState() {
        return userState == null ? 0 : userState;
    }

    public void setUserState(Integer userState) {
        this.userState = userState;
    }

    public Integer getUserType() {
        return userType == null ? 0 : userType;
    }

    public void setUserType(Integer userType) {
        this.userType = userType;
    }

    public String getUserUKey() {
        return userUKey == null ? "" : userUKey.trim();
    }

    public void setUserUKey(String userUKey) {
        this.userUKey = userUKey;
    }

    public Integer getLoginType() {
        return loginType;
    }

    public void setLoginType(Integer loginType) {
        this.loginType = loginType;
    }

    public List<String> getBaseDepartmentIds() {
        return baseDepartmentIds == null ? new ArrayList<>(0) : baseDepartmentIds;
    }

    public void setBaseDepartmentIds(List<String> baseDepartmentIds) {
        this.baseDepartmentIds = baseDepartmentIds;
    }

    public String getDataRight() {
        return dataRight == null ? "" : dataRight.trim();
    }

    public void setDataRight(String dataRight) {
        this.dataRight = dataRight;
    }

    public List<String> getDataRightUserIds() {
        return dataRightUserIds == null ? new ArrayList<>(0) : dataRightUserIds;
    }

    public void setDataRightUserIds(List<String> dataRightUserIds) {
        this.dataRightUserIds = dataRightUserIds;
    }


    public List<RoleSimpleDTO> getRoles() {
        return roles;
    }

    public void setRoles(List<RoleSimpleDTO> roles) {
        this.roles = roles;
    }

    public List<GroupSimpleDTO> getGroups() {
        return groups;
    }

    public void setGroups(List<GroupSimpleDTO> groups) {
        this.groups = groups;
    }

    public List<DepartmentSimpleDTO> getDepartments() {
        return departments;
    }

    public void setDepartments(List<DepartmentSimpleDTO> departments) {
        this.departments = departments;
    }

    public List<PositionSimpleDTO> getPositions() {
        return positions;
    }

    public void setPositions(List<PositionSimpleDTO> positions) {
        this.positions = positions;
    }

    public Object getExpandData() {
        return expandData;
    }

    public void setExpandData(Object expandData) {
        this.expandData = expandData;
    }

    public static class UserBuilder {
        private String username;
        private String password;
        private List<GrantedAuthority> authorities;
        private boolean accountExpired;
        private boolean accountLocked;
        private boolean credentialsExpired;
        private boolean disabled;
        private Function<String, String> passwordEncoder;

        private UserBuilder() {
            this.passwordEncoder = (password) -> {
                return password;
            };
        }

        public LoginUser.UserBuilder username(String username) {
            this.username = username;
            return this;
        }

        public LoginUser.UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public LoginUser.UserBuilder passwordEncoder(Function<String, String> encoder) {
            this.passwordEncoder = encoder;
            return this;
        }

        public LoginUser.UserBuilder roles(String... roles) {
            List<GrantedAuthority> authorities = new ArrayList(roles.length);
            String[] var3 = roles;
            int var4 = roles.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                String role = var3[var5];
                authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
            }

            return this.authorities((Collection) authorities);
        }

        public LoginUser.UserBuilder authorities(GrantedAuthority... authorities) {
            return this.authorities((Collection) Arrays.asList(authorities));
        }

        public LoginUser.UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
            this.authorities = new ArrayList(authorities);
            return this;
        }

        public LoginUser.UserBuilder authorities(String... authorities) {
            return this.authorities((Collection) AuthorityUtils.createAuthorityList(authorities));
        }

        public LoginUser.UserBuilder accountExpired(boolean accountExpired) {
            this.accountExpired = accountExpired;
            return this;
        }

        public LoginUser.UserBuilder accountLocked(boolean accountLocked) {
            this.accountLocked = accountLocked;
            return this;
        }

        public LoginUser.UserBuilder credentialsExpired(boolean credentialsExpired) {
            this.credentialsExpired = credentialsExpired;
            return this;
        }

        public LoginUser.UserBuilder disabled(boolean disabled) {
            this.disabled = disabled;
            return this;
        }

        public UserDetails build() {
            String encodedPassword = (String) this.passwordEncoder.apply(this.password);
            return new User(this.username, encodedPassword, !this.disabled, !this.accountExpired, !this.credentialsExpired, !this.accountLocked, this.authorities);
        }
    }

    private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
        private static final long serialVersionUID = 510L;

        private AuthorityComparator() {
        }

        public int compare(GrantedAuthority g1, GrantedAuthority g2) {
            if (g2.getAuthority() == null) {
                return -1;
            } else {
                return g1.getAuthority() == null ? 1 : g1.getAuthority().compareTo(g2.getAuthority());
            }
        }
    }

}
