package com.axelor.apps.hr.service;

import com.axelor.apps.base.db.EventsPlanning;
import com.axelor.apps.base.db.EventsPlanningLine;
import com.axelor.apps.base.db.repo.EventsPlanningLineRepository;
import com.axelor.apps.hr.db.Absence;
import com.axelor.apps.hr.db.Authorization;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.OffDayWork;
import com.axelor.apps.hr.db.Shift;
import com.axelor.apps.hr.db.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.AuthorizationRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.OffDayWorkRepository;
import com.axelor.apps.hr.db.repo.ShiftRepository;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;

public class DailyReportServiceImpl implements DailyReportService {

  private final DailyReportRepository dailyReportRepository;
  private final AuthorizationRepository autorizationRepository;
  private final AbsenceRepository absenceRepository;
  private final String HOLIDAYS_STRING = "Planning jours de Féries DZ";
  private EventsPlanning eventsPlanning;
  private List<EventsPlanningLine> eventsPlanningLines;
  private static final LocalTime NIGHT_START = LocalTime.of(21, 0); // 9:00 PM
  private static final LocalTime NIGHT_END = LocalTime.of(5, 0); // 5:00 AM

  private LocalTime SHIFT_8h16_min;
  private LocalTime SHIFT_8h16_max;
  private LocalTime SHIFT_14h22_min;
  private LocalTime SHIFT_14h22_max;
  private LocalTime SHIFT_22h6_min;
  private LocalTime SHIFT_22h6_max;
  private LocalTime SHIFT_6h14_min;
  private LocalTime SHIFT_6h14_max;

  @Inject
  public DailyReportServiceImpl(
      DailyReportRepository dailyReportRepository,
      AuthorizationRepository autorizationRepository,
      AbsenceRepository absenceRepository) {
    this.dailyReportRepository = dailyReportRepository;
    this.autorizationRepository = autorizationRepository;
    this.absenceRepository = absenceRepository;
    eventsPlanningLines =
        Beans.get(EventsPlanningLineRepository.class)
            .all()
            .filter("self.eventsPlanning = 4")
            .fetch();

    SHIFT_8h16_min =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 0").fetchOne().getEnterMin();
    SHIFT_8h16_max =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 0").fetchOne().getEnterMax();
    SHIFT_14h22_min =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 1").fetchOne().getEnterMin();
    SHIFT_14h22_max =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 1").fetchOne().getEnterMax();
    SHIFT_22h6_min =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 2").fetchOne().getEnterMin();
    SHIFT_22h6_max =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 2").fetchOne().getEnterMax();
    SHIFT_6h14_min =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 3").fetchOne().getEnterMin();
    SHIFT_6h14_max =
        Beans.get(ShiftRepository.class).all().filter("self.shift = 3").fetchOne().getEnterMax();
  }

  @Override
  @Transactional
  public void workingHours(DailyReport dailyReport) {

    LocalDateTime[] enters = {
      dailyReport.getEnter1(), dailyReport.getEnter2(), dailyReport.getEnter3(),
      dailyReport.getEnter4(), dailyReport.getEnter5(), dailyReport.getEnter6()
    };
    LocalDateTime[] quits = {
      dailyReport.getQuit1(), dailyReport.getQuit2(), dailyReport.getQuit3(),
      dailyReport.getQuit4(), dailyReport.getQuit5(), dailyReport.getQuit6()
    };

    Employee employee = dailyReport.getEmployee();
    LocalDate reportDate = dailyReport.getReportDate();
    Integer shift = dailyReport.getShift().getShift();

    LocalDateTime firstEnter, lastQuit;

    LocalTime shiftStartHour = null,
        shiftEndHour = null,
        shiftStartPauseHour = null,
        shiftEndPauseHour = null;
    if (shift != null) {
      shiftStartHour = dailyReport.getShift().getStartHour();
      shiftEndHour = dailyReport.getShift().getEndHour();
      shiftStartPauseHour = dailyReport.getShift().getStartPause();
      shiftEndPauseHour = dailyReport.getShift().getEndPause();
    }

    if (enters[0] != null && quits[0] != null) {
      // Calculate total work duration
      Duration totalDuration = Duration.ZERO;
      Duration totalSupDuration = Duration.ZERO;
      Duration nightDuration = Duration.ZERO;
      Duration breakDuration = Duration.ZERO;
      firstEnter = enters[0];
      lastQuit = quits[0];

      for (int i = 0; i < enters.length; i++) {
        if (enters[i] != null && quits[i] != null) {
          // totalDuration = totalDuration.plus(Duration.between(enters[i], quits[i]));
          lastQuit = quits[i];
        }
      }

      dailyReport.setLastQuit(lastQuit);
      LocalTime firstEnterTime = firstEnter.toLocalTime();
      LocalTime lastQuitTime = lastQuit.toLocalTime();

      // Calculate late arrival if firstEnter is later than shift start
      if (shiftStartHour != null) {
        if (firstEnterTime.isAfter(shiftStartHour)) {
          long minutesLate = Duration.between(shiftStartHour, firstEnterTime).toMinutes();
          BigDecimal lateArrival =
              BigDecimal.valueOf(minutesLate)
                  .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
          dailyReport.setLateArrival(lateArrival);
        }
      }

      // Calculate early departure if lastQuit is earlier than shift end
      if (shiftEndHour != null) {
        if (lastQuitTime.isBefore(shiftEndHour)) {
          long minutesEarly = Duration.between(lastQuitTime, shiftEndHour).toMinutes();
          BigDecimal earlyDeparture =
              BigDecimal.valueOf(minutesEarly)
                  .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
          dailyReport.setEarlyDeparture(earlyDeparture);
        }
      }

      // Total hours
      totalDuration = totalDuration.plus(Duration.between(firstEnter, lastQuit));
      long totalMinutes = totalDuration.toMinutes();
      BigDecimal totalHours =
          BigDecimal.valueOf(totalMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
      dailyReport.setWorkHours(totalHours);

      // Calculate night hours
      nightDuration = calculateNightDuration(firstEnter, lastQuit);
      long totalNightMinutes = nightDuration.toMinutes();
      BigDecimal totalNightHours =
          BigDecimal.valueOf(totalNightMinutes)
              .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
      dailyReport.setNightHours(totalNightHours);

      // Break Hours
      breakDuration = calculateBreakDuration(enters, quits);
      long breakMinutes = breakDuration.toMinutes();
      BigDecimal breakHours =
          BigDecimal.valueOf(breakMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
      dailyReport.setBreakHours(breakHours);

      if (shiftStartPauseHour != null && shiftEndPauseHour != null) {
        boolean allInAllowedRange =
            areBreaksInAllowedRange(enters, quits, shiftStartPauseHour, shiftEndPauseHour);
        dailyReport.setBreakNotInTheAllowedRange(allInAllowedRange);
      }

      // shift 2
      if (shift == 2) {
        BigDecimal extraHours50 = BigDecimal.ZERO;
        BigDecimal extraHours100 = BigDecimal.ZERO;

        DayOfWeek enterDay = firstEnter.getDayOfWeek();
        DayOfWeek quitDay = lastQuit.getDayOfWeek();

        LocalDateTime midnight = firstEnter.toLocalDate().atStartOfDay().plusDays(1);

        // Calculate time from first enter to midnight
        Duration beforeMidnightDuration = Duration.between(firstEnter, midnight);
        long beforeMidnightMinutes = beforeMidnightDuration.toMinutes();
        BigDecimal beforeMidnightHours =
            BigDecimal.valueOf(beforeMidnightMinutes)
                .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);

        // Calculate time from midnight to last quit
        Duration afterMidnightDuration = Duration.between(midnight, lastQuit);
        long afterMidnightMinutes = afterMidnightDuration.toMinutes();
        BigDecimal afterMidnightHours =
            BigDecimal.valueOf(afterMidnightMinutes)
                .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);

        if (enterDay == DayOfWeek.THURSDAY && quitDay == DayOfWeek.FRIDAY
            || isSpecialOvertimeDay(lastQuit)) {
          extraHours100 = afterMidnightHours;
          dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
          if (enterDay == DayOfWeek.THURSDAY && quitDay == DayOfWeek.FRIDAY)
            // No Extra Hours ticket needed
            dailyReport.setIsValidSupHours(true);
        } else if (isSpecialOvertimeDay(firstEnter)) {

          // Add recup
          if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0)
            createOffDayWork(reportDate, employee);
          extraHours100 = beforeMidnightHours;
          dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
        } else if (enterDay == DayOfWeek.FRIDAY && quitDay == DayOfWeek.SATURDAY) {

          // Add recup
          if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0)
            createOffDayWork(reportDate, employee);
          extraHours100 = beforeMidnightHours;
          extraHours50 = afterMidnightHours;
          dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);

        } else if ((enterDay == DayOfWeek.SATURDAY && quitDay == DayOfWeek.SUNDAY)) {
          extraHours50 = beforeMidnightHours;
          dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
        } else {
          totalSupDuration = calculateSupplementaryHours(firstEnter, lastQuit, shift, reportDate);
          long totalSupMinutes = totalSupDuration.toMinutes();
          extraHours50 =
              BigDecimal.valueOf(totalSupMinutes)
                  .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
          dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
        }

        dailyReport.setExtraHours100(extraHours100);
        dailyReport.setExtraHours50(extraHours50);

        // other shifts
      } else {
        // Calculate supplementary hours
        totalSupDuration = calculateSupplementaryHours(firstEnter, lastQuit, shift, reportDate);
        long totalSupMinutes = totalSupDuration.toMinutes();
        BigDecimal totalSupHours =
            BigDecimal.valueOf(totalSupMinutes)
                .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);

        // Holidays and weekends
        if (firstEnter.getDayOfWeek() == DayOfWeek.SATURDAY) {

          if (shift == 0 || shift == 3) {
            dailyReport.setExtraHours50(totalHours.subtract(totalNightHours));
            dailyReport.setExtraHours100(totalNightHours);
            dailyReport.setAllowanceRecall(
                totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
          } else {
            dailyReport.setExtraHours50(totalHours);
            dailyReport.setAllowanceRecall(
                totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
          }

        } else if (firstEnter.getDayOfWeek() == DayOfWeek.FRIDAY
            || isSpecialOvertimeDay(firstEnter)) {

          // Add recup
          if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0)
            createOffDayWork(reportDate, employee);
          dailyReport.setExtraHours100(totalHours);
          dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);

        } else {

          if (shift == 0 || shift == 3) {
            dailyReport.setExtraHours50(totalSupHours.subtract(totalNightHours));
            dailyReport.setExtraHours100(totalNightHours);
            dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
            dailyReport.setAllowanceRecall(
                totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
          } else {
            dailyReport.setExtraHours50(totalSupHours);
            dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
            dailyReport.setAllowanceRecall(
                totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
          }
        }
      }

      // Calculate ITP
      dailyReport.setItp(calculateItp(totalHours, shift, dailyReport.getHasItp()));
      dailyReport.setIsCalculated(true);

    } else if (enters[0] != null && quits[0] == null) {
      // When the employee registers attendance only once
      if (shift != 2) {
        dailyReport.setWorkHours(BigDecimal.valueOf(8));
        dailyReport.setAbsenceHours(BigDecimal.valueOf(0));
        dailyReport.setIsCalculated(true);
      }
    } else if (enters.length != quits.length) {

      Duration totalDuration = Duration.ZERO;
      firstEnter = enters[0];
      lastQuit = quits[quits.length - 1];
      totalDuration = totalDuration.plus(Duration.between(firstEnter, lastQuit));
      long totalMinutes = totalDuration.toMinutes();
      BigDecimal totalHours =
          BigDecimal.valueOf(totalMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
      dailyReport.setWorkHours(totalHours);
      dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);

      dailyReport.setLastQuit(lastQuit);
      LocalTime firstEnterTime = firstEnter.toLocalTime();
      LocalTime lastQuitTime = lastQuit.toLocalTime();
      // Calculate late arrival if firstEnter is later than shift start
      if (firstEnterTime.isAfter(shiftStartHour)) {
        long minutesLate = Duration.between(shiftStartHour, firstEnterTime).toMinutes();
        BigDecimal lateArrival =
            BigDecimal.valueOf(minutesLate).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
        dailyReport.setLateArrival(lateArrival);
      }

      // Calculate early departure if lastQuit is earlier than shift end
      if (lastQuitTime.isBefore(shiftEndHour)) {
        long minutesEarly = Duration.between(lastQuitTime, shiftEndHour).toMinutes();
        BigDecimal earlyDeparture =
            BigDecimal.valueOf(minutesEarly)
                .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
        dailyReport.setEarlyDeparture(earlyDeparture);
      }

    } else if (employee == null) {
      System.err.println("Warning: Employee doesn't exist.");
      dailyReport.setIsCalculated(true);
    } else {
      dailyReport.setWorkHours(BigDecimal.valueOf(0));
      dailyReport.setNightHours(BigDecimal.valueOf(0));
      dailyReport.setBreakHours(BigDecimal.valueOf(0));
      dailyReport.setExtraHours50(BigDecimal.valueOf(0));
      dailyReport.setExtraHours100(BigDecimal.valueOf(0));
      dailyReport.setItp(BigDecimal.valueOf(0));
      dailyReport.setLateArrival(BigDecimal.valueOf(0));
      dailyReport.setEarlyDeparture(BigDecimal.valueOf(0));
      dailyReport.setAllowance(0);
      dailyReport.setAllowanceRecall(0);
      dailyReport.setBreakNotInTheAllowedRange(false);
      dailyReport.setShift(Beans.get(ShiftRepository.class).find(50L));
      System.err.println("Warning: no attendances for this day.");
      dailyReport.setIsCalculated(true);
    }

    // Absences
    if (dailyReport.getAbsence() == null) {
      Absence absence =
          Beans.get(AbsenceRepository.class)
              .all()
              .filter(
                  "self.employee = :employee and self.startDate <= :reportDate and self.endDate >= :reportDate and (self.archived = false or self.archived is null)")
              .bind("employee", employee)
              .bind(
                  "reportDate",
                  reportDate) // Changed from absenceStartDate and absenceEndDate to reportDate
              .fetchOne();

      if (absence != null) {
        dailyReport.setAbsence(absence);
      }
    }

    // Authorization
    if (dailyReport.getAuthorizationList() == null) {
      List<Authorization> authorizations =
          Beans.get(AuthorizationRepository.class)
              .all()
              .filter("self.employee = :employee and self.requisitionDate = :reportDate")
              .bind("employee", employee)
              .bind("reportDate", reportDate)
              .fetch();

      if (authorizations != null) {
        List<Authorization> authorizationList =
            new ArrayList<>(); // Create a new list for authorizations

        for (Authorization authorization : authorizations) {
          authorization.setDailyReport(dailyReport);
          authorizationList.add(authorization); // Add each authorization to the list

          // Check authorization type and set corresponding flags in dailyReport
          if (authorization.getAuthorizationType() == 0 && authorization.getStatusSelect() == 3) {
            dailyReport.setIsAuthorizedEarlyDeparture(true);
          }
          if (authorization.getAuthorizationType() == 1 && authorization.getStatusSelect() == 3) {
            dailyReport.setIsAuthorizedLateArrival(true);
          }
          if (authorization.getAuthorizationType() == 2 && authorization.getStatusSelect() == 3) {
            dailyReport.setIsAuthorizedAbsence(true);
          }
        }
        // Set the authorization list to dailyReport
        dailyReport.setAuthorizationList(authorizationList);
      }
    }

    // Holidays
    if (isSpecialOvertimeDay(reportDate.atStartOfDay())) {
      dailyReport.setIsFerieDay(true);
    }

    // Weekends
    if (reportDate.getDayOfWeek() == DayOfWeek.FRIDAY
        || reportDate.getDayOfWeek() == DayOfWeek.SATURDAY) {
      dailyReport.setIsWeekend(true);
    }

    // Absence Hours
    if (!(dailyReport.getIsWeekend() || dailyReport.getIsFerieDay())
        && dailyReport.getWorkHours().compareTo(BigDecimal.valueOf(8)) < 0) {
      dailyReport.setAbsenceHours(BigDecimal.valueOf(8).subtract(dailyReport.getWorkHours()));
      // Create Absence AI
      if (dailyReport.getAbsenceSet().isEmpty()) {
        Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence();
        Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival();
        Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture();
        // AI all day
        if (dailyReport.getAbsenceHours().compareTo(new BigDecimal("8")) == 0) {
          if (!isAuthorizedAbsence) {
            Absence absence = new Absence();
            absence.setEmployee(employee);
            absence.setAbsenceType(19); // Absence irrégulière
            absence.setStartDate(reportDate.atStartOfDay());
            absence.setEndDate(reportDate.atStartOfDay());
            BigDecimal totalAbsenceHours =
                Beans.get(AbsenceServiceImpl.class)
                    .calculateTotalAbsenceHours(
                        reportDate.atStartOfDay(), reportDate.atStartOfDay());
            absence.setTotalAbsenceHours(totalAbsenceHours);
            absenceRepository.save(absence);
            dailyReport.addAbsenceSetItem(absence);
          }
        } else {
          if (dailyReport.getShift().getMaxTimeLateArrival()
              != null) { // to check that is different to shift N/A
            LocalTime firstEnterTime = dailyReport.getEnter1().toLocalTime();
            if (firstEnterTime.isAfter(dailyReport.getShift().getMaxTimeLateArrival())
                && !isAuthorizedLateArrival) {
              Absence absence = new Absence();
              absence.setEmployee(employee);
              absence.setAbsenceType(20); // Retard irrégulier
              absence.setStartDate(reportDate.atTime(shiftStartHour));
              absence.setEndDate(dailyReport.getEnter1());
              BigDecimal totalAbsenceHours =
                  Beans.get(AbsenceServiceImpl.class)
                      .calculateTotalAbsenceMinutes(
                          reportDate.atTime(shiftStartHour), dailyReport.getEnter1());
              absence.setTotalAbsenceHours(totalAbsenceHours);
              absenceRepository.save(absence);
              dailyReport.addAbsenceSetItem(absence);
            }
            if (dailyReport.getLastQuit() != null) {
              LocalTime lastQuitTime = dailyReport.getLastQuit().toLocalTime();
              if (lastQuitTime.isBefore(dailyReport.getShift().getMaxTimeEarlyDeparture())
                  && !isAuthorizedEarlyDeparture) {
                Absence absence = new Absence();
                absence.setEmployee(employee);
                absence.setAbsenceType(21); // Départ irrégulier
                absence.setStartDate(dailyReport.getLastQuit());
                absence.setEndDate(reportDate.atTime(shiftEndHour));
                BigDecimal totalAbsenceHours =
                    Beans.get(AbsenceServiceImpl.class)
                        .calculateTotalAbsenceMinutes(
                            dailyReport.getLastQuit(), reportDate.atTime(shiftEndHour));
                absence.setTotalAbsenceHours(totalAbsenceHours);
                absenceRepository.save(absence);
                dailyReport.addAbsenceSetItem(absence);
              }
            }
          }
        }
      }
    } else {
      dailyReport.setAbsenceHours(BigDecimal.ZERO);
    }

    dailyReportRepository.save(dailyReport);
  }

  @Transactional
  public void determineShift(DailyReport dailyReport) {
    LocalDateTime enters1 = dailyReport.getEnter1();
    if (enters1 != null) {
      // Extract time from enters1
      LocalTime firstTime = enters1.toLocalTime();

      // Define shifts
      Shift shift;
      if (firstTime.isAfter(SHIFT_6h14_min) && firstTime.isBefore(SHIFT_6h14_max)) {
        shift = Beans.get(ShiftRepository.class).find(3L); // Shift 6h14
      } else if (firstTime.isAfter(SHIFT_8h16_min) && firstTime.isBefore(SHIFT_8h16_max)) {
        shift = Beans.get(ShiftRepository.class).find(4L); // Shift 8h16
      } else if (firstTime.isAfter(SHIFT_14h22_min) && firstTime.isBefore(SHIFT_14h22_max)) {
        shift = Beans.get(ShiftRepository.class).find(1L); // Shift 14h22
      } else if (firstTime.isAfter(SHIFT_22h6_min) || firstTime.isBefore(SHIFT_22h6_max)) {
        shift = Beans.get(ShiftRepository.class).find(2L); // Shift 22h6
      } else {
        shift = Beans.get(ShiftRepository.class).find(50L); // N/A
      }
      dailyReport.setShift(shift);
      dailyReportRepository.save(dailyReport);
    }
  }

  public void workingHoursForAll(List<DailyReport> DailyReportList) {
    for (int i = 0; i < DailyReportList.size(); i++) {
      // System.out.println(DailyReportList.get(i).getId());
      workingHours(DailyReportList.get(i));
    }
    System.out.println("Calculation Done");
  }

  private boolean isSpecialOvertimeDay(LocalDateTime date) {
    LocalDate givenDate = date.toLocalDate();
    for (EventsPlanningLine line : eventsPlanningLines) {
      if (line.getDate().equals(givenDate)) {
        return true;
      }
    }
    return false;
  }

  private Duration calculateNightDuration(LocalDateTime enter, LocalDateTime quit) {

    // Create LocalDateTime objects for night start and end
    LocalDateTime nightStartDateTime = enter.toLocalDate().atTime(NIGHT_START);
    LocalDateTime nightEndDateTime = enter.toLocalDate().plusDays(1).atTime(NIGHT_END);

    // Initialize night duration to zero
    Duration nightDuration1 = Duration.ZERO;

    // Check if there is an overlap with the night period
    if (enter.isBefore(nightEndDateTime) && quit.isAfter(nightStartDateTime)) {

      // Calculate effective start and end times for night hours
      LocalDateTime effectiveStart =
          enter.isBefore(nightStartDateTime) ? nightStartDateTime : enter;
      LocalDateTime effectiveEnd = quit.isAfter(nightEndDateTime) ? nightEndDateTime : quit;

      // Calculate and return the night duration
      if (effectiveStart.isBefore(effectiveEnd)) {
        nightDuration1 = Duration.between(effectiveStart, effectiveEnd);
      }
    }

    return nightDuration1;
  }

  private Duration calculateSupplementaryHours(
      LocalDateTime enter, LocalDateTime quit, int shift, LocalDate reportDate) {

    // Calculate Supp hours for (0,1,2,3) shifts
    Shift shiftInstance =
        Beans.get(ShiftRepository.class)
            .all()
            .filter("self.shift = :shift")
            .bind("shift", shift)
            .fetchOne();

    LocalTime shiftStart = shiftInstance.getStartHour();
    LocalTime shiftEnd = shiftInstance.getEndHour();

    // Create LocalDateTime objects for shift start and end
    LocalDateTime shiftStartDateTime = reportDate.atTime(shiftStart);
    LocalDateTime shiftEndDateTime = quit.toLocalDate().atTime(shiftEnd);

    // Initialize supplementary duration to zero
    Duration supDuration = Duration.ZERO;

    // Calculate supplementary duration before shift start
    if (enter.isBefore(shiftStartDateTime)) {
      Duration beforeShiftDuration = Duration.between(enter, shiftStartDateTime);
      if (beforeShiftDuration.toMinutes() > 30) {
        supDuration = supDuration.plus(beforeShiftDuration);
      }
    }

    // Calculate supplementary duration after shift end
    if (quit.isAfter(shiftEndDateTime)) {
      Duration afterShiftDuration = Duration.between(shiftEndDateTime, quit);
      if (afterShiftDuration.toMinutes() > 30) {
        supDuration = supDuration.plus(afterShiftDuration);
      }
    }

    return supDuration;
  }

  private Duration calculateBreakDuration(LocalDateTime[] enters, LocalDateTime[] quits) {

    Duration breakDuration = Duration.ZERO;
    for (int i = 1; i < quits.length; i++) {
      if (enters[i] != null && quits[i - 1] != null) {
        breakDuration = breakDuration.plus(Duration.between(quits[i - 1], enters[i]));
      }
    }
    return breakDuration;
  }

  private boolean areBreaksInAllowedRange(
      LocalDateTime[] enters,
      LocalDateTime[] quits,
      LocalTime allowedStartTime,
      LocalTime allowedEndTime) {

    for (int i = 1; i < quits.length; i++) {
      if (enters[i] != null && quits[i - 1] != null) {
        LocalTime breakStartTime = quits[i - 1].toLocalTime();
        LocalTime breakEndTime = enters[i].toLocalTime();

        // Check if the break falls outside the allowed range
        if (breakStartTime.isBefore(allowedStartTime) || breakEndTime.isAfter(allowedEndTime)) {
          return false;
        }
      }
    }

    return true;
  }

  private BigDecimal calculateItp(BigDecimal totalHours, Integer shift, Boolean hasItp) {
    // Shift 0 (no itp)
    if (hasItp == true && shift != 0) return totalHours.min(BigDecimal.valueOf(8));
    else return BigDecimal.ZERO;
  }

  private void createOffDayWork(LocalDate reportDate, Employee employee) {

    // Vérification si l'enregistrement existe déjà
    OffDayWork existingOffDayWork =
        Beans.get(OffDayWorkRepository.class)
            .all()
            .filter("self.employee = :employee AND self.offDay = :reportDate")
            .bind("employee", employee)
            .bind("reportDate", reportDate)
            .fetchOne();

    if (existingOffDayWork == null) {
      // Si aucun enregistrement n'est trouvé, créer un nouveau OffDayWork
      OffDayWork offDayWork = new OffDayWork();
      offDayWork.setEmployee(employee);
      offDayWork.setOffDay(reportDate);
      Beans.get(OffDayWorkRepository.class).save(offDayWork);
    }
  }

  public boolean checkShiftChange(Employee employee, LocalDate reportDate, Integer currentShift) {

    LocalDate firstDayOfWeek = getPreviousWeekSunday(reportDate);
    DailyReport rapportOfFirstDayOfWeek =
        dailyReportRepository
            .all()
            .filter("self.employee = :employee and self.reportDate = :firstDayOfWeek")
            .bind("employee", employee)
            .bind("firstDayOfWeek", firstDayOfWeek)
            .fetchOne();

    Integer firstShift = rapportOfFirstDayOfWeek.getShift().getShift();
    // Corrected print statements
    System.out.println("The day: " + reportDate + " , Shift: " + currentShift);
    System.out.println("The Sunday day: " + firstDayOfWeek + " , Shift: " + firstShift);
    System.out.println("Result: " + !currentShift.equals(firstShift));
    return !currentShift.equals(firstShift);
  }

  // private Duration calculateAbsencesHours(LocalDateTime quit, int shift){ }
  private static LocalDate getPreviousWeekSunday(LocalDate date) {
    if (date.getDayOfWeek() == DayOfWeek.SUNDAY) {
      return date;
    }

    // Find the day of week for the given date
    DayOfWeek dayOfWeek = date.getDayOfWeek();
    // Calculate the number of days to the previous Sunday
    int daysToPreviousSunday = dayOfWeek.getValue() + 7 - DayOfWeek.SUNDAY.getValue();
    // Calculate the previous Sunday date
    return date.minusDays(daysToPreviousSunday);
  }

  public static BigDecimal customRound(BigDecimal value) {
    // Get the fractional part of the number
    BigDecimal fractionalPart = value.remainder(BigDecimal.ONE);

    // Define the intervals and their corresponding rounded values
    if (fractionalPart.compareTo(new BigDecimal("0.125")) < 0) {
      return value.setScale(0, RoundingMode.DOWN);
    } else if (fractionalPart.compareTo(new BigDecimal("0.125")) >= 0
        && fractionalPart.compareTo(new BigDecimal("0.375")) < 0) {
      return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.25"));
    } else if (fractionalPart.compareTo(new BigDecimal("0.375")) >= 0
        && fractionalPart.compareTo(new BigDecimal("0.625")) < 0) {
      return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.50"));
    } else if (fractionalPart.compareTo(new BigDecimal("0.625")) >= 0
        && fractionalPart.compareTo(new BigDecimal("0.875")) < 0) {
      return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.75"));
    } else if (fractionalPart.compareTo(new BigDecimal("0.875")) >= 0) {
      return value.setScale(0, RoundingMode.UP);
    } else {
      return value; // In case no rounding is needed
    }
  }

  @Transactional(rollbackOn = {Exception.class})
  public void deducePrimes(
      DailyReport dailyReport, Integer primeSelection, BigDecimal valueToDeduce)
      throws AxelorException {
    switch (primeSelection) {
      case 1: // ITP
        dailyReport.setDeduceItp(true);
        dailyReport.setItpToDeduce(valueToDeduce);
        break;
      case 2: // Nuissance
        dailyReport.setDeduceNuissance(true);
        dailyReport.setItpToDeduce(valueToDeduce);
        break;
      case 3: // HS 50
        dailyReport.setDeduceSupHours50(true);
        dailyReport.setSupHours50ToDeduce(valueToDeduce);
        break;
      case 4: // HS 100
        dailyReport.setDeduceSupHours100(true);
        dailyReport.setSupHours100ToDeduce(valueToDeduce);
        break;
      default:
        return; // Invalid configSelect, stop processing
    }
    Beans.get(DailyReportRepository.class).save(dailyReport);
  }

  @Transactional(rollbackOn = {Exception.class})
  public void massDeducePrimes(
      List<DailyReport> dailyReportList, Integer primeSelection, BigDecimal valueToDeduce)
      throws AxelorException {
    for (DailyReport dailyReport : dailyReportList) {
      this.deducePrimes(dailyReport, primeSelection, valueToDeduce);
    }
  }
}
