package com.novoda.downloadmanager;

import com.novoda.notils.logger.simple.Log;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static com.novoda.downloadmanager.InternalFileSizeFixtures.aFileSize;
import static com.novoda.downloadmanager.NetworkResponseFixtures.aNetworkResponse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;

public class NetworkFileDownloaderTest {

    private static final byte[] BYTES_TO_RECEIVE = "s".getBytes();

    private static final String ANY_RAW_URL = "http://example.com";
    private static final FileSize KNOWN_FILE_SIZE = aFileSize().withAreBytesDownloadedKnown(true).build();
    private static final FileSize UNKNOWN_FILE_SIZE = aFileSize().withAreBytesDownloadedKnown(false).build();

    private final FileDownloader.Callback callback = mock(FileDownloader.Callback.class);
    private final HttpClient httpClient = mock(HttpClient.class);
    private final NetworkRequestCreator requestCreator = spy(new NetworkRequestCreator());

    private NetworkFileDownloader networkFileDownloader;

    @Before
    public void setUp() throws IOException {
        Log.setShowLogs(false);
        networkFileDownloader = new NetworkFileDownloader(httpClient, requestCreator);
        given(httpClient.execute(any(NetworkRequest.class))).willReturn(
                aNetworkResponse().withInputStream(new ByteArrayInputStream(BYTES_TO_RECEIVE)).build()
        );
    }

    @Test
    public void createsRequestWithDownloadedBytesHeader_whenKnown() {
        networkFileDownloader.startDownloading(ANY_RAW_URL, KNOWN_FILE_SIZE, callback);

        verify(requestCreator).createDownloadRequestWithDownloadedBytesHeader(ANY_RAW_URL, KNOWN_FILE_SIZE.currentSize(), KNOWN_FILE_SIZE.totalSize());
    }

    @Test
    public void createsDownloadRequest() {
        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(requestCreator).createDownloadRequest(ANY_RAW_URL);
    }

    @Test
    public void emitsBytes_whenDownloading() {
        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        byte[] expectedBytes = new byte[4096];
        expectedBytes[0] = "s".getBytes()[0];

        verify(callback).onBytesRead(expectedBytes, 1);
    }

    @Test
    public void closesByteStream_whenDownloading() throws IOException {
        HttpClient.NetworkResponse networkResponse = spy(aNetworkResponse().build());
        given(httpClient.execute(any(NetworkRequest.class))).willReturn(networkResponse);

        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(networkResponse).closeByteStream();
    }

    @Test
    public void emitsError_whenResponseCodeIsInvalid() throws IOException {
        given(httpClient.execute(any(NetworkRequest.class))).willReturn(aNetworkResponse().withCode(418).build());

        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(callback).onError();
    }

    @Test
    public void emitsDownloadFinished_whenResponseCodeIsInvalid() throws IOException {
        given(httpClient.execute(any(NetworkRequest.class))).willReturn(aNetworkResponse().withCode(418).build());

        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(callback).onDownloadFinished();
    }

    @Test
    public void emitsError_whenRequestExecutionFails() throws IOException {
        given(httpClient.execute(any(NetworkRequest.class))).willThrow(IOException.class);

        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(callback).onError();
    }

    @Test
    public void emitsDownloadFinished_whenRequestExecutionFails() throws IOException {
        given(httpClient.execute(any(NetworkRequest.class))).willThrow(IOException.class);

        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        verify(callback).onDownloadFinished();
    }

    @Ignore // How can we test the `canDownload` flag?
    @Test
    public void stopsEmittingBytes_whenStoppingDownload() throws IOException {
        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);

        networkFileDownloader.stopDownloading();

        verify(callback, never()).onBytesRead(new byte[10], 0);
    }
}

@@ -379,56 +379,8 @@
 se;%0A
-import static org.mockito.ArgumentMatchers.any;%0A
 impo
@@ -565,16 +565,211 @@
 Bytes();
+%0A    private static final HttpClient.NetworkResponse RESPONSE_WITH_INPUT_STREAM = aNetworkResponse()%0A            .withInputStream(new ByteArrayInputStream(BYTES_TO_RECEIVE))%0A            .build();
 %0A%0A    pr
@@ -1038,32 +1038,145 @@
 (false).build();
+%0A    private static final HttpClient.NetworkResponse INVALID_RESPONSE = aNetworkResponse().withCode(418).build();
 %0A%0A    private fi
@@ -1374,20 +1374,16 @@
 eator = 
-spy(
 new Netw
@@ -1401,17 +1401,16 @@
 reator()
-)
 ;%0A%0A    p
@@ -1497,35 +1497,16 @@
  setUp()
- throws IOException
  %7B%0A     
@@ -1627,275 +1627,84 @@
     
-    given(httpClient.execute(any(NetworkRequest.class))).willReturn(%0A                aNetworkResponse().withInputStream(new ByteArrayInputStream(BYTES_TO_RECEIVE)).build()%0A        );%0A    %7D%0A%0A    @Test%0A    public void createsRequestWithDownloadedBytesHeader_whenKnown()
+%7D%0A%0A    @Test%0A    public void emitsBytes_whenDownloading() throws IOException
  %7B%0A 
@@ -1714,104 +1714,41 @@
     
-n
+N
 etwork
-FileDownloader.startDownloading(ANY_RAW_URL, KNOWN_FILE_SIZE, callback);%0A%0A        verify(
+Request downloadRequest = 
 requ
@@ -1749,33 +1749,32 @@
 = requestCreator
-)
 .createDownloadR
@@ -1887,285 +1887,94 @@
     
-%7D%0A%0A
     
-@Test%0A    public void createsDownloadRequest() %7B%0A        networkFileDownloader.startDownloading(ANY_RAW_URL, UNKNOWN_FILE_SIZE, callback);%0A%0A        verify(requestCreator).createDownloadRequest(ANY_RAW_URL);%0A    %7D%0A%0A    @Test%0A    public void emitsBytes_whenDownloading() %7B
+given(httpClient.execute(downloadRequest)).willReturn(RESPONSE_WITH_INPUT_STREAM);
 %0A   
@@ -2022,34 +2022,32 @@
 ng(ANY_RAW_URL, 
-UN
 KNOWN_FILE_SIZE,
@@ -2421,32 +2421,56 @@
 ute(
-any(NetworkRequest.class
+requestCreator.createDownloadRequest(ANY_RAW_URL
 ))).
@@ -2771,87 +2771,87 @@
 ute(
-any(NetworkRequest.class))).willReturn(aNetworkResponse().withCode(418).build()
+requestCreator.createDownloadRequest(ANY_RAW_URL))).willReturn(INVALID_RESPONSE
 );%0A%0A
@@ -3118,87 +3118,87 @@
 ute(
-any(NetworkRequest.class))).willReturn(aNetworkResponse().withCode(418).build()
+requestCreator.createDownloadRequest(ANY_RAW_URL))).willReturn(INVALID_RESPONSE
 );%0A%0A
@@ -3465,32 +3465,56 @@
 ute(
-any(NetworkRequest.class
+requestCreator.createDownloadRequest(ANY_RAW_URL
 ))).
@@ -3812,32 +3812,56 @@
 ute(
-any(NetworkRequest.class
+requestCreator.createDownloadRequest(ANY_RAW_URL
 ))).

Remove spy from request creator.