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

import com.atlassian.bitbucket.mesh.git.hook.GitHookInputParser.ChunkType;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;

public class GitHookInputParserBuilder {

    private final ByteArrayOutputStream buffer;
    private final DataOutputStream stream;

    public GitHookInputParserBuilder() {
        buffer = new ByteArrayOutputStream();
        stream = new DataOutputStream(buffer);
    }

    public GitHookInputParserBuilder arguments(String... arguments) throws IOException {
        for (String argument : arguments) {
            writeChunk(ChunkType.ARG, argument);
        }

        return this;
    }

    public GitHookInputParser build() {
        return build(Long.MAX_VALUE);
    }

    public GitHookInputParser build(long maxSize) {
        return build(maxSize, bytes -> Channels.newChannel(new ByteArrayInputStream(bytes)));
    }

    public GitHookInputParser build(long maxSize, Function<byte[], ReadableByteChannel> bufferToChannel) {
        writeType(ChunkType.END);
        return new GitHookInputParser(bufferToChannel.apply(buffer.toByteArray()), null, maxSize);
    }

    public GitHookInputParserBuilder environment(String key, String value) throws IOException {
        return chunk(ChunkType.ENVIRONMENT, key + "=" + value);
    }

    public GitHookInputParserBuilder gitDir(String value) throws IOException {
        return chunk(ChunkType.GIT_DIR, value);
    }

    public GitHookInputParserBuilder hookRequestId(String value) throws IOException {
        return chunk(ChunkType.HOOK_REQUEST_ID, value);
    }

    public GitHookInputParserBuilder hookType(String value) throws IOException {
        return chunk(ChunkType.HOOK_TYPE, value);
    }

    public GitHookInputParserBuilder requestId(String requestId, String repositoryId) throws IOException {
        chunk(ChunkType.EXECUTION_CONTEXT_ID, requestId);
        writeString(repositoryId);
        return this;
    }

    public GitHookInputParserBuilder stdin(String value) throws IOException {
        return chunk(ChunkType.STDIN, value);
    }

    private GitHookInputParserBuilder chunk(ChunkType type, String value) throws IOException {
        writeChunk(type, value);

        return this;
    }

    private void writeChunk(ChunkType type, String data) throws IOException {
        writeType(type);
        writeString(data);
    }

    private void writeString(String data) throws IOException {
        byte[] valueBytes = data.getBytes(StandardCharsets.UTF_8);
        if (valueBytes.length > GitHookOutputWriter.SOCKET_OUTPUT_MAX) {
            throw new IOException(valueBytes.length + " bytes cannot be written in a single packet");
        }

        stream.writeShort(valueBytes.length);
        stream.write(valueBytes);
    }

    private void writeType(ChunkType type) {
        buffer.write(type.getCharacter());
    }
}
