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

import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitError;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcInvalidPack;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcInvalidPack.Reason;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;

import static com.atlassian.bitbucket.mesh.rpc.v1.RpcInvalidPack.Reason.*;
import static io.grpc.Status.INVALID_ARGUMENT;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

public class InvalidPackException extends AbstractGitException {

    private final String details;
    private final Reason reason;

    public InvalidPackException(@Nonnull Reason reason, @Nullable String details) {
        super(createMessage(requireNonNull(reason, "reason"), details), INVALID_ARGUMENT);

        this.details = details;
        this.reason = reason;
    }

    @Nonnull
    public static InvalidPackException forStderr(@Nullable String stderr) {
        stderr = StringUtils.trimToNull(stderr);
        String detail = null;
        if (stderr != null) {
            int index = stderr.lastIndexOf('\n');
            detail = StringUtils.removeStart(index == -1 ? stderr : stderr.substring(index + 1), "fatal: ");
            if (stderr.contains("fatal: did not receive ")) {
                return new InvalidPackException(MISSING_OBJECT, detail);
            }
            if (stderr.contains("fatal: object ") && stderr.contains("expected type")) {
                return new InvalidPackException(INVALID_OBJECT, detail);
            }
            if (stderr.contains("fatal: early EOF")) {
                return new InvalidPackException(EARLY_EOF, null);
            }
            if (stderr.contains("fatal: pack too large") || stderr.contains("fatal: pack exceeds")) {
                return new InvalidPackException(TOO_LARGE, detail);
            }
            if (stderr.contains("unresolved delta")) {
                return new InvalidPackException(UNRESOLVED_DELTA, detail);
            }
        }
        return new InvalidPackException(UNKNOWN, detail);
    }

    @Nonnull
    public Optional<String> getDetails() {
        return ofNullable(details);
    }

    @Nonnull
    public Reason getReason() {
        return reason;
    }

    @Nonnull
    @Override
    public RpcGitError toError() {
        return RpcGitError.newBuilder()
                .setInvalidPack(RpcInvalidPack.newBuilder()
                        .setDetails(defaultString(details))
                        .setReason(reason))
                .build();
    }

    private static String createMessage(Reason reason, String details) {
        StringBuilder builder = new StringBuilder("Invalid pack: ")
                .append(reason);
        if (isNotBlank(details)) {
            builder.append(" (").append(details).append(")");
        }
        return builder.toString();
    }
}
