package samuelb.capripol;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import samuelb.capripol.Services.GroupService;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;
//Stored procedure for calculating a User's rating
@Table(name="Users")
@Entity
@NamedStoredProcedureQueries({
        @NamedStoredProcedureQuery(
                name = "userRating",
                procedureName = "CalculateUserRating",
                parameters = {
                        @StoredProcedureParameter(
                                name = "baseRating",
                                type = BigDecimal.class,
                                mode = ParameterMode.IN),
                        @StoredProcedureParameter(
                                name = "userID",
                                type = Long.class,
                                mode = ParameterMode.IN),
                        @StoredProcedureParameter(
                                name = "focusID",
                                type = Long.class,
                                mode = ParameterMode.IN),
                        @StoredProcedureParameter(
                                name = "userRating",
                                type = BigDecimal.class,
                                mode = ParameterMode.OUT)
                })
})
/*
Entity representing a User. Has a set of Groups and Ratings as well as
Roles in the system (admin, superadmin) and group-based Roles
 */
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long userID;

    @Column(name = "userHashedPassword")
    private String password;

    @Column(name = "userName")
    private String userName;
    @Column(name = "userEmail")
    private String email;

    @ManyToMany
    @JoinTable(name = "GroupMembers",
            joinColumns = @JoinColumn(
                    name = "userID"),
            inverseJoinColumns = @JoinColumn(
                    name = "groupID"))
    private Set<Group> usersGroups = new HashSet<>();

    //Holds a Users roles in the system i.e. Admin, Super Admin
    @ManyToMany
    @JoinTable(name = "UserRoles",
        joinColumns = @JoinColumn(
                name = "userID"),
            inverseJoinColumns = @JoinColumn(
                    name = "roleID"))
    private Set<Role> systemRoles = new HashSet<>();

    //Holds a Users Roles in their Groups
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<GroupUserRoles> groupUserRoles = new HashSet<>();

    //Holds a Users base Ratings for foci
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Rating> ratings;

    protected User(){}

    public User(String userHashedPassword, String userName, String email, Set<Role> systemRoles){
        this.password = userHashedPassword;
        this.userName = userName;
        this.email = email;
        this.systemRoles = systemRoles;
    }

    public User(String userName, String userHashedPassword){
        this.userName = userName;
        this.password = userHashedPassword;
    }

    //Setters and Getters

    public Long getUserId() {
        return userID;
    }

    public void setId(Long userID) {
        this.userID = userID;
    }

    public String getPassword(){
        return password;
    }

    public void setPassword(String password){
        this.password = password;
    }

    public String getUsername() {
        return userName;
    }

    public void setUsername(String name) {
        this.userName = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<Role> getSystemRoles() {
        return systemRoles;
    }

    public void setSystemRoles(Set<Role> systemRoles) {
        this.systemRoles = systemRoles;
    }

    public Set<Rating> getRatings() {
        return ratings;
    }

    public BigDecimal getRating(Focus focus){
        for(Rating rating: ratings){
            if(rating.getFocus() == focus){
                return rating.getBaseRating();
            }
        }
        return BigDecimal.ONE;
    }

    public void setRatings(Set<Rating> ratings) {
        this.ratings = ratings;
    }

    public void addRating(Rating rating){
        this.ratings.add(rating);
    }

    public String systemRolesToString(){
        String outputRoles = "";
        int count = 0;
        for(Role role: systemRoles){
            if(count >= 1){
                outputRoles += ", ";
            }
            outputRoles += role.getName();
            count++;
        }
        return outputRoles;
    }

    public String allRolesToString(){
        String outputRoles = systemRolesToString();
        if(!outputRoles.equals("")){
            outputRoles += ", ";
        }
        int count = 0;
        for(GroupUserRoles groupUserRole: groupUserRoles){
            if(count >= 1){
                outputRoles += ", ";
            }
            outputRoles += groupUserRole.getGroupRole().getName() + " - " + groupUserRole.getGroup().getGroupName();
            count++;
        }
        return outputRoles;
    }

    public void removeRole(Role role){
        this.systemRoles.remove(role);
    }

    public Set<Group> getGroups() {
        return usersGroups;
    }

    public void addGroup(Group group) {
        this.usersGroups.add(group);
    }

    public void addGroups(Set<Group> groups) {
        this.usersGroups.addAll(groups);
    }

    public Set<Group> getUsersGroups() {
        return usersGroups;
    }

    public void setUsersGroups(Set<Group> usersGroups) {
        this.usersGroups = usersGroups;
    }

    public void addGroupRole(GroupUserRoles groupUserRoles){
        this.groupUserRoles.add(groupUserRoles);
    }

    public GroupUserRoles getRolesOfGroup(Group group){
        GroupUserRoles groupUserRole = null;
        for(GroupUserRoles role: groupUserRoles){
            if(role.getGroup() == group){
                groupUserRole = role;
            }
        }
        return groupUserRole;
    }

    public Long getUserID() {
        return userID;
    }

    public void setUserID(Long userID) {
        this.userID = userID;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Set<GroupUserRoles> getGroupUserRoles() {
        return groupUserRoles;
    }

    public void setGroupUserRoles(Set<GroupUserRoles> groupUserRoles) {
        if(this. groupUserRoles == null){
            this.groupUserRoles = groupUserRoles;
        }else{
            this.groupUserRoles.retainAll(groupUserRoles);
            this.groupUserRoles.addAll(groupUserRoles);
        }
    }

    public boolean hasRole(GroupRole groupRole, Group group){
        boolean result = false;
        for(GroupUserRoles groupUserRole: groupUserRoles){
            if(groupUserRole.getGroupRole().equals(groupRole) && groupUserRole.getGroup().equals(group)){
                result = true;
            }
        }
        return result;
    }

    public boolean hasRating(Rating rating){
        return ratings.contains(rating);
    }

    public String rolesForGroup(Group group){
        String outputRoles = "";
        int count = 0;
        for(GroupUserRoles groupUserRoles: groupUserRoles){
            for(GroupUserRoles groupUserRolesB: group.getGroupUserRoles()){
                if(groupUserRoles.equals(groupUserRolesB)){
                    if(count >= 1){
                        outputRoles += ", ";
                    }
                    outputRoles += groupUserRoles.getGroupRole().getName();
                    count++;
                }
            }
        }
        return outputRoles;
    }

    public String groupsToString(){
        String outputGroups = "";
        int count = 0;
        if(usersGroups.size() > 0){
            for(Group group: usersGroups){
                if(count >= 1){
                    outputGroups += ", ";
                }
                outputGroups += group.getGroupName();
                count++;
            }
        }else{
            outputGroups = "None";
        }
        return outputGroups;
    }
}