package com.atlassian.stash.internal.user;

import com.atlassian.crowd.embedded.impl.IdentifierUtils;
import com.atlassian.stash.internal.ApplicationConstants;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.validation.annotation.OptionalString;

import javax.annotation.Nonnull;
import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class InternalGrantedPermission {

    private static final String ID_GEN = "grantedPermIdGenerator";

    @TableGenerator(name=ID_GEN, table= ApplicationConstants.ID_SEQUENCE_TABLE, pkColumnValue = "granted_permission", allocationSize = 10)
    @Id @GeneratedValue(generator = ID_GEN, strategy = GenerationType.TABLE)
    private final Long id;

    @Column(name = "perm_id", nullable = false)
    @org.hibernate.annotations.Type(type = "com.atlassian.hibernate.extras.type.GenericEnumUserType", parameters =
    @org.hibernate.annotations.Parameter(name = "enumClass", value = "com.atlassian.bitbucket.permission.Permission"))
    private final Permission permission;

    @Column(name = "group_name")
    @OptionalString(size = 255)
    private final String group;

    @ManyToOne(fetch = FetchType.LAZY)
    private final InternalApplicationUser user;

    protected InternalGrantedPermission() {
        this.id = null;
        this.permission = null;
        this.group = null;
        this.user = null;
    }

    protected InternalGrantedPermission(Long id, Permission permission, String group, InternalApplicationUser user) {
        this.id = id;
        this.permission = permission;
        this.group = group == null ? null : IdentifierUtils.toLowerCase(group);
        this.user = user;
    }

    public abstract void accept(@Nonnull InternalGrantedPermissionVisitor visitor);

    public Long getId() {
        return id;
    }

    public String getGroup() {
        return group;
    }

    public Permission getPermission() {
        return permission;
    }

    public InternalApplicationUser getUser() {
        return user;
    }

    protected abstract static class AbstractBuilder<B extends AbstractBuilder<B>> {

        protected String group;
        protected Long id;
        protected Permission permission;
        protected InternalApplicationUser user;

        protected AbstractBuilder() {

        }

        protected AbstractBuilder(InternalGrantedPermission source) {
            group = source.getGroup();
            id = source.getId();
            permission = source.getPermission();
            user = source.getUser();
        }

        public B group(String group) {
            this.group = group;
            return self();
        }

        public B id(long id) {
            this.id = id;
            return self();
        }

        public B permission(Permission permission) {
            this.permission = permission;
            return self();
        }

        public B user(InternalApplicationUser user) {
            this.user = user;
            return self();
        }

        protected abstract B self();

    }


}
