package com.atlassian.stash.internal.repository;

import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.validation.annotation.OptionalString;
import com.atlassian.bitbucket.validation.annotation.RequiredString;
import com.google.common.base.Function;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.persistence.Column;
import javax.persistence.Embeddable;

/**
 * @since 2.11
 */
@Embeddable
public class InternalRefChange implements RefChange {

    public static final Function<RefChange, InternalRefChange> TRANSFORM_STRICT = change -> {
        //RepositoryRefsChangedEvents are raised by plugins, which supply their own RefChange instances. Several
        //of these plugins are known to violate the invariants of the RefChange interface. Unfortunately, the
        //built-in SimpleRefChange type makes this simple... It doesn't null-check its @Nonnull arguments.
        if (change == null || change.getType() == null || change.getRef() == null ||
                StringUtils.isBlank(change.getFromHash()) || change.getFromHash().length() != 40 ||
                StringUtils.isBlank(change.getToHash()) || change.getToHash().length() != 40 ||
                StringUtils.isBlank(change.getRef().getId())) {
            return null;
        }
        return new InternalRefChange(change);
    };

    public static final Function<RefChange, InternalRefChange> TRANSFORM_TOLERANT = change -> {
        //RepositoryRefsChangedEvents are raised by plugins, which supply their own RefChange instances. Several
        //of these plugins are known to violate the invariants of the RefChange interface. Unfortunately, the
        //built-in SimpleRefChange type makes this simple... It doesn't null-check its @Nonnull arguments.
        if (change == null || change.getType() == null || change.getRef() == null ||
                StringUtils.isBlank(change.getRef().getId())) {
            return null;
        }
        return new InternalRefChange(change);
    };

    @Column(name = "from_hash", length = 40)
    @OptionalString
    private final String fromHash;

    @Column(name = "ref_id", nullable = false, length = 1024)
    @RequiredString(size = 1024)
    private final String refId;

    @Column(name = "to_hash", length = 40)
    @OptionalString
    private final String toHash;

    @Column(name = "change_type", nullable = false)
    @org.hibernate.annotations.Type(type = "com.atlassian.hibernate.extras.type.GenericEnumUserType", parameters = {
            @org.hibernate.annotations.Parameter(name = "enumClass", value = "com.atlassian.bitbucket.repository.RefChangeType")
    })
    private final RefChangeType type;

    protected InternalRefChange() {
        fromHash = refId = toHash = null;
        type = null;
    }

    private InternalRefChange(RefChange change) {
        fromHash = change.getFromHash();
        refId = change.getRef().getId();
        toHash = change.getToHash();
        type = change.getType();
    }

    @Nonnull
    @Override
    public String getFromHash() {
        return fromHash;
    }

    @Nonnull
    @Override
    public String getRefId() {
        return refId;
    }

    @Nonnull
    @Override
    public MinimalRef getRef() {
        // TODO: fix this for real
        return new SimpleMinimalRef.Builder()
                .id(refId)
                .displayId(refId)
                .type(refId.startsWith("refs/tags/") ? StandardRefType.TAG : StandardRefType.BRANCH)
                .build();
    }

    @Nonnull
    @Override
    public String getToHash() {
        return toHash;
    }

    @Nonnull
    @Override
    public RefChangeType getType() {
        return type;
    }
}
