/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.planimod.core;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import com.patrikdufresne.managers.ManagerEvent;
import com.patrikdufresne.managers.ManagerException;

/**
 * This class intend to test the function of {@link EmployeePreferenceManager}.
 * 
 * @author ikus060
 * 
 */
public class EmployeePreferenceManagerTest extends ManagerTestCase {

    @Test
    public void list_withTwoEmployees_ReturnUniqueObject() throws ManagerException {

        Team t1 = addTeam(managers, "team1");
        Team t2 = addTeam(managers, "team2");

        Employee emp1 = addEmployee(managers, "Employee#1");
        setEmployeePreference(managers, emp1, null, null, null, Arrays.asList(t1, t2));
        Employee emp2 = addEmployee(managers, "Employee#2");
        setEmployeePreference(managers, emp2, null, null, null, Arrays.asList(t1, t2));

        List<EmployeePreference> prefs;
        try {
            prefs = managers.getEmployeePreferenceManager().list();
        } catch (ManagerException e) {
            fail("Fail to get Employee preference", e);
            return;
        }

        assertNotNull(prefs);
        assertEquals("Wrong number of preferences.", 2, prefs.size());
    }

    /**
     * Check if the list does not contains preferences for archived employee.
     * 
     * @throws ManagerException
     */
    @Test
    public void list_WithArchivedEmployee() throws ManagerException {

        Team t1 = addTeam(managers, "team1");
        Team t2 = addTeam(managers, "team2");
        Employee emp1 = addEmployee(managers, "Employee#1");
        setEmployeePreference(managers, emp1, null, null, null, Arrays.asList(t1, t2));
        Employee emp2 = addEmployee(managers, "Employee#2");
        setEmployeePreference(managers, emp2, null, null, null, Arrays.asList(t1, t2));

        // Archive employee #2
        managers.archiveAll(Arrays.asList(emp2));

        List<EmployeePreference> prefs = managers.getEmployeePreferenceManager().list();
        assertNotNull(prefs);
        assertEquals(1, prefs.size());
        assertEquals(emp1, prefs.get(0).getEmployee());
    }

    @Test
    public void listByEmployee() {

        Employee emp1 = addEmployee(managers, "Employee#1");
        addEmployee(managers, "Employee#2");

        EmployeePreference pref;
        try {
            pref = managers.getEmployeePreferenceManager().getByEmployee(emp1);
        } catch (ManagerException e) {
            fail("Fail to get Employee preference", e);
            return;
        }

        assertNotNull(pref);
        assertEquals(emp1, pref.getEmployee());

    }

    @Test
    public void listByEmployees() {

        Employee emp1 = addEmployee(managers, "Employee#1");
        Employee emp2 = addEmployee(managers, "Employee#2");

        List<EmployeePreference> prefs;
        try {
            prefs = managers.getEmployeePreferenceManager().listByEmployees(Arrays.asList(emp1, emp2));
        } catch (ManagerException e) {
            fail("Fail to get Employee preference", e);
            return;
        }

        assertNotNull(prefs);
        assertEquals("Wrong number of preferences.", 2, prefs.size());
    }

    /**
     * Check if the Employee record is also removed when an EmployeePreference is removed. In this test case, the
     * employee / employeePreference can't be removed because it's used.
     * 
     * @throws ManagerException
     */
    @Test
    public void remove_withDependencies_expectError() throws ManagerException {

        addManagerObserver(ManagerEvent.REMOVE, Employee.class);
        addManagerObserver(ManagerEvent.REMOVE, EmployeePreference.class);

        /*
         * Add employee object
         */
        Employee employee = new Employee();

        managers.getEmployeeManager().add(Arrays.asList(employee));
        assertTrue(managers.getEmployeeManager().list().contains(employee));
        EmployeePreference pref;
        assertNotNull(pref = managers.getEmployeePreferenceManager().getByEmployee(employee));

        /*
         * Add dependencies
         */
        Position pos = addPosition(managers, null, "Position1", false);
        managers.getQualificationManager().setQualification(pos, employee, true);
        assertNotNull(managers.getQualificationManager().listByPositionAndEmployee(pos, employee));

        /*
         * Remove employee.
         */
        try {
            managers.removeAll(Arrays.asList(pref));
            Assert.fail("It should not be possible to remove EmployeePreference object.");
            return;
        } catch (ManagerException e) {
            // OK
        }

        // Make sure no event is send
        assertEquals("Wrong number of events.", 0, getManagerEvents().size());
        assertManagerEvent(0, getManagerEvents(), ManagerEvent.REMOVE, employee);

        // Make sure the object is still in the managers
        assertTrue(managers.getEmployeeManager().list().contains(employee));
        assertNotNull(managers.getEmployeePreferenceManager().getByEmployee(employee));
    }

    /**
     * Check if the Employee record is also removed when an EmployeePreference is removed. In this test case, the
     * employee / employeePreference can't be removed because it's used.
     * 
     * @throws ManagerException
     */
    @Test
    public void remove_withoutDependencies() throws ManagerException {

        addManagerObserver(ManagerEvent.REMOVE, Employee.class);
        addManagerObserver(ManagerEvent.REMOVE, EmployeePreference.class);

        /*
         * Add employee object
         */
        Employee employee = new Employee();

        managers.getEmployeeManager().add(Arrays.asList(employee));
        assertTrue(managers.getEmployeeManager().list().contains(employee));
        EmployeePreference pref;
        assertNotNull(pref = managers.getEmployeePreferenceManager().getByEmployee(employee));

        /*
         * Remove employee.
         */
        managers.removeAll(Arrays.asList(pref));

        // Make sure no event is send
        assertEquals("Wrong number of events.", 2, getManagerEvents().size());
        assertManagerEvent(1, getManagerEvents(), ManagerEvent.REMOVE, employee);
        assertManagerEvent(1, getManagerEvents(), ManagerEvent.REMOVE, pref);

        // Make sure the object is still in the managers
        assertFalse(managers.getEmployeeManager().list().contains(employee));
        assertNull(managers.getEmployeePreferenceManager().getByEmployee(employee));
    }

    @Test
    public void update_WrongPreferredPosition_ThrowException() {

        Team shift1 = addTeam(managers, "shift1");
        Position pos1 = addPosition(managers, null, "position#1", false);

        Employee emp1 = addEmployee(managers, "employee#1");
        EmployeePreference pref;
        try {
            pref = managers.getEmployeePreferenceManager().getByEmployee(emp1);
        } catch (ManagerException e) {
            fail("Fail to get Employee preference", e);
            return;
        }

        pref.setPreferredPosition(pos1);
        pref.setPreferredPositionTeam(shift1);

        // Preferred position not classified
        try {
            managers.getEmployeePreferenceManager().update(Arrays.asList(pref));
            Assert.fail("Settings a preferred position to a non-classified position should fail.");
        } catch (ManagerException e) {

        }

    }

    /**
     * Check if an exception is raised when trying to update the preference of an archived employee.
     * 
     * @throws ManagerException
     */
    @Test(expected = ManagerException.class)
    public void update_WithArchivedEmployee() throws ManagerException {

        // Create Employee object + set preferences
        Team shift1 = addTeam(managers, "shift1");
        Position pos1 = addPosition(managers, null, "position#1", false);
        Employee emp1 = addEmployee(managers, "employee#1");
        setEmployeePreference(managers, emp1, null, null, null, null);

        // Archive employee object
        managers.archiveAll(Arrays.asList(emp1));

        // Try to update the preferences
        setEmployeePreference(managers, emp1, null, null, null, Arrays.asList(shift1));
    }

    @Test
    public void update_WrongPreferredPosition_Working() {

        Team shift1 = addTeam(managers, "shift1");
        Position pos1 = addPosition(managers, null, "position#1", true);

        Employee emp1 = addEmployee(managers, "employee#1");
        addQualification(managers, emp1, pos1);
        EmployeePreference pref;
        try {
            pref = managers.getEmployeePreferenceManager().getByEmployee(emp1);
        } catch (ManagerException e) {
            fail("Fail to get Employee preference", e);
            return;
        }

        pref.setPreferredPosition(pos1);
        pref.setPreferredPositionTeam(shift1);

        // Preferred position not classified
        try {
            managers.getEmployeePreferenceManager().update(Arrays.asList(pref));
        } catch (ManagerException e) {
            fail("Settings a preferred position to a classified position should work.", e);
        }

    }
}
