package com.axelor.apps.hr.service;

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.DailyReport;
import com.axelor.apps.hr.db.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
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.temporal.ChronoUnit;
import java.util.List;

public class AbsenceServiceImpl implements AbsenceService {

  private final AbsenceRepository absenceRepository;
  private final DailyReportRepository dailyReportRepository;
  private List<EventsPlanningLine> eventsPlanningLines;

  @Inject
  public AbsenceServiceImpl(
      AbsenceRepository absenceRepository, DailyReportRepository dailyReportRepository) {
    this.absenceRepository = absenceRepository;
    this.dailyReportRepository = dailyReportRepository;
    eventsPlanningLines =
        Beans.get(EventsPlanningLineRepository.class).all().filter("self.id = 4").fetch();
  }

  @Transactional
  public void attachTheAbsenceWithDailyReport(Absence absence, List<DailyReport> dailyreports) {
    // Iterate over each DailyReport in the list
    for (DailyReport dailyreport : dailyreports) {
      dailyreport.addAbsenceSetItem(absence); // Set the absence for each report
      dailyReportRepository.save(dailyreport);
    }
  }

  @Transactional
  public void deAttachTheAbsenceWithDailyReport(
      Absence absence, List<DailyReport> dailyreports, Boolean archive) {
    if (dailyreports != null) {
      // Iterate over each DailyReport in the list
      for (DailyReport dailyreport : dailyreports) {
        dailyreport.removeAbsenceSetItem(null); // Set the absence for each report
        dailyReportRepository.save(dailyreport);
      }
    }

    if (archive) {
      absence.setArchived(true);
      absenceRepository.save(absence);
    }
  }

  @Transactional
  public void chooseAbsenceType(Absence absence, Integer selectedType) {
    // Set the selected absence type to the absence entity
    // Assuming you have a field named 'typeAbsence' in the Absence entity
    absence.setAbsenceType(selectedType);

    // Save the absence entity
    absenceRepository.save(absence);
  }

  public BigDecimal calculateTotalAbsenceHours(
      LocalDateTime absenceStartDate, LocalDateTime absenceEndDate) {
    BigDecimal totalAbsenceHours = BigDecimal.ZERO;

    if (absenceStartDate.equals(absenceEndDate)) {
      totalAbsenceHours = BigDecimal.valueOf(8);
    } else if (absenceStartDate.toLocalDate().equals(absenceEndDate.toLocalDate())) {
      // If the start and end dates are the same, calculate the hour difference
      long hours = ChronoUnit.HOURS.between(absenceStartDate, absenceEndDate);
      totalAbsenceHours = BigDecimal.valueOf(hours);
    } else {
      long absenceDays = 0;
      LocalDate currentDate = absenceStartDate.toLocalDate();

      // Loop through each day between start and end date
      while (!currentDate.isAfter(absenceEndDate.toLocalDate())) {
        DayOfWeek dayOfWeek = currentDate.getDayOfWeek();

        // Exclude Friday (5) and Saturday (6) and holidays
        if (dayOfWeek != DayOfWeek.FRIDAY
            && dayOfWeek != DayOfWeek.SATURDAY
            && !isSpecialOvertimeDay(currentDate)) {
          absenceDays++;
        }
        currentDate = currentDate.plusDays(1);
      }

      // Multiply the counted days by 8 hours per day
      totalAbsenceHours = BigDecimal.valueOf(absenceDays).multiply(BigDecimal.valueOf(8));
    }
    return totalAbsenceHours;
  }

  public BigDecimal calculateTotalAbsenceMinutes(
      LocalDateTime absenceStartDate, LocalDateTime absenceEndDate) {
    // Calculate the duration between the two LocalDateTime objects
    Duration duration = Duration.between(absenceStartDate, absenceEndDate);

    // Convert the duration to minutes and then divide by 60 to get hours
    long minutes = duration.toMinutes();
    BigDecimal hours =
        BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, BigDecimal.ROUND_HALF_UP);

    return customRound(hours);
  }

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

  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
    }
  }
}
