package com.banesco.common.domain.dto.bian.device;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Clase utilitaria para operaciones relacionadas con fechas y horas.
 * Proporciona métodos para validar, comparar y calcular períodos entre fechas.
 */
public class DateHelper {


    private final Logger logger = Logger.getLogger(DateHelper.class.getName());
    private final ZoneId currentZone = ZoneId.of("America/Caracas");
    private final DateTimeFormatter DATE_YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private final DateTimeFormatter TIME_HH_MM_SS = DateTimeFormatter.ofPattern("HH:mm:ss");

    private static class SingletonHelper {
        private static final DateHelper INSTANCE = new DateHelper();
    }


    /**
     * Constructo private to no allow other Instances
     */
    private DateHelper() {
        // not public instance
    }

    /**
     * Get Instance Singleton
     *
     * @return
     */
    public static DateHelper getInstance() {
        return SingletonHelper.INSTANCE;
    }

    /**
     * Permite obtener la Fecha Hora Actual
     *
     * @return
     */
    public LocalDateTime getCurrentDateTime() {
        return LocalDateTime.now(currentZone);
    }

    /**
     * Permite Obtener la Hora para una FechaHora, en caso de ser null se obtiene la hora actual
     *
     * @param now Feha Hora Local
     * @return
     */
    public String getTime(LocalDateTime now) {
        if (now == null) {
            now = LocalDateTime.now(currentZone);
        }
        return now.format(TIME_HH_MM_SS);
    }

    /**
     * Permite Obtener la Fecha para una FechaHora, en caso de ser null se obtiene la fecha actual
     *
     * @param now Feha Hora Local
     * @return
     */
    public String getDate(LocalDateTime now) {
        if (now == null) {
            now = LocalDateTime.now(currentZone);
        }
        return now.format(DATE_YYYY_MM_DD);
    }

    /**
     * Check is Current Hour is in  Hour Range passed by parameters
     * if Parameters are equal then return false
     *
     * @param rangeHourBegin  Cadena de la hora de inicio del Rango
     * @param rangeHourFinish Candena de la hora de fin del Rango
     * @param hourStrToCheck  Hora en cadena para evaluar si se encuentra entre el rango de parametros
     * @return boolean
     */
    public boolean isHourInRange(String rangeHourBegin, String rangeHourFinish, String hourStrToCheck) {

        return isHourInRange(Integer.parseInt(rangeHourBegin.replace(":", "")),
                Integer.parseInt(rangeHourFinish.replace(":", "")),
                hourStrToCheck
        );

    }

    /**
     * Check is Current Hour is in  Hour Range passed by parameters
     * if Parameters are equal then return false
     *
     * @param rangeHourBegin  Numero entero de la hora de inicio del Rango
     * @param rangeHourFinish Numero entero de la hora de fin del Rango
     * @param hourStrToCheck  Hora en cadena para evaluar si se encuentra entre el rango de parametros
     * @return boolean
     */
    public boolean isHourInRange(int rangeHourBegin, int rangeHourFinish, String hourStrToCheck) {
        boolean mustValidateService = false;

        if (rangeHourBegin == rangeHourFinish) {
            return mustValidateService;
        }

        int currentHour = Integer.parseInt(hourStrToCheck.replace(":", ""));


        if (rangeHourBegin > rangeHourFinish) { // hour end is less than hour start, => then validate next day

            if (currentHour <= rangeHourFinish || currentHour >= rangeHourBegin) {
                mustValidateService = true;
            }
        } else if (currentHour >= rangeHourBegin && currentHour <= rangeHourFinish) { // Hour the same day
            mustValidateService = true;
        }
        logger.log(Level.INFO, "La hora (%s => %d) %sesta entre %d y %d ".formatted(
                hourStrToCheck,
                currentHour,
                (mustValidateService ? "" : "NO "),
                rangeHourBegin,
                rangeHourFinish));

        return mustValidateService;
    }

    /**
     * Calcula el número de días entre dos fechas.
     *
     * @param startDate Fecha de inicio en formato ISO (yyyy-MM-dd)
     * @param endDate   Fecha de fin en formato ISO (yyyy-MM-dd)
     * @return El número de días entre las fechas o -1 si ocurre un error al
     * procesar las fechas
     */
    public long daysBetweenTwoDates(String startDate, String endDate) {
        try {
            return ChronoUnit.DAYS.between(LocalDate.parse(startDate), LocalDate.parse(endDate));
        } catch (DateTimeException dte) {
            return -1L;
        }
    }

    /**
     * Verifica si la fecha de inicio es anterior o igual a la fecha de fin.
     *
     * @param startDate Fecha de inicio en formato ISO (yyyy-MM-dd)
     * @param endDate   Fecha de fin en formato ISO (yyyy-MM-dd)
     * @return true si startDate es anterior o igual a endDate, false en caso
     * contrario
     */
    public boolean isStartDateBeforeEndDate(String startDate, String endDate) {
        return LocalDate.parse(startDate).isBefore(LocalDate.parse(endDate))
                || LocalDate.parse(startDate).isEqual(LocalDate.parse(endDate));
    }
}
