Project.java

package taskfusion.domain;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import taskfusion.exceptions.AlreadyExistsException;
import taskfusion.exceptions.InvalidPropertyException;
import taskfusion.exceptions.NotFoundException;
import taskfusion.exceptions.OperationNotAllowedException;
import taskfusion.helpers.DateHelper;
import taskfusion.persistency.EmployeeRepository;
import taskfusion.persistency.ProjectRepository;
import taskfusion.viewModels.ProjectViewModel;

public class Project implements ConvertibleToViewModelInterface {
  private String projectNumber;
  private String projectTitle;
  private String customer;
  private Employee projectLeader;
  private Map<String, Employee> assignedEmployees = new HashMap<>();
  private List<ProjectActivity> activities = new ArrayList<ProjectActivity>();
  private Map<String, Report> reports = new HashMap<>();

  public Project(String projectTitle, Calendar date) {
    this.projectTitle = projectTitle;
    this.projectNumber = generateProjectNumber(date);
  }

  public ProjectViewModel toViewModel() {
    return new ProjectViewModel(this);
  }

  public boolean isInternal() {
    return customer == null;
  }

  public void setCustomer(String customer) {
    this.customer = customer;
  }

  public String getCustomer() {
    return this.customer;
  }

  public String getProjectTitle() {
    return this.projectTitle;
  }

  public String getProjectNumber() {
    return this.projectNumber;
  }

  /**
   * Generate a unique 5 digit project number as a string. Always leading with
   * two-digit-year
   * followed by incemental number.
   * 
   * For loop to identify highest last number, by ChatGPT v3.5
   */
  public static String generateProjectNumber(Calendar date) {
    //assert date != null; // Precondition
    int year = DateHelper.twoDigitYearFromDate(date);
    int num = 0;

    // Find the highest incremental number for the current year
    for (String projectNumber : ProjectRepository.getInstance().all().keySet()) {
      if (projectNumber.startsWith(String.format("%02d", year))) {
        num = Integer.parseInt(projectNumber.substring(2));
      }
    }

    int previousProjectNumber = year * 1000 + num;
    int nextProjectNumber = (year * 1000) + num + 1;
    // assert nextProjectNumber == previousProjectNumber + 1; // Postcondition

    if (nextProjectNumber < 10000) {
      return "0" + nextProjectNumber;
    }

    return "" + nextProjectNumber;

  }

  public void setProjectLeader(Employee employee) throws AlreadyExistsException {
    if (this.projectLeader != null) {
      throw new AlreadyExistsException("Der kan kun være en projektleder");
    }
    this.projectLeader = employee;
  }

  public Employee getProjectLeader() {
    return this.projectLeader;
  }

  public Employee getAssignedEmployee(String initials) {
    return assignedEmployees.get(initials);
  }

  public Map<String, Employee> getAssignedEmployees() {
    return assignedEmployees;
  }

  public void assignEmployee(String employeeInitials, Employee loggedInUser)
      throws NotFoundException, OperationNotAllowedException {

    Employee employee = EmployeeRepository.getInstance().findByInitials(employeeInitials);

    if (!allowAssignEmployeeToProject(loggedInUser)) {
      throw new OperationNotAllowedException("Kun projektleder kan tildele medarbejdere til projektet");
    }

    assignedEmployees.put(employee.getInitials(), employee);

  }

  private boolean allowAssignEmployeeToProject(Employee loggedInUser) {

    if (projectLeader == null) {
      return true;
    }
    if (loggedInUser.getInitials().equals(projectLeader.getInitials())) {
      return true;
    }
    return false;
  }

  public ProjectActivity createProjectActivity(String title, String startWeek, String endWeek, Employee loggedInUser)
      throws AlreadyExistsException, OperationNotAllowedException, InvalidPropertyException {

    /**
     * NOTE: Assertions are disabled using comments,
     * as they are not properly managed in code doverage report.
     */

    // assert title != null && startWeek != null && endWeek != null && 
    //   loggedInUser != null && activities != null; // Precondition

    if (hasProjectLeader()) {
      if (!projectLeader.isSameAs(loggedInUser)) {
        throw new OperationNotAllowedException("Kun projektlederen kan oprette en projekt aktivitet for dette projekt");
      }
    }

    if (hasProjectActivity(title)) {
      throw new AlreadyExistsException("Projekt aktivitet findes allerede");
    }

    ProjectActivity activity = new ProjectActivity(title, startWeek, endWeek);
    this.activities.add(activity);

    /**
     * NOTE: This post condition does not check for
     * !project@pre.hasProjectActivity(title)
     */
    // assert (
    // activities.contains(activity) &&
    // (!hasProjectLeader() || projectLeader.isSameAs(loggedInUser) )
    // ); // Postcondition

    return activity;
  }

  public boolean hasProjectActivity(String title) {
    for (ProjectActivity projectActivity : this.activities) {
      if (projectActivity.getTitle().equals(title)) {
        return true;
      }
    }
    return false;
  }

  public List<ProjectActivity> getActivities() {
    return activities;
  }

  public boolean hasProjectLeader() {
    return projectLeader != null;
  }

  public ProjectActivity findProjectActivity(String title) throws NotFoundException {
    for (ProjectActivity projectActivity : this.activities) {
      if (projectActivity.getTitle().equals(title)) {
        return projectActivity;
      }
    }
    throw new NotFoundException("Projektaktiviteten findes ikke.");
  }

  public List<WorktimeRegistration> getWorktimeRegistrations() throws NotFoundException {

    List<WorktimeRegistration> list = new ArrayList<>();

    for (ProjectActivity projectActivity : this.activities) {
      list.addAll(projectActivity.getWorktimeRegistrations());
    }
    return list;
  }

  public List<Employee> getListOfAssignedEmployees() {
    return this.assignedEmployees.values().stream().toList();
  }

  public void addLatestReport(String date, Report report) {
    this.reports.put(date, report);
  }

  public Map<String, Report> getReports() {
    return reports;
  }

  public void setActivityTimeBudget(Employee employee, String projectActivityTitle, Integer timeBudget) throws OperationNotAllowedException, NotFoundException {
    if (projectLeader == null || projectLeader.isSameAs(employee)) {
      findProjectActivity(projectActivityTitle).setTimeBudget(timeBudget);
    } else {
      throw new OperationNotAllowedException("Kun projektlederen kan tildele tidsbudgetter");
    }
  }

}