package com.github.blahord.integral.component.excel;

import com.github.blahord.bettercollections.util.Maps;
import com.github.blahord.integral.ComponentException;
import com.github.blahord.integral.data.DataRow;
import com.github.blahord.integral.data.DataRowBuilder;
import com.github.blahord.integral.schema.SchemaColumn;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.Instant;
import java.util.Arrays;

import static com.github.blahord.bettercollections.compatibility.MapWrappers.asJavaUtilMap;
import static com.github.blahord.bettercollections.util.Maps.entry;
import static com.github.blahord.integral.type.DefaultTypes.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class WriteXLSTest {

    private WriteXLS writeXLS;

    private ByteArrayOutputStream outputStream;

    @BeforeEach
    public void setUp() {
        outputStream = new ByteArrayOutputStream(1024 * 100);

        writeXLS = new WriteXLS(
            "writeODS", "in",
            Arrays.asList(
                new SchemaColumn("integer", SIMPLE_NULLABLE_INTEGER_TYPE),
                new SchemaColumn("float", SIMPLE_NULLABLE_FLOAT_TYPE),
                new SchemaColumn("string", SIMPLE_NULLABLE_STRING_TYPE),
                new SchemaColumn("boolean", SIMPLE_NULLABLE_BOOLEAN_TYPE),
                new SchemaColumn("time", SIMPLE_NULLABLE_TIME_TYPE)
            ),
            outputStream
        );
    }

    @Test
    public void testRun() {
        writeXLS.setTableName("table");
        writeXLS.onJobStarted();

        writeXLS.addInputData("in", createDataRow(1L, 1.5, "aaa", true, Instant.parse("2000-01-01T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(null, 2.5, "bbb", false, Instant.parse("2000-01-02T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(3L, null, "ccc", true, Instant.parse("2000-01-03T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(4L, 4.5, null, false, Instant.parse("2000-01-04T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(5L, 5.5, "eee", null, Instant.parse("2000-01-05T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(6L, 6.5, "fff", false, null));
        writeXLS.run();
        writeXLS.addInputData("in", createDataRow(7L, 7.5, "ggg", true, Instant.parse("2000-01-07T12:12:12Z")));
        writeXLS.run();
        writeXLS.addInputData("in", DataRow.EOI);
        writeXLS.run();

        writeXLS.onJobFinished();

        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray());

        final ReadXLS readXLS = new ReadXLS(
            "read", byteArrayInputStream,
            asJavaUtilMap(Maps.<Integer, SchemaColumn>asMap(
                entry(0, new SchemaColumn("integer", SIMPLE_NULLABLE_INTEGER_TYPE)),
                entry(1, new SchemaColumn("float", SIMPLE_NULLABLE_FLOAT_TYPE)),
                entry(2, new SchemaColumn("string", SIMPLE_NULLABLE_STRING_TYPE)),
                entry(3, new SchemaColumn("boolean", SIMPLE_NULLABLE_BOOLEAN_TYPE)),
                entry(4, new SchemaColumn("time", SIMPLE_NULLABLE_TIME_TYPE))
            )),
            "data"
        );

        readXLS.setTableName("table");
        readXLS.onJobStarted();

        readXLS.run();
        DataRow dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(1L);
        assertThat(dataRow.getFloatValue("float")).isEqualTo(1.5);
        assertThat(dataRow.getStringValue("string")).isEqualTo("aaa");
        assertThat(dataRow.getBooleanValue("boolean")).isTrue();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-01T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isNull();
        assertThat(dataRow.getFloatValue("float")).isEqualTo(2.5);
        assertThat(dataRow.getStringValue("string")).isEqualTo("bbb");
        assertThat(dataRow.getBooleanValue("boolean")).isFalse();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-02T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(3L);
        assertThat(dataRow.getFloatValue("float")).isNull();
        assertThat(dataRow.getStringValue("string")).isEqualTo("ccc");
        assertThat(dataRow.getBooleanValue("boolean")).isTrue();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-03T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(4L);
        assertThat(dataRow.getFloatValue("float")).isEqualTo(4.5);
        assertThat(dataRow.getStringValue("string")).isNull();
        assertThat(dataRow.getBooleanValue("boolean")).isFalse();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-04T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(5L);
        assertThat(dataRow.getFloatValue("float")).isEqualTo(5.5);
        assertThat(dataRow.getStringValue("string")).isEqualTo("eee");
        assertThat(dataRow.getBooleanValue("boolean")).isNull();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-05T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(6L);
        assertThat(dataRow.getFloatValue("float")).isEqualTo(6.5);
        assertThat(dataRow.getStringValue("string")).isEqualTo("fff");
        assertThat(dataRow.getBooleanValue("boolean")).isFalse();
        assertThat(dataRow.getTimeValue("time")).isNull();

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow.getIntegerValue("integer")).isEqualTo(7L);
        assertThat(dataRow.getFloatValue("float")).isEqualTo(7.5);
        assertThat(dataRow.getStringValue("string")).isEqualTo("ggg");
        assertThat(dataRow.getBooleanValue("boolean")).isTrue();
        assertThat(dataRow.getTimeValue("time")).isEqualTo(Instant.parse("2000-01-07T12:12:12Z"));

        readXLS.run();
        dataRow = readXLS.pollOutputData("data");
        assertThat(dataRow).isSameAs(DataRow.EOI);
        readXLS.onJobFinished();
    }

    @Test
    public void test_RunFailWrite() {
        final WriteXLS writeXLS1 = new WriteXLS(
            "write", "in",
            java.util.Collections.singletonList(new SchemaColumn("value", SIMPLE_INTEGER_TYPE)),
            new OutputStream() {
                @Override
                public void write(int i) throws IOException {
                    throw new IOException("Test-Exception");
                }
            }
        );

        writeXLS1.setTableName("table");
        writeXLS1.onJobStarted();
        assertThatThrownBy(writeXLS1::onJobFinished).isInstanceOf(ComponentException.class);
    }

    private DataRow createDataRow(Long longValue, Double doubleValue, String stringValue, Boolean booleanValue, Instant timeValue) {
        final DataRowBuilder dataRowBuilder = new DataRowBuilder();

        dataRowBuilder.addValue("integer", longValue);
        dataRowBuilder.addValue("float", doubleValue);
        dataRowBuilder.addValue("string", stringValue);
        dataRowBuilder.addValue("boolean", booleanValue);
        dataRowBuilder.addValue("time", timeValue);

        return dataRowBuilder.build();
    }
}