package oving6.stock;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class StockIndexTest {

	private static final double applePrice = 534.98;
	private static final double facebookPrice = 67.80;
	private static final double epsilon = 0.000001;

	private Stock apple;
	private Stock facebook;
	private StockIndex index0;
	private StockIndex index1;
	private StockIndex indexN;

	@BeforeEach
	public void setUp() {
		apple = new Stock("AAPL", applePrice);
		facebook = new Stock("FB", facebookPrice);

		index0 = new StockIndex("OSEBX");
		index1 = new StockIndex("OSEBX", facebook);
		indexN = new StockIndex("OSEBX", facebook, apple);
	}

	@Test
	@DisplayName("Test constructor")
	public void testConstructor() {
		assertEquals(0.0, index0.getIndex(), epsilon, "Test the value of the index with 0 stocks");
		assertEquals(facebookPrice, index1.getIndex(), epsilon,
				"Test the value of the index with 1 stock");
		assertEquals(facebookPrice + applePrice, indexN.getIndex(), epsilon,
				"Test the value of the index with 2 stocks");

		assertThrows(IllegalArgumentException.class, () -> {
			new StockIndex(null);
		}, "Test constructor with null name");

		assertThrows(IllegalArgumentException.class, () -> {
			new StockIndex("OSEBX", apple, null, facebook);
		}, "Test constructor with null stocks");
	}

	@Test
	@DisplayName("Add stock")
	public void testAddStock() {
		assertEquals(0.0, index0.getIndex(), epsilon, "Test the value of the index with 0 stocks");

		index0.addStock(facebook);
		assertEquals(facebookPrice, index0.getIndex(), epsilon,
				"Test the value of the index after adding 1 stock");

		assertThrows(IllegalArgumentException.class, () -> {
			index0.addStock(null);
		}, "Test adding null stock");
	}

	@Test
	@DisplayName("Add the same stock twice")
	public void testAddDuplicateStocks() {
		assertEquals(0.0, index0.getIndex(), epsilon, "Test the value of the index with 0 stocks");

		index0.addStock(facebook);
		assertEquals(facebookPrice, index0.getIndex(), epsilon,
				"Test the value of the index after adding 1 stock");

		index0.addStock(facebook);
		assertEquals(facebookPrice, index0.getIndex(), epsilon,
				"Test the value of the index after adding a stock that is already in the index");
	}

	@Test
	@DisplayName("Remove stock")
	public void testRemoveStock() {
		assertEquals(facebookPrice + applePrice, indexN.getIndex(), epsilon,
				"Test the value of the index with 2 stocks");

		indexN.removeStock(apple);
		assertEquals(facebookPrice, indexN.getIndex(), epsilon,
				"Test the value of the index after removing 1 stock");

		indexN.removeStock(apple);
		assertEquals(facebookPrice, indexN.getIndex(), epsilon,
				"Test the value of the index after removing 1 stock that was not in the index");

		indexN.removeStock(facebook);
		assertEquals(0.0, indexN.getIndex(), epsilon,
				"Test the value of the index after removing the only stock in the index");
	}

	@Test
	@DisplayName("Change stock price")
	public void testChangePrice() {
		double facebookPrice2 = 67.0;
		double facebookPrice3 = 69.0;

		facebook.setPrice(facebookPrice2);
		assertEquals(facebookPrice2, index1.getIndex(), epsilon,
				"Test the value of the index with 1 stock after changing the stock price");
		assertEquals(facebookPrice2 + applePrice, indexN.getIndex(), epsilon,
				"Test the value of the index with 2 stocks after changing the price of 1 stock");

		facebook.setPrice(facebookPrice3);
		assertEquals(facebookPrice3, index1.getIndex(), epsilon,
				"Test the value of the index with 1 stock after changing the stock price a "
						+ "second time");
		assertEquals(facebookPrice3 + applePrice, indexN.getIndex(), epsilon,
				"Test the value of the index with 2 stocks after changing the price of 1 stock a "
						+ "second time");
	}
}
