package org.jboss.resteasy.links.test;

import static org.jboss.resteasy.test.TestPortProvider.generateBaseUrl;

import java.security.Principal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.links.RESTServiceDiscovery;
import org.jboss.resteasy.plugins.server.embedded.SecurityDomain;
import org.jboss.resteasy.plugins.server.embedded.SimplePrincipal;
import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
import org.jboss.resteasy.test.EmbeddedContainer;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class TestSecureLinks
{

	private static Dispatcher dispatcher;

	@BeforeClass
	public static void beforeClass() throws Exception
	{
		dispatcher = EmbeddedContainer.start("/", new SecurityDomain(){

			public Principal authenticate(String username, String password)
					throws SecurityException {
				return new SimplePrincipal(username);
			}

			public boolean isUserInRoll(Principal username, String role) {
				return username.getName().equals(role);
			}
			
		}).getDispatcher();
	}

	@AfterClass
	public static void afterClass() throws Exception
	{
		EmbeddedContainer.stop();
	}

	@Parameters
	public static List<Class<?>[]> getParameters(){
		return Arrays.asList(new Class<?>[]{SecureBookStore.class}, new Class<?>[]{SecureBookStoreMinimal.class});
	}

	private Class<?> resourceType;
	private String url;
	private BookStoreService client;
	private DefaultHttpClient httpClient;
	public TestSecureLinks(Class<?> resourceType){
		this.resourceType = resourceType;
	}

	@Before
	public void before(){
		POJOResourceFactory noDefaults = new POJOResourceFactory(resourceType);
		dispatcher.getRegistry().addResourceFactory(noDefaults);
		url = generateBaseUrl();
		
		// Configure HttpClient to authenticate preemptively
		// by prepopulating the authentication data cache.
		// 1. Create AuthCache instance
		AuthCache authCache = new BasicAuthCache();
		// 2. Generate BASIC scheme object and add it to the local auth cache
		BasicScheme basicAuth = new BasicScheme();
		authCache.put(getHttpHost(url), basicAuth);
		// 3. Add AuthCache to the execution context
		BasicHttpContext localContext = new BasicHttpContext();
		localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
		
		httpClient = new DefaultHttpClient();
		ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor(httpClient, localContext);
		client = ProxyFactory.create(BookStoreService.class, url, executor);
	}

	@After
	public void after(){
		// TJWS does not support chunk encodings well so I need to kill kept
		// alive connections
		httpClient.getConnectionManager().closeIdleConnections(0, TimeUnit.MILLISECONDS);
		dispatcher.getRegistry().removeRegistrations(resourceType);
	}
	
	@Test
	public void testSecureLinksAdmin() throws Exception
	{
		CredentialsProvider cp = httpClient.getCredentialsProvider();
		cp.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("admin", "asd"));
		Book book = client.getBookXML("foo");
		checkBookLinks1(url, book, "add", "update", "list", "self", "remove");
	}

	@Test
	public void testSecureLinksPowerUser() throws Exception
	{
		CredentialsProvider cp = httpClient.getCredentialsProvider();
		cp.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("power-user", "asd"));
		Book book = client.getBookXML("foo");
		checkBookLinks1(url, book, "add", "update", "list", "self");
	}

	@Test
	public void testSecureLinksUser() throws Exception
	{
		CredentialsProvider cp = httpClient.getCredentialsProvider();
		cp.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("user", "asd"));
		Book book = client.getBookXML("foo");
		checkBookLinks1(url, book, "list", "self");
	}

	private void checkBookLinks1(String url, Book book, String... expectedLinks) {
		Assert.assertNotNull(book);
		Assert.assertEquals("foo", book.getTitle());
		Assert.assertEquals("bar", book.getAuthor());
		RESTServiceDiscovery links = book.getRest();
		Assert.assertNotNull(links);
		Assert.assertEquals(expectedLinks.length, links.size());
		for (String expectedLink : expectedLinks) {
			Assert.assertNotNull(links.getLinkForRel(expectedLink));
		}
	}

	private HttpHost getHttpHost(String url)
	{
		int i1 = url.indexOf(':');
		String scheme = url.substring(0, i1);
		int i2 = url.indexOf(':', i1 + 1);
		String host = url.substring(i1 + 3, i2);
		int i3 = url.indexOf('/', i2 + 1);
		String port = i3 == -1 ? url.substring(i2 + 1)
				               : url.substring(i2 + 1, i3); 
		return new HttpHost(host, Integer.valueOf(port), scheme);
	}
}