@Override
  public List<AuditLogDTO> getAuditLogs() {

    DAOFactory factory = new DAOFactory(em);

    List<AuditLogDTO> result = new LinkedList<>();

    for (IAuditLog item : factory.getAuditLogDAO().findAll()) {
      result.add(new AuditLogDTO(item));
    }
    return result;
  }
  // it is not specified if we should use a transaction in here
  // nevertheless it seams legit to do so, since otherwise in case of an exception we might have
  // marked a lecture as paid even if we have not created a bill for it
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  @Asynchronous
  @Override
  public Future<BillDTO> getBillForLecturer(String lecturerName) throws Exception {

    BillDTO billDTO = new BillDTO();
    billDTO.setLecturerName(lecturerName);
    List<BillDTO.BillPerLecture> bills = new LinkedList<>();
    billDTO.setBills(bills);

    DAOFactory factory = new DAOFactory(em);

    List<ILecturer> lecturers = factory.getLecturerDAO().findByName(lecturerName);

    if (lecturers == null || lecturers.size() != 1) {
      throw new AssignmentException("Unable to uniquely identify the requested lecturer.");
    }

    ILecturer lecturer = lecturers.get(0);

    // the order by is just required to make the unit test work
    // in addition the LectureStreamingDAO.findAll method had to be changed (order by clause added)
    Query qry =
        em.createQuery(
                "select distinct l from Lecture l join fetch l.lectureStreaming ls join fetch ls.classrooms c where l.lecturer = :lecturer and l.isPaid = false and ls.status = :status order by ls.id",
                Lecture.class)
            .setParameter("lecturer", lecturer)
            .setParameter("status", LectureStatus.FINISHED);
    List<ILecture> unpaidLectures = qry.getResultList();
    int cntPaidLectures =
        em.createQuery("from Lecture  where isPaid = true and lecturer = :lecturer")
            .setParameter("lecturer", lecturer)
            .getResultList()
            .size();

    BigDecimal totalPrice = new BigDecimal(0);

    System.out.println("Unpaid: " + unpaidLectures.size() + "; Paid: " + cntPaidLectures);

    for (ILecture lecture : unpaidLectures) {
      BillDTO.BillPerLecture billPerLecture = billDTO.new BillPerLecture();
      bills.add(billPerLecture);

      billPerLecture.setLectureId(lecture.getId());
      billPerLecture.setNumberOfClassrooms(lecture.getLectureStreaming().getClassrooms().size());

      BigDecimal streamingCosts = new BigDecimal(0);

      int streamingTime =
          lecture
              .getStreamingTime(); // getDateDiff(lecture.getLectureStreaming().getStart(),
                                   // lecture.getLectureStreaming().getEnd(), TimeUnit.MINUTES);

      System.out.println("Lecture: " + lecture.getId() + "; Time: " + streamingTime);
      BigDecimal reducedPercentage = new BigDecimal(1);

      for (IClassroom classroom : lecture.getLectureStreaming().getClassrooms()) {

        BigDecimal costsPerStudent =
            classroom.getVirtualSchool().getMOCPlatform().getCostsPerStudent();
        System.out.println(
            "Classroom: " + classroom.getId() + "; CostsPerStudent: " + costsPerStudent);

        // this code makes one important assumption: the platform is going to be the same for every
        // classroom this lecture is streamed at
        // this is required for calculating the discount for the setupCosts correctly
        // however, the assignment doesn't specify how to handle such situations, as usual!
        List<Double> discountsList =
            em.createQuery(
                    "select m.discount from Membership m where m.id.lecturer = :lecturer and m.id.mocPlatform = :platform",
                    Double.class)
                .setParameter("lecturer", lecturer)
                .setParameter("platform", classroom.getVirtualSchool().getMOCPlatform())
                .getResultList();
        System.out.print("DiscountListSize: " + discountsList.size());
        if (discountsList.size() == 1) {
          reducedPercentage = new BigDecimal(1).subtract(BigDecimal.valueOf(discountsList.get(0)));
          System.out.print("; ReducedPercentage: " + reducedPercentage);
        }
        System.out.println();

        // naming something costsPerStudent while we must not multiply it with the amount of
        // students is so weird
        BigDecimal streamingCostsForClassroom =
            costsPerStudent.multiply(
                new BigDecimal(
                    streamingTime
                    /** classroom.getStudentCapacity() */
                    ));

        streamingCosts = streamingCosts.add(streamingCostsForClassroom.multiply(reducedPercentage));
        System.out.println(
            "StreamingCostsForClassroom: "
                + streamingCostsForClassroom
                + "; StreamingCosts: "
                + streamingCosts);
      }

      BigDecimal setupCosts =
          priceManagementBean.getPrice(cntPaidLectures).multiply(reducedPercentage);
      billPerLecture.setSetupCosts(setupCosts);

      BigDecimal totalCosts = setupCosts.add(streamingCosts);

      System.out.println("SetupCosts: " + setupCosts + "; LectureCosts: " + totalCosts);

      billPerLecture.setStreamingCosts(streamingCosts);
      billPerLecture.setLectureCosts(totalCosts);

      cntPaidLectures++;
      totalPrice = totalPrice.add(totalCosts);

      lecture.setPaid(true);
      em.persist(lecture);
    }

    billDTO.setTotalPrice(totalPrice);

    return new AsyncResult<BillDTO>(billDTO);
  }