package com.atlassian.bitbucket.commit;

import com.atlassian.bitbucket.property.PropertyMap;
import com.atlassian.bitbucket.property.SimplePropertySupport;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.user.Person;
import com.google.common.collect.ImmutableList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

public class SimpleCommit extends SimpleMinimalCommit implements Commit, Serializable {

    private final Person author;
    private final Date authorTimestamp;
    private final String message;
    private final List<MinimalCommit> parents;

    private transient SimplePropertySupport propertySupport;
    private transient Repository repository;

    private SimpleCommit(Builder builder) {
        super(builder.minimalCommitBuilder);

        propertySupport = new SimplePropertySupport(builder);
        author = requireNonNull(builder.author, "author");
        authorTimestamp = requireNonNull(builder.authorTimestamp, "authorTimestamp");
        message = builder.message;
        parents = ImmutableList.copyOf(builder.parents);
        repository = builder.repository;
    }

    @Override
    public boolean equals(Object o) {
        if (super.equals(o) && o instanceof SimpleCommit) {
            SimpleCommit that = (SimpleCommit) o;

            return Objects.equals(getAuthor(), that.getAuthor()) &&
                    Objects.equals(getAuthorTimestamp(), that.getAuthorTimestamp()) &&
                    Objects.equals(getMessage(), that.getMessage());
        }
        return false;
    }

    @Nonnull
    @Override
    public PropertyMap getProperties() {
        return propertySupport.getProperties();
    }

    @Nonnull
    @Override
    public Person getAuthor() {
        return author;
    }

    @Nonnull
    @Override
    public Date getAuthorTimestamp() {
        return authorTimestamp;
    }

    @Override
    @Nullable
    public String getMessage() {
        return message;
    }

    @Nonnull
    @Override
    public List<MinimalCommit> getParents() {
        return parents;
    }

    @Override
    @Nullable
    public Repository getRepository() {
        return repository;
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getAuthor(), getAuthorTimestamp(), getMessage());
    }

    public void setProperties(@Nonnull PropertyMap properties) {
        propertySupport.setProperties(properties);
    }

    public void setRepository(Repository repository) {
        if (this.repository != null) {
            throw new IllegalStateException("Repository has already been set; you cannot override once set.");
        }
        this.repository = repository;
    }

    @Override
    public String toString() {
        return "SimpleCommit{id='" + getId() +
                "', author='" + getAuthor() +
                "', authorTimestamp=" + getAuthorTimestamp() +
                ", message='" + getMessage() + "'}";
    }

    public static final class Builder extends SimplePropertySupport.AbstractPropertyBuilder<Builder> {

        private final SimpleMinimalCommit.Builder minimalCommitBuilder;
        
        private Person author;
        private Date authorTimestamp;
        private String message;
        private List<MinimalCommit> parents;
        private Repository repository;

        public Builder(@Nonnull String id) {
            minimalCommitBuilder = new SimpleMinimalCommit.Builder(id);
            parents = new ArrayList<>();
        }

        public Builder(@Nonnull Commit commit) {
            super(commit);

            author = commit.getAuthor();
            authorTimestamp = commit.getAuthorTimestamp();
            message = commit.getMessage();
            minimalCommitBuilder = new SimpleMinimalCommit.Builder(commit);
            parents = new ArrayList<>(commit.getParents());
            repository = commit.getRepository();
        }

        @Nonnull
        public Builder author(Person value) {
            author = value;

            return self();
        }

        @Nonnull
        public Builder authorTimestamp(@Nullable Date value) {
            authorTimestamp = value == null ? null : new Date(value.getTime());
            
            return self();
        }

        @Nonnull
        public SimpleCommit build() {
            return new SimpleCommit(this);
        }

        @Nonnull
        public Builder clearParents() {
            parents.clear();

            return self();
        }

        @Nonnull
        public Builder displayId(@Nullable String value) {
            minimalCommitBuilder.displayId(value);
            
            return self();
        }

        @Nonnull
        public Builder message(@Nullable String value) {
            message = value;

            return self();
        }

        @Nonnull
        public Builder parent(@Nullable MinimalCommit value) {
            addIf(Objects::nonNull, parents, value);

            return self();
        }

        @Nonnull
        public Builder parents(@Nullable MinimalCommit value, @Nullable MinimalCommit... values) {
            addIf(Objects::nonNull, parents, value, values);

            return self();
        }

        @Nonnull
        public Builder parents(@Nullable Iterable<MinimalCommit> values) {
            addIf(Objects::nonNull, parents, values);

            return self();
        }

        @Nonnull
        public Builder repository(@Nullable Repository value) {
            repository = value;

            return self();
        }

        @Nonnull
        @Override
        protected Builder self() {
            return this;
        }
    }
}
