package com.danikula.videocache.support;

import com.danikula.android.garden.io.IoUtils;
import com.danikula.videocache.ByteArraySource;
import com.danikula.videocache.HttpProxyCacheServer;
import com.danikula.videocache.HttpUrlSource;
import com.danikula.videocache.ProxyCacheException;
import com.danikula.videocache.Source;
import com.danikula.videocache.sourcestorage.SourceInfoStorage;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import org.apache.tools.ant.util.ReflectUtil;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URL;
import java.util.Random;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

/**
 * @author Alexey Danilov ([email protected]).
 */
public class ProxyCacheTestUtils {

    public static final String HTTP_DATA_URL = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/android.jpg";
    public static final String HTTP_DATA_URL_ONE_REDIRECT = "http://bit.ly/1LAJKAy";
    public static final String HTTP_DATA_URL_3_REDIRECTS = "http://bit.ly/1QtKJiB";
    public static final String HTTP_DATA_URL_6_REDIRECTS = "http://ow.ly/Z17wz";
    public static final String HTTP_DATA_BIG_URL = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/phones.jpg";
    public static final String HTTP_DATA_BIG_URL_ONE_REDIRECT = "http://bit.ly/24DdZ06";
    public static final String ASSETS_DATA_NAME = "android.jpg";
    public static final String ASSETS_DATA_BIG_NAME = "phones.jpg";
    public static final int HTTP_DATA_SIZE = 4768;
    public static final int HTTP_DATA_BIG_SIZE = 94363;

    public static byte[] getFileContent(File file) throws IOException {
        return Files.asByteSource(file).read();
    }

    public static Response readProxyResponse(HttpProxyCacheServer proxy, String url) throws IOException {
        return readProxyResponse(proxy, url, -1);
    }

    public static Response readProxyResponse(HttpProxyCacheServer proxy, String url, int offset) throws IOException {
        String proxyUrl = proxy.getProxyUrl(url, false);
        if (!proxyUrl.startsWith("http://127.0.0.1")) {
            throw new IllegalStateException("Proxy url " + proxyUrl + " is not proxied! Original url is " + url);
        }
        URL proxiedUrl = new URL(proxyUrl);
        HttpURLConnection connection = (HttpURLConnection) proxiedUrl.openConnection();
        try {
            if (offset >= 0) {
                connection.setRequestProperty("Range", "bytes=" + offset + "-");
            }
            return new Response(connection);
        } finally {
            connection.disconnect();
        }
    }

    public static byte[] loadTestData() throws IOException {
        return loadAssetFile(ASSETS_DATA_NAME);
    }

    public static byte[] loadAssetFile(String name) throws IOException {
        InputStream in = RuntimeEnvironment.application.getResources().getAssets().open(name);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        IoUtils.copy(in, out);
        IoUtils.closeSilently(in);
        IoUtils.closeSilently(out);
        return out.toByteArray();
    }

    public static File getTempFile(File file) {
        return new File(file.getParentFile(), file.getName() + ".download");
    }

    public static File newCacheFile() {
        return new File(RuntimeEnvironment.application.getCacheDir(), UUID.randomUUID().toString());
    }

    public static byte[] generate(int capacity) {
        Random random = new Random(System.currentTimeMillis());
        byte[] result = new byte[capacity];
        random.nextBytes(result);
        return result;
    }

    public static HttpUrlSource newAngryHttpUrlSource() throws ProxyCacheException {
        HttpUrlSource source = mock(HttpUrlSource.class);
        doThrow(new RuntimeException()).when(source).getMime();
        doThrow(new RuntimeException()).when(source).read(any(byte[].class));
        doThrow(new RuntimeException()).when(source).open(anyInt());
        doThrow(new RuntimeException()).when(source).length();
        doThrow(new RuntimeException()).when(source).getUrl();
        doThrow(new RuntimeException()).when(source).close();
        return source;
    }

    public static HttpUrlSource newNotOpenableHttpUrlSource(String url, SourceInfoStorage sourceInfoStorage) throws ProxyCacheException {
        HttpUrlSource httpUrlSource = new HttpUrlSource(url, sourceInfoStorage);
        HttpUrlSource source = spy(httpUrlSource);
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                System.out.print("Can't open!!!");
                throw new RuntimeException();
            }
        }).when(source).open(anyInt());
        return source;
    }

    public static Source newPhlegmaticSource(byte[] data, final int maxDelayMs) throws ProxyCacheException {
        Source spySource = spy(new ByteArraySource(data));
        final Random delayGenerator = new Random(System.currentTimeMillis());
        doAnswer(new Answer() {

            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                Thread.sleep(delayGenerator.nextInt(maxDelayMs));
                return null;
            }
        }).doCallRealMethod().when(spySource).read(any(byte[].class));
        return spySource;
    }

    public static int getPort(HttpProxyCacheServer server) {
        String proxyUrl = server.getProxyUrl("test");
        Pattern pattern = Pattern.compile("http://127.0.0.1:(\\d*)/test");
        Matcher matcher = pattern.matcher(proxyUrl);
        assertThat(matcher.find()).isTrue();
        String portAsString = matcher.group(1);
        return Integer.parseInt(portAsString);
    }

    public static int getPortWithoutPing(HttpProxyCacheServer server) {
        return (Integer) ReflectUtil.getField(server, "port");
    }

    public static void installExternalSystemProxy() {
        // see proxies list at http://proxylist.hidemyass.com/
        Proxy systemProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("162.8.230.7", 11180));
        ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
        when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(systemProxy));
        ProxySelector.setDefault(mockedProxySelector);
    }

    public static void resetSystemProxy() {
        ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
        when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(Proxy.NO_PROXY));
        ProxySelector.setDefault(mockedProxySelector);
    }
}