package com.fsck.k9.mailstore;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.fsck.k9.mailstore.util.FileFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;


public class DeferredFileBodyTest {
    public static final String TEST_ENCODING = "test-encoding";
    public static final byte[] TEST_DATA_SHORT = "test data".getBytes();
    public static final byte[] TEST_DATA_LONG = "test data long enough to be file backed".getBytes();
    public static final int TEST_THRESHOLD = 15;


    private File createdFile;
    private DeferredFileBody deferredFileBody;


    @Before
    public void setUp() throws Exception {
        FileFactory fileFactory = new FileFactory() {
            @Override
            public File createFile() throws IOException {
                assertNull("only a single file should be created", createdFile);
                createdFile = File.createTempFile("test", "tmp");
                createdFile.deleteOnExit();
                return createdFile;
            }
        };

        deferredFileBody = new DeferredFileBody(TEST_THRESHOLD, fileFactory, TEST_ENCODING);
    }

    @Test
    public void withShortData__getLength__shouldReturnWrittenLength() throws Exception {
        writeShortTestData();

        assertNull(createdFile);
        assertEquals(TEST_DATA_SHORT.length, deferredFileBody.getSize());
    }

    @Test
    public void withLongData__getLength__shouldReturnWrittenLength() throws Exception {
        writeLongTestData();

        assertNotNull(createdFile);
        assertEquals(TEST_DATA_LONG.length, deferredFileBody.getSize());
    }

    @Test
    public void withShortData__shouldReturnData() throws Exception {
        writeShortTestData();

        InputStream inputStream = deferredFileBody.getInputStream();
        byte[] data = IOUtils.toByteArray(inputStream);

        assertNull(createdFile);
        assertArrayEquals(TEST_DATA_SHORT, data);
    }

    @Test
    public void withLongData__shouldReturnData() throws Exception {
        writeLongTestData();

        InputStream inputStream = deferredFileBody.getInputStream();
        byte[] data = IOUtils.toByteArray(inputStream);
        InputStream fileInputStream = new FileInputStream(createdFile);
        byte[] dataFromFile = IOUtils.toByteArray(fileInputStream);

        assertArrayEquals(TEST_DATA_LONG, data);
        assertArrayEquals(TEST_DATA_LONG, dataFromFile);
    }

    @Test
    public void withShortData__getFile__shouldWriteDataToFile() throws Exception {
        writeShortTestData();

        File returnedFile = deferredFileBody.getFile();
        InputStream fileInputStream = new FileInputStream(returnedFile);
        byte[] dataFromFile = IOUtils.toByteArray(fileInputStream);

        assertSame(createdFile, returnedFile);
        assertArrayEquals(TEST_DATA_SHORT, dataFromFile);
    }

    @Test
    public void withLongData__getFile__shouldReturnCreatedFile() throws Exception {
        writeLongTestData();

        File returnedFile = deferredFileBody.getFile();

        assertSame(createdFile, returnedFile);
    }

    @Test
    public void withShortData__writeTo__shouldWriteData() throws Exception {
        writeShortTestData();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        deferredFileBody.writeTo(baos);

        assertArrayEquals(TEST_DATA_SHORT, baos.toByteArray());
    }

    @Test(expected = UnsupportedOperationException.class)
    public void setEncoding__shouldThrow() throws Exception {
        deferredFileBody.setEncoding("anything");
    }

    @Test
    public void getEncoding__shouldReturnEncoding() throws Exception {
        assertEquals(TEST_ENCODING, deferredFileBody.getEncoding());
    }

    private void writeShortTestData() throws IOException {
        OutputStream outputStream = deferredFileBody.getOutputStream();
        outputStream.write(TEST_DATA_SHORT);
        outputStream.close();
    }

    private void writeLongTestData() throws IOException {
        OutputStream outputStream = deferredFileBody.getOutputStream();
        outputStream.write(TEST_DATA_LONG);
        outputStream.close();
    }
}
