package com.axelor.apps.hr.web;

import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.repo.PeriodRepository;
import com.axelor.apps.hr.db.Absence;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.MonthlyReport;
import com.axelor.apps.hr.db.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.MonthlyReportRepository;
import com.axelor.apps.hr.service.MonthlyReportServiceImpl;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.lang.invoke.MethodHandles;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MonthlyReportController {

  private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  public void createMensuelReport(ActionRequest request, ActionResponse response) {
    try {

      Object object = request.getContext().get("period");
      LinkedHashMap<String, Object> periodMap = (LinkedHashMap<String, Object>) object;
      Integer periodIdInt = (Integer) periodMap.get("id");
      Long periodId = periodIdInt.longValue();
      Period period = Beans.get(PeriodRepository.class).find(periodId);
      LocalDate startDate = period.getFromDate();
      LocalDate endDate = period.getToDate();

      // int remainingDateToLastOfMonth = calculateDaysExcludingFridaysAndThursdays(endDate);

      // Get active employees in this period
      List<Employee> employees =
          Beans.get(EmployeeRepository.class)
              .all()
              .filter("self.quitDate is null or self.quitDate >= :startDate")
              .bind("startDate", startDate)
              .fetch();

      // Fetch all rapport journaliers within the date range for all employees
      List<DailyReport> allDailyReports =
          Beans.get(DailyReportRepository.class)
              .all()
              .filter("self.reportDate >= :startDate and self.reportDate <= :endDate")
              .bind("startDate", startDate)
              .bind("endDate", endDate)
              .fetch();

      // Fetch all absences within the date range for all employees
      List<Absence> allAbsences =
          Beans.get(AbsenceRepository.class)
              .all()
              .filter("DATE(self.startDate) >= :startDate and DATE(self.startDate) <= :endDate")
              .bind("startDate", startDate)
              .bind("endDate", endDate)
              .fetch();

      // Iterate over employees and calculate/update MonthlyReport instances
      for (Employee employee : employees) {
        // Filter rapport journaliers for the current employee
        List<DailyReport> employeeDailyReports =
            allDailyReports
                .stream()
                .filter(r -> r.getEmployee().equals(employee))
                .collect(Collectors.toList());

        // Filter absences for the current employee
        List<Absence> employeeAbsences =
            allAbsences
                .stream()
                .filter(a -> a.getEmployee().equals(employee))
                .collect(Collectors.toList());

        if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) {
          System.out.println(
              "Create monthly report for employee: " + employee.getRegistrationNumber());
          // Process the employee's monthly report using filtered rapport and absences
          Beans.get(MonthlyReportServiceImpl.class)
              .createMensuelReport(
                  employee, period, startDate, endDate, employeeDailyReports, employeeAbsences);
        } else {
          log.error("No Daily Reports exist for employee: " + employee.getRegistrationNumber());
        }
      }
      // Indicate that the action was successful and a reload is needed
      response.setReload(true);
    } catch (Exception e) {
      // Handle any exceptions
      e.printStackTrace();
      response.setError("An error occurred: " + e.getMessage());
    }
  }

  public void updateMensuelReport(ActionRequest request, ActionResponse response) {
    try {

      Object periodObject = request.getContext().get("period");
      LinkedHashMap<String, Object> periodMap = (LinkedHashMap<String, Object>) periodObject;
      Integer periodIdInt = (Integer) periodMap.get("id");
      Long periodId = periodIdInt.longValue();
      Period period = Beans.get(PeriodRepository.class).find(periodId);
      LocalDate startDate = period.getFromDate();
      LocalDate endDate = period.getToDate();

      //
      Object employeesObject = request.getContext().get("employees");
      System.out.println("Employees" + employeesObject);

      // Initialize the employees list
      List<Employee> employees = new ArrayList<>();

      // Check if employeesObject is not null and cast it to a list
      if (employeesObject instanceof List) {
        List<LinkedHashMap<String, Object>> employeesList =
            (List<LinkedHashMap<String, Object>>) employeesObject;

        // Loop through each employee in the list
        for (LinkedHashMap<String, Object> employeeMap : employeesList) {
          Integer employeeIdInt = (Integer) employeeMap.get("id");

          // Check for null ID to avoid potential NullPointerException
          if (employeeIdInt != null) {
            Long employeeId = employeeIdInt.longValue();

            // Retrieve the employee from the repository
            Employee employee = Beans.get(EmployeeRepository.class).find(employeeId);

            if (employee != null) {
              employees.add(employee); // Use add() instead of append()
            } else {
              System.out.println("Employee with ID " + employeeId + " not found.");
            }
          } else {
            System.out.println("Employee ID is missing in the employeeMap.");
          }
        }
      } else {
        response.setFlash("No employees Selected.");
      }

      // int remainingDateToLastOfMonth = calculateDaysExcludingFridaysAndThursdays(endDate);
      List<Integer> employeesIds = new ArrayList<>();
      for (Employee e : employees) {
        employeesIds.add(e.getId().intValue());
      }

      // Fetch all rapport journaliers within the date range for all employees
      List<DailyReport> allDailyReports =
          Beans.get(DailyReportRepository.class)
              .all()
              .filter(
                  "self.employee in :employeesIds and self.reportDate >= :startDate and self.reportDate <= :endDate")
              .bind("startDate", startDate)
              .bind("endDate", endDate)
              .bind("employeesIds", employeesIds)
              .fetch();

      // Fetch all absences within the date range for all employees
      List<Absence> allAbsences =
          Beans.get(AbsenceRepository.class)
              .all()
              .filter(
                  "self.employee in :employeesIds and DATE(self.startDate) >= :startDate and DATE(self.startDate) <= :endDate")
              .bind("startDate", startDate)
              .bind("endDate", endDate)
              .bind("employeesIds", employeesIds)
              .fetch();

      for (Employee employee : employees) {
        // Check if a MonthlyReport exists for this employee in the specified period
        MonthlyReport monthlyReport =
            Beans.get(MonthlyReportRepository.class)
                .all()
                .filter("self.employee = :employee and self.period = :period")
                .bind("employee", employee)
                .bind("period", period)
                .fetchOne();

        Optional<MonthlyReport> monthlyReportOpt = Optional.ofNullable(monthlyReport);

        // Filter daily reports for the current employee
        List<DailyReport> employeeDailyReports =
            allDailyReports
                .stream()
                .filter(r -> r.getEmployee().equals(employee))
                .collect(Collectors.toList());

        // Filter absences for the current employee
        List<Absence> employeeAbsences =
            allAbsences
                .stream()
                .filter(a -> a.getEmployee().equals(employee))
                .collect(Collectors.toList());

        if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) {
          if (monthlyReportOpt.isPresent()) {
            MonthlyReport existingReport = monthlyReportOpt.get();
            System.out.println(
                "Update monthly report for employee: " + employee.getRegistrationNumber());
            // Update the existing monthly report
            Beans.get(MonthlyReportServiceImpl.class)
                .updateMensuelReport(
                    existingReport,
                    employee,
                    period,
                    startDate,
                    endDate,
                    employeeDailyReports,
                    employeeAbsences);
          } else {
            System.out.println(
                "Create monthly report for employee: " + employee.getRegistrationNumber());
            // Create a new monthly report
            Beans.get(MonthlyReportServiceImpl.class)
                .createMensuelReport(
                    employee, period, startDate, endDate, employeeDailyReports, employeeAbsences);
          }
        } else {
          System.err.println(
              "No Daily Reports exist for employee: " + employee.getRegistrationNumber());
        }
      }

      // Indicate that the action was successful and a reload is needed
      response.setReload(true);
    } catch (Exception e) {
      // Handle any exceptions
      e.printStackTrace();
      response.setError("An error occurred: " + e.getMessage());
    }
  }

  /*public void calculePrimeAssiduite(ActionRequest request, ActionResponse response) {
    try {
      Long monthlyReportId = (Long) request.getContext().asType(MonthlyReport.class).getId();
      MonthlyReport monthlyReport = Beans.get(MonthlyReportRepository.class).find(monthlyReportId);

      Beans.get(MonthlyReportServiceImpl.class).calculePrimeAssiduite(monthlyReport);

      // Indicate that the action was successful and a reload is needed
      response.setReload(true);
    } catch (Exception e) {
      // Handle any exceptions
      e.printStackTrace();
      response.setError("An error occurred: " + e.getMessage());
    }
  }*/

  private int calculateDaysExcludingFridaysAndThursdays(LocalDate endDate) {
    // Get the last day of the month for endDate
    LocalDate lastDayOfMonth = endDate.with(TemporalAdjusters.lastDayOfMonth());

    // Calculate the number of days from endDate to lastDayOfMonth excluding Fridays and Thursdays
    int daysToLastDateExcludingFridaysAndThursdays = 0;
    LocalDate currentDate = endDate.plusDays(1); // Start from the day after endDate

    while (!currentDate.isAfter(lastDayOfMonth)) {
      DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
      if (dayOfWeek != DayOfWeek.FRIDAY && dayOfWeek != DayOfWeek.THURSDAY) {
        daysToLastDateExcludingFridaysAndThursdays++;
      }
      currentDate = currentDate.plusDays(1); // Move to the next day
    }

    return daysToLastDateExcludingFridaysAndThursdays;
  }
}
