package com.atlassian.stash.internal.auth.trusted;

import com.atlassian.stash.internal.ApplicationConstants;
import com.atlassian.bitbucket.util.CustomPreconditions;
import com.atlassian.bitbucket.validation.annotation.RequiredString;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;

@Entity @Cacheable
@Table(name = InternalTrustedApplicationRestriction.TABLE,
        uniqueConstraints = @UniqueConstraint(columnNames = {"trusted_app_id", "restriction_type", "restriction_value"}))
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class InternalTrustedApplicationRestriction {

    private static final String ID_GEN = "trustedAppRestrictionIdGenerator";

    static final String TABLE = "trusted_app_restriction";

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

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "trusted_app_id", nullable=false)
    private final InternalTrustedApplication application;

    @Column(name = "restriction_type", nullable=false)
    private final Short type;

    @Column(name = "restriction_value", nullable=false) @RequiredString(size = 255)
    private final String value;

    // for JPA
    @SuppressWarnings("UnusedDeclaration")
    protected InternalTrustedApplicationRestriction() {
        id = null;
        application = null;
        type = null;
        value = null;
    }

    InternalTrustedApplicationRestriction(InternalTrustedApplication application, Type type, String value) {
        this(null, application, type, value);
    }

    InternalTrustedApplicationRestriction(Integer id, InternalTrustedApplication application, Type type, String value) {
        this.id = id;
        this.application = application;
        this.type = type != null ? type.getDbValue() : null;
        this.value = value;

        CustomPreconditions.checkRequiredString(value, 255);
    }

    public Integer getId() {
        return id;
    }

    public Type getType() {
        return type != null ? Type.fromDbValue(type) : null;
    }

    public String getValue() {
        return value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof InternalTrustedApplicationRestriction)) {
            return false;
        }
        InternalTrustedApplicationRestriction that = (InternalTrustedApplicationRestriction) o;

        return (application == null ? that.application == null : application.equals(that.application)) &&
                (type == null ? that.type == null : type.equals(that.type)) &&
                (value == null ? that.value == null : value.equals(that.value));
    }

    @Override
    public int hashCode() {
        int result = application != null ? application.hashCode() : 0;
        result = 31 * result + (type != null ? type.hashCode() : 0);
        result = 31 * result + (value != null ? value.hashCode() : 0);
        return result;
    }

    public static enum Type {
        IP(0),
        URL(1);

        private final short dbValue;

        Type(int dbValue) {
            this.dbValue = (short) dbValue;
        }

        public short getDbValue() {
            return dbValue;
        }

        public static Type fromDbValue(short dbValue) {
            for (Type type : values()) {
                if (type.getDbValue() == dbValue) {
                    return type;
                }
            }
            throw new IllegalArgumentException();
        }
    }

}
