package com.atlassian.bitbucket.mesh.git.content;

import com.atlassian.bitbucket.mesh.rpc.v1.RpcPerson;
import com.atlassian.bitbucket.mesh.util.BuilderSupport;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import java.util.Date;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

public class Blame {

    private final RpcPerson author;
    private final Date authorTimestamp;
    private final RpcPerson committer;
    private final Date committerTimestamp;
    private final String commitId;
    private final String fileName;
    private final int lineNumber;
    private final int spannedLines;

    private Blame(Builder builder) {
        this.author = builder.author;
        this.authorTimestamp = builder.authorTimestamp;
        this.committer = builder.committer;
        this.committerTimestamp = builder.committerTimestamp;
        this.commitId = builder.commitId;
        this.fileName = builder.fileName;
        this.lineNumber = builder.lineNumber;
        this.spannedLines = builder.spannedLines;
    }

    @Nonnull
    public RpcPerson getAuthor() {
        return author;
    }

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

    @Nonnull
    public RpcPerson getCommitter() {
        return committer;
    }

    @Nonnull
    public Date getCommitterTimestamp() {
        return committerTimestamp;
    }

    @Nonnull
    public String getCommitId() {
        return commitId;
    }

    @Nonnull
    public String getFileName() {
        return fileName;
    }

    public int getLineNumber() {
        return lineNumber;
    }

    public int getSpannedLines() {
        return spannedLines;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder().append(lineNumber);
        if (spannedLines > 1) {
            builder.append("-").append(lineNumber + spannedLines - 1);
        }
        builder.append(":").append(commitId)
                .append(" ").append(author).append(" ").append(authorTimestamp)
                .append(" (").append(fileName).append(")");

        return builder.toString();
    }

    public static final class Builder extends BuilderSupport {

        private RpcPerson author;
        private Date authorTimestamp;
        private RpcPerson committer;
        private Date committerTimestamp;
        private String commitId;
        private String fileName;
        private int lineNumber;
        private int spannedLines;

        public Builder() {
        }

        public Builder(@Nonnull Blame blame) {
            author = requireNonNull(blame, "blame").getAuthor();
            authorTimestamp = blame.getAuthorTimestamp();
            committer = blame.getCommitter();
            committerTimestamp = blame.getCommitterTimestamp();
            commitId = blame.getCommitId();
            fileName = blame.getFileName();
            lineNumber = blame.getLineNumber();
            spannedLines = blame.getSpannedLines();
        }

        @Nonnull
        public Builder author(@Nonnull RpcPerson value) {
            author = requireNonNull(value, "author");

            return this;
        }

        @Nonnull
        public Builder authorTimestamp(@Nonnull Date value) {
            authorTimestamp = requireNonNull(value, "authorTimestamp");

            return this;
        }

        @Nonnull
        public Blame build() {
            checkArgument(lineNumber > 0, "Line numbers begin at 1 and are required");
            //noinspection ConstantConditions
            checkArgument(author != null && author.getName() != null && authorTimestamp != null,
                    "Author information (name and timestamp) is required");
            checkArgument(StringUtils.isNotBlank(commitId), "A commit hash is required");
            checkArgument(StringUtils.isNotBlank(fileName), "A filename is required");

            if (spannedLines < 1) {
                spannedLines = 1;
            }

            return new Blame(this);
        }

        @Nonnull
        public Builder committer(@Nonnull RpcPerson value) {
            committer = requireNonNull(value, "committer");

            return this;
        }

        @Nonnull
        public Builder committerTimestamp(@Nonnull Date value) {
            committerTimestamp = requireNonNull(value, "committerTimestamp");

            return this;
        }

        @Nonnull
        public Builder commitId(@Nonnull String value) {
            commitId = requireNonBlank(value, "commitId");

            return this;
        }

        @Nonnull
        public Builder fileName(@Nonnull String value) {
            fileName = requireNonBlank(value, "fileName");

            return this;
        }

        @Nonnull
        public Builder lineNumber(int value) {
            lineNumber = value;

            return this;
        }

        @Nonnull
        public Builder spannedLines(int value) {
            spannedLines = value;

            return this;
        }
    }
}
