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

import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;
import com.google.protobuf.ByteString;

import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.atlassian.bitbucket.mesh.git.hosting.GitPackets.FLUSH_PACKET;

public class ReceivePackRequestAnalyzer implements GitReceivePackFilter {

    private static final Pattern REF_CHANGE_COMMAND = Pattern.compile("([0-9a-z]{40}) ([0-9a-z]{40}) (.*)");

    private boolean done;
    private boolean shallow;
    private boolean signed;
    private boolean update;

    @Nonnull
    @Override
    public RpcStreamChunk onPacketLine(@Nonnull RpcStreamChunk chunk) {
        ByteString data = chunk.getData();
        // See https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt for a detailed
        // description of the pack protocol
        if (data.size() >= 4) { // git packets start with 4 characters describing the packet length
            String line = data.toStringUtf8();
            if (line.equals(FLUSH_PACKET) || line.startsWith("push-cert-end")) {
                // flush packet or end of the certified push section
                done = true;
            } else {
                // strip off the 4 chars used for the git packet size
                line = line.substring(4);
                if (line.startsWith("push-cert")) {
                    signed = true;
                } else if (line.startsWith("shallow ")) {
                    shallow = true;
                } else {
                    Matcher matcher = REF_CHANGE_COMMAND.matcher(line);
                    if (matcher.matches()) {
                        update = true;
                    }
                }
            }
        }

        return chunk;
    }

    @Override
    public boolean wantsPacketLines() {
        return !done;
    }

    /**
     * @return whether the receive-pack was signed with a push certificate
     */
    public boolean isSigned() {
        return signed;
    }

    /**
     * @return whether the receive-pack originated from a shallow repository
     */
    public boolean isShallow() {
        return shallow;
    }

    /**
     * @return whether the receive-pack request contained any commands to update refs
     */
    public boolean isUpdate() {
        return update;
    }
}
