package oving6.office;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class ManagerTest {

	private Clerk clerk;
	private Manager manager;
	private Printer printer;

	@BeforeEach
	public void setUp() {
		printer = new Printer();
		clerk = new Clerk(printer);
		manager = new Manager(List.of(clerk));
	}

	@Test
	@DisplayName("Create Manager with empty Employee collection")
	public void testNoEmployeesConstructor() {
		assertThrows(IllegalArgumentException.class, () -> {
			new Manager(new ArrayList<Employee>());
		}, "Test creating a new Manager with an empty Employee collection");
	}

	@Test
	@DisplayName("Check getResourceCount without middle managers")
	public void testResourceCount() {
		assertEquals(2, manager.getResourceCount(),
				"Test the number of resources for a manager without middle managers");
	}

	@Test
	@DisplayName("Check getResourceCount with middle managers")
	public void testMiddleManagementResourceCount() {
		Manager topManager = new Manager(List.of(manager));
		assertEquals(3, topManager.getResourceCount(),
				"Test the number of resources with middle managers");
	}

	@Test
	@DisplayName("Perform a calculation")
	public void testDoCalculations() {
		assertEquals(0, clerk.getTaskCount(),
				"Test the initialization of the number of tasks for a worker");
		assertEquals(0, manager.getTaskCount(),
				"Test the initialization of the number of tasks for a manager");

		double calc = manager.doCalculations((x, y) -> x + y, 2.0, 3.5);
		assertEquals(5.5, calc, "Test calculation with addition: 2.0 + 3.5");

		assertEquals(1, clerk.getTaskCount(),
				"Test the number of tasks for a worker after 1 calculation");
		assertEquals(1, manager.getTaskCount(),
				"Test the number of tasks for a manager after 1 calculation");
	}

	@Test
	@DisplayName("Print a document")
	public void testPrintDocuments() {
		manager.printDocument("document1");
		assertTrue(printer.getPrintHistory(clerk).contains("document1"),
				"Test that the printer history contains the document that was printed");
		assertEquals(1, clerk.getTaskCount(),
				"Test the number of tasks for a worker after 1 print");
		assertEquals(1, manager.getTaskCount(),
				"Test the number of tasks for a manager after 1 print");
		assertEquals(1, printer.getPrintHistory(clerk).size(),
				"Test the number of prints in the printer history after 1 print");
	}

	@Test
	@DisplayName("Check task count")
	public void testTaskCount() {
		assertEquals(0, clerk.getTaskCount(),
				"Test the initialization of the number of tasks for a worker");
		assertEquals(0, manager.getTaskCount(),
				"Test the initialization of the number of tasks for a manager");

		manager.printDocument("document");
		assertEquals(1, clerk.getTaskCount(),
				"Test the number of tasks for a worker after 1 print");
		assertEquals(1, manager.getTaskCount(),
				"Test the number of tasks for a manager after 1 print");

		manager.doCalculations((x, y) -> x + y, 2.0, 3.5);
		assertEquals(2, clerk.getTaskCount(),
				"Test the number of tasks for a worker after 1 print and 1 calculation");
		assertEquals(2, manager.getTaskCount(),
				"Test the number of tasks for a manager after 1 print and 1 calculation");
	}

	@Test
	@DisplayName("Multiple clerks")
	public void testSeveralClerks() {
		Clerk secondClerk = new Clerk(printer);
		Manager multiManager = new Manager(List.of(clerk, secondClerk));
		assertEquals(1, clerk.getResourceCount(), "Test the number of resources for worker #1");
		assertEquals(3, multiManager.getResourceCount(),
				"Test the number of resources for a manager with two workers");
		assertEquals(1, secondClerk.getResourceCount(), "Test the resources for worker #2");

		multiManager.printDocument("document");
		assertEquals(1, multiManager.getTaskCount(),
				"Test the number of tasks for a manager after 1 print");
		assertTrue(
				(clerk.getTaskCount() == 1 || secondClerk.getTaskCount() == 1)
						&& (clerk.getTaskCount() == 0 || secondClerk.getTaskCount() == 0),
				"Test that only one of the workers has performed a task after 1 print");

		double calc = multiManager.doCalculations((x, y) -> x + y, 2.0, 3.5);
		assertEquals(5.5, calc, "Test calculation with addition: 2.0 + 3.5");
		assertEquals(2, multiManager.getTaskCount(),
				"Test the number of tasks for a manager after 1 print and 1 calculation");
	}
}
