/**
   * We know what is the next visa to apply. Let's add it to the list of applied visas and check
   * what is the new visaStatus of the request
   *
   * @param appliedVisa Visa to add
   */
  private void addAgreementVisa(RequestAgreementVisa appliedVisa) {
    getAgreementVisas().add(appliedVisa);

    RequestAgreementVisaStatus lastAppliedVisaStatus = appliedVisa.getStatus();

    // If last visa applied has been denied, no need to carry on: request is refused
    if (lastAppliedVisaStatus == RequestAgreementVisaStatus.DENIED) {
      nextAgreementVisaRank = -1;
      agreementStatus = RequestAgreementStatus.REFUSED;
      overallStatus = RequestOverallStatus.REFUSED;
    } else if (lastAppliedVisaStatus == RequestAgreementVisaStatus.GRANTED) {
      // Let's check if granted visa is the last one
      Integer rankOfLastExpectedVisa =
          ruleAud
              .getVisas()
              .stream()
              .max(Comparator.comparing(AgreementRuleVisaAud::getRank))
              .get()
              .getRank();

      if (appliedVisa.getRank().equals(rankOfLastExpectedVisa)) {
        nextAgreementVisaRank = -1;
        agreementStatus = RequestAgreementStatus.GRANTED;
        overallStatus = RequestOverallStatus.VALIDATED;
      } else {
        nextAgreementVisaRank++;
      }
    } else {
      // Not supposed to happen except business rule gets changed
      throw UNEXPECTED_ERROR.exception(
          "Unknown visaStatus of agreement visa: " + lastAppliedVisaStatus);
    }
  }
  /**
   * Method to call when we want to apply (grant or deny) an agreement visa
   *
   * @param employee
   * @param visaStatus
   * @param comment
   * @return
   */
  public Request applyAgreementVisa(
      Employee employee, RequestAgreementVisaStatus visaStatus, String comment) {
    if (!waitsForAnAgreementVisa()) {
      throw REQUEST_DOES_NOT_EXPECT_ANY_AGREEMENT_VISA.exception();
    }

    if (userHasAlreadyAppliedAVisa(employee.getId())) {
      throw EMPLOYEE_HAS_ALREADY_APPLIED_A_VISA.exception(employee.getFullName());
    }

    AgreementRuleVisaAud nextExpectedVisa =
        getNextExpectedAgreementVisa()
            .orElseThrow(() -> COULD_NOT_FIND_NEXT_EXPECTED_AGREEMENT_VISA.exception());

    Department department = employee.getDepartment();
    Seniority seniority = employee.getSeniority();

    if (!nextExpectedVisa.canBeAppliedBy(department, seniority)) {
      throw VISA_TO_APPLY_DOESNT_MATCH_NEXT_EXPECTED_ONE.exception(
          department.getName(), seniority.getValue(),
          nextExpectedVisa.getDepartment().getName(), nextExpectedVisa.getSeniority().getValue());
    }

    LocalDateTime now = LocalDateTime.now();

    RequestAgreementVisa appliedVisa =
        RequestAgreementVisa.build(
            this,
            null,
            employee,
            visaStatus,
            nextAgreementVisaRank,
            comment,
            department,
            seniority,
            now);

    addAgreementVisa(appliedVisa);

    updateUser = employee.getUid();
    updateDate = now;

    return this;
  }