// import { format, startOfMonth, endOfMonth, eachDayOfInterval, startOfWeek, endOfWeek, isSameMonth } from 'date-fns';



const levenshtein = require('fastest-levenshtein');

const fs = typeof window === 'undefined' ? require('fs') : undefined;
const path = typeof window === 'undefined' ? require('path') : undefined;
const { PrismaClient, UserRole } = require('@prisma/client');
const DayOfWeek = require("@prisma/client").DayOfWeek;
const winston = require('winston');
const { getSession } = require("next-auth/react");
const { DateTime, FixedOffsetZone } = require('luxon');

const logger = winston.createLogger({
    level: 'info', // Set the default log level
    format: winston.format.json(), // Choose a log format
    transports: [
        // Define where logs should be output (e.g., to a file or the console)
        new winston.transports.Console(),
    ],
});

exports.logger = logger;

// import { DayOfWeek } from "@prisma/client";

//const UserRole = require("@prisma/client").UserRole; - does not allow usage of UserRole in code
// import { UserRole } from '@prisma/client';

// const getConfig = require("next/config");
// exports.nextconfig = getConfig();
// //const { serverRuntimeConfig } = getConfig();

// const dotenv = require("dotenv");
// dotenv.config();
// // dotenv.config({ path: ".env.local" });

exports.adjustUtcTimeToSofia = function (time) {
    // Convert the Date object to a Luxon DateTime object in UTC
    let result = DateTime.fromJSDate(time, { zone: 'utc' });
    // Convert to Sofia time, retaining the local time as provided
    result = result.setZone('Europe/Sofia', { keepLocalTime: true });
    // Set hours, minutes, and seconds to match the input time
    result = result.set({
        hour: time.getHours(),
        minute: time.getMinutes(),
        second: time.getSeconds()
    });
    return result.toJSDate();
};

exports.adjustTimeToUTC = function (time) {
    let result = DateTime.fromJSDate(time, { zone: 'Europe/Sofia' });
    result = result.setZone('utc', { keepLocalTime: true });
    return result.toJSDate();
};


exports.isValidPhoneNumber = function (phone) {
    if (typeof phone !== 'string') {
        return false; // or handle as you see fit
    }
    // Remove spaces and dashes for validation
    const cleanedPhone = phone.replace(/\s|-/g, '');

    // Check if the phone starts with '08' and has 10 digits
    if ((cleanedPhone.startsWith('08') || cleanedPhone.startsWith('07')) && cleanedPhone.length === 10) {
        return true;
    }

    // Check if the phone starts with '+359' and has the correct length
    if (cleanedPhone.startsWith('+359') && cleanedPhone.length === 13) {
        return true;
    }

    // If neither condition is met, the phone number is invalid
    return false;
}

exports.getBaseUrl = function (relative = "", req = null) {
    return process.env.NEXT_PUBLIC_PUBLIC_URL + relative;
};


let prisma;
exports.getPrismaClient = function getPrismaClient() {
    if (!prisma) {
        logger.debug("getPrismaClient: process.env.DATABASE = ", process.env.DATABASE);
        prisma = new PrismaClient({
            // Optional: Enable Prisma logging
            log: ['query', 'info', 'warn', 'error'],
            datasources: { db: { url: process.env.DATABASE } },
        });
    }
    logger.debug("getPrismaClient: process.env.DATABASE = ", process.env.DATABASE);

    return prisma;
}


exports.removeAccentsAndSpecialCharacters = function (str) {
    return str.normalize("NFD") // split an accented letter in the base letter and the accent
        .replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
        .replace(/[^a-zA-Z0-9]/g, ""); // remove all chars not letters and not digits
}

Date.prototype.getDayEuropean = function () {
    const day = this.getDay();
    return (day === 0) ? 6 : day - 1; // Convert 0 (Sunday) to 6, and decrement other days by 1
};

exports.getDayOfWeek = function (date) {
    return date.getDayEuropean();
};
// Helper function to convert month name to 0-based index
exports.getMonthNames = function () {
    return ["януари", "февруари", "март", "април", "май", "юни", "юли", "август", "септември", "октомври", "ноември", "декември"];
}
exports.getMonthIndex = function (monthName) {
    return exports.getMonthNames().indexOf(monthName.toLowerCase());
};

exports.getMonthName = function (monthIndex) {
    return exports.getMonthNames()[monthIndex];
};


exports.getMonthNameEn = function (monthIndex) {
    const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    return monthNames[monthIndex];
};

exports.dayOfWeekNames = ["понеделник", "вторник", "сряда", "четвъртък", "петък", "събота", "неделя"];
exports.DaysOfWeekArray = [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday];

// Helper function to convert day of week name to 0-based index (Sunday = 0)
exports.getDayOfWeekIndex = function (dayOfWeekName) {
    dayOfWeekName = dayOfWeekName.toLowerCase().trim();
    const abbreviatedDayOfWeekNames = ["пон", "вт", "ср", "четв", "пет", "съб", "нед"];
    const enDayOfWeekNames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].map((d) => d.toLowerCase());

    let index = exports.dayOfWeekNames.indexOf(dayOfWeekName);

    // If not found in the full names, look in the abbreviated names
    if (index === -1) {
        index = abbreviatedDayOfWeekNames.indexOf(dayOfWeekName);
    }
    if (index === -1) {
        index = enDayOfWeekNames.indexOf(dayOfWeekName);
    }
    logger.debug("getDayOfWeekIndex: dayOfWeekName = ", dayOfWeekName, "index = ", index);
    return index;
};


exports.getDayOfWeekName = function (date) {
    const dayOfWeekIndex = date.getDayEuropean();
    return exports.dayOfWeekNames[dayOfWeekIndex];
};

exports.getDayOfWeekNameEnEnumForDate = function (date) {
    date = new Date(date);
    const dayOfWeekIndex = date.getDayEuropean();
    return exports.DaysOfWeekArray[dayOfWeekIndex];
}

//obsolete: we want to ensure getDayEuropean() is used, hense we will not use this function
// exports.getDayOfWeekNameEnEnum = function (dayOfWeekIndex) {
//     //return enum instead of string
//     // const dayOfWeekNames: Record<number, DayOfWeek> = {
//     //     0: DayOfWeek.Monday,
//     //     1: DayOfWeek.Tuesday,
//     //     2: DayOfWeek.Wednesday,
//     //     3: DayOfWeek.Thursday,
//     //     4: DayOfWeek.Friday,
//     //     5: DayOfWeek.Saturday,
//     //     6: DayOfWeek.Sunday
//     // };
//     return exports.DaysOfWeekArray[dayOfWeekIndex];

// }
exports.getPubTypeEnum = function (text) {
    const input = text.trim();
    const mapping = {
        'Вестител': 'Publisher',
        'Бетелит': 'Bethelite',
        'Редовен Пионер': 'RegularPioneer',
        'Специален Пионер/Мисионер': 'SpecialPioneer_Missionary',
    };

    // Default to 'Publisher' if the input does not match any key
    return mapping[input] || 'Publisher';
}


/*
sets the date portion of the date object to the correct day of week, keeping the time portion
*/
exports.getDayOfWeekDate = function (dayOfWeekName, date = new Date()) {
    const targetDay = exports.DaysOfWeekArray.indexOf(dayOfWeekName); // 0 = Sunday, 1 = Monday, etc.
    if (targetDay === -1) {
        throw new Error('Invalid day of week name provided');
    }
    // Adjust currentDay to match our custom mapping
    let currentDay = date.getDayEuropean();
    const daysDifference = targetDay - currentDay;
    date.setDate(date.getDate() + daysDifference);
    return date;
};
//common.getWeekOfMonth(date)
exports.getWeekOfMonth = function (inputDate) { //getWeekOfMonth?
    let date = new Date(inputDate);
    let firstDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
    let firstMonday = new Date(firstDayOfMonth);

    // Adjust firstDayOfMonth to the first Monday of the month
    if (firstDayOfMonth.getDay() === 0) { // Sunday
        firstMonday.setDate(2);
    } else if (firstDayOfMonth.getDay() !== 1) { // Not Monday
        firstMonday.setDate(9 - firstDayOfMonth.getDay());
    }

    // Calculate the difference in days
    let diff = (date - firstMonday) / (1000 * 60 * 60 * 24);
    // Calculate week number
    let weekNumber = Math.ceil((diff + 1) / 7);

    return weekNumber;
};

exports.getDateFromWeekNrAndDayOfWeek = function (firstMonday, weekNr, dayOfWeekEnum, startTime) {
    firstMonday = new Date(firstMonday);
    startTime = new Date(startTime);
    if (!weekNr || weekNr < 1 || weekNr > 5) {
        weekNr = this.getWeekOfMonth(startTime);
    }
    //get int from dayOfWeekEnum
    let dayOfWeekNr = this.getDayOfWeekIndex(dayOfWeekEnum);
    if (dayOfWeekNr < 0 || dayOfWeekNr > 6) {
        dayOfWeekNr = 0;
    }

    // Calculate the day offset from the first Monday of the month
    // Note: Assuming dayOfWeekEnum starts from 0 (Monday) to 6 (Sunday)
    const daysFromFirstMonday = (weekNr - 1) * 7 + dayOfWeekNr;

    // Calculate the new date
    let newStart = new Date(firstMonday);
    newStart.setDate(firstMonday.getDate() + daysFromFirstMonday);

    // Extract time from startTime and apply it to newStart
    const time = new Date(startTime);
    newStart.setHours(time.getHours(), time.getMinutes(), time.getSeconds());

    return newStart;
}

exports.getMonthDatesInfo = function (date) {
    // cast to date if not daate
    date = new Date(date);
    // get first day of the month
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    // get first day of next month
    var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 1);
    // lastDay = new Date(lastDay.getTime() - 1);
    lastDay = new Date(lastDay.getTime() - 86400000); // 86400000 ms = 1 day

    // // get the day of the week of the first day
    var firstDayOfWeek = firstDay.getDay(); // 0 = Sunday, 1 = Monday, etc.
    // calculate the date of the first Monday
    var firstMonday = new Date(firstDay);
    if (firstDayOfWeek !== 1) { // Check if the first day is not already a Monday
        firstMonday.setDate(firstDay.getDate() + ((7 - firstDayOfWeek) % 7) + 1);
    }

    // get last Monday
    var lastMonday = new Date(lastDay);
    if (lastDay.getDay() !== 0) { // Check if the last day is not already a Sunday
        lastMonday.setDate(lastDay.getDate() - ((lastDay.getDay() + 6) % 7));
    } else {
        lastMonday.setDate(lastDay.getDate() - 6);
    }

    // get Sunday of the last week
    var lastSunday = new Date(lastMonday);
    lastSunday.setDate(lastMonday.getDate() + 6);
    var monthName = exports.getMonthName(date.getMonth());


    // var firstDayNextMonth = new Date(date.getFullYear(), date.getMonth() + 1, 1);
    // // Calculate the last Sunday of the current month
    // // .getDay() returns 0 for Sunday, 1 for Monday, ..., 6 for Saturday
    // var lastSunday = new Date(firstDayNextMonth);
    // lastSunday.setDate(firstDayNextMonth.getDate() - firstDayNextMonth.getDay());

    //logger.debug("Last Sunday: ", lastSunday);
    const diffInDays = (lastSunday - firstMonday) / (1000 * 60 * 60 * 24);
    // Calculate number of weeks, rounding up for partial weeks
    const nrOfWeeks = Math.ceil((diffInDays + 1) / 7);

    return {
        firstDay: firstDay,
        lastDay: lastDay,
        firstMonday: firstMonday,
        lastMonday: lastMonday,
        lastSunday: lastSunday,
        date: date,
        monthName: monthName,
        year: date.getFullYear(),
        nrOfWeeks: nrOfWeeks
    };
};
exports.getMonthInfo = exports.getMonthDatesInfo;

exports.getMonthlyScheduleRange = function (date) {
    let info = exports.getMonthDatesInfo(date);
    // get first day of the month
    var firstDay = info.firstDay;
    // get last sunday of the month
    var lastSunday = info.lastSunday;
    return {
        firstDay: firstDay,
        lastSunday: lastSunday
    };
};

exports.getWeekNumber = function (date) {
    let info = exports.getMonthDatesInfo(date);

    // If the date is before the first full week, return 0
    if (date < info.firstMonday) {
        return 0;
    }

    // Calculate the week number based on the first full week
    return Math.ceil((date.getDate() - info.firstMonday.getDate() + 1) / 7);
};

exports.compareTimes = function (time1, time2) {
    const time1String = `${getHours(time1)}:${getMinutes(time1)}`;
    const time2String = `${getHours(time2)}:${getMinutes(time2)}`;
    return time1String.localeCompare(time2String);
};

exports.normalizeTime = function (date, baseDate) {
    let newDate = new Date(baseDate);
    newDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), 0);
    return newDate;
}

exports.getTimeRange = function (start, end) {
    start = new Date(start);
    end = new Date(end);
    const startTime = start.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });
    const endTime = end.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });
    return `${startTime}-${endTime}`;
}

//get date in format сряда 1 юли 2020 г.
exports.getDateFormated = function (date) {
    const dayOfWeekName = exports.getDayOfWeekName(date);
    const day = date.getDate();
    const monthName = exports.getMonthName(date.getMonth());
    const year = date.getFullYear();
    return `${dayOfWeekName} ${day} ${monthName} ${year} г.`;
}

exports.getDateFormattedShort = function (date) {
    if (!date) {
        return "";
    }

    const day = date.getDate();
    const monthName = exports.getMonthName(date.getMonth());
    return `${day} ${monthName}`;
}


exports.getDateTimeFormatted = function (date) {
    //     const startTime = start.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });
    return exports.getISODateOnly(new Date(date)) + " " + exports.getTimeFormatted(date);
}

/*Todo: remove:
toISOString
slice(0, 10)
 
getISODateFromDateTime > getISODateOnly
 
*/
exports.toLocalISO = function (date) {
    // Adjust for timezone and get ISO string without 'Z'
    const offset = date.getTimezoneOffset() * 60000; // offset in milliseconds
    return new Date(date.getTime() - offset).toISOString().slice(0, -1);
};

exports.getISODateOnly = function (date) {
    // Use toLocalISO and get the date part only
    return this.toLocalISO(date).split('T')[0];
};

exports.getDateFromDateTime = function (day) {
    // Adjust for timezone and reset time to midnight
    var date = new Date(day);
    date.setHours(0, 0, 0, 0);
    const offset = date.getTimezoneOffset() * 60000;
    return new Date(date.getTime() - offset);
};


// ###########################################
// remove empty nodes from json

// Function to check if an object is empty or contains only empty nodes
exports.isEmpty = function (obj) {
    for (const key in obj) {
        if (typeof obj[key] === 'object') {
            if (!exports.isEmpty(obj[key])) {
                return false;
            }
        } else if (obj[key] !== '') {
            return false;
        }
    }
    return true;
}

// Function to remove empty nodes from a JSON object
exports.jsonRemoveEmptyNodes = function (obj) {
    const newObj = {};

    for (const key in obj) {
        if (typeof obj[key] === 'object') {
            const cleanedObj = exports.jsonRemoveEmptyNodes(obj[key]);
            if (!exports.isEmpty(cleanedObj)) {
                newObj[key] = cleanedObj;
            }
        } else if (obj[key] !== '') {
            newObj[key] = obj[key];
        }
    }

    return newObj;
}




// #region string functions

//input:Светослав и Сириел Георгиеви
//output: Светослав Георгиев, Сириел Георгиев
//input: Игор и Галина Москвини
//output: Игор Москвин, Галина Москвин
exports.separateFamilyMemberNames = function (namesArray) {
    logger.debug("separateFamilyMemberNames: " + namesArray);
    const result = [];
    // itteraate over all names
    for (let i = 0; i < namesArray.trim().split(' '); i++) {
        const name = namesArray[i];

        // if name contains " и " then split it into two names
        // else just add it to the result
        if (name.includes(" и ")) {
            const namePairs = name.split(" и ");
            const firstNames = namePairs.map((pair) => pair.split(" ")[0]);
            // last name is only one. and it is shared for both firstnames
            const lastName = namePairs[1].split(" ")[1];

            for (let i = 0; i < namePairs.length; i++) {
                let firstName = firstNames[i];

                if (i === namePairs.length - 1 && namePairs.length % 2 !== 0) {
                    result.push(`${firstName} ${lastName}`);
                } else {
                    result.push(`${firstName} ${lastName}`);
                }
            }
        } else {
            result.push(name);
        }
    }

    //normalize names
    for (var i = 0; i < result.length; i++) {
        var name = result[i].trim();
        //cut last letter of name if it is "a" or "и" (bulgarian feminine ending)
        if (name.endsWith("a") || name.endsWith("и")) {
            name = name.substring(0, name.length - 1);
        }
        result[i] = name;
    }

    return result;
}

exports.separateFamilyMemberNames2 = function (input) {
    const nameParts = input.trim().split(' ');
    let familyName = nameParts.pop();

    const firstNames = nameParts.join(' ').split(' и ');
    const result = firstNames.map(firstName => `${firstName} ${familyName}`);
    return result;

}

exports.fuzzySearch = function (publishers, searchQuery, distanceThreshold = 0.9) {
    const lowerCaseSearchQuery = searchQuery?.toLowerCase();

    let results = publishers
        .filter((p) => {
            try {
                const fullName = p.firstName.toLowerCase() + " " + p.lastName.toLowerCase();
                const reversedFullName = p.lastName.toLowerCase() + " " + p.firstName.toLowerCase();

                const distanceFullName = levenshtein.distance(fullName, lowerCaseSearchQuery);
                const distanceReversedFullName = levenshtein.distance(reversedFullName, lowerCaseSearchQuery);

                const similarityFullName = 1 - distanceFullName / Math.max(fullName.length, lowerCaseSearchQuery.length);
                const similarityReversedFullName = 1 - distanceReversedFullName / Math.max(reversedFullName.length, lowerCaseSearchQuery.length);

                // If total fullname length is less than 10 symbols, allow results that are 1 symbol difference
                if ((fullName.length < 10 || reversedFullName.length < 10) && (distanceFullName <= 1 || distanceReversedFullName <= 1)) {
                    return true;
                }

                return similarityFullName >= distanceThreshold || similarityReversedFullName >= distanceThreshold
            } catch (e) {
                logger.error(e);
                console.log(e);
                return false;
            }
        })
        .sort((a, b) => (a.similarity > b.similarity ? -1 : 1));

    return (results && results.length > 0) ? results[0] : null;

}


exports.getCurrentMonthFormatted = function () {
    const getCurrentYearMonth = () => {
        const currentDate = new Date();
        const year = currentDate.getFullYear();
        const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
        return `${year}-${month}`;
    }
    return getCurrentYearMonth();
}

exports.getCurrentYearMonth = () => {
    const currentDate = new Date();
    const year = currentDate.getFullYear();
    const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
    return `${year}-${month}`;
}


// new date FNs
/**
 * Parses an input into a Luxon DateTime object, setting the timezone to 'Europe/Sofia' while keeping the local time.
 * @param {string|Date} input - The input date string or JavaScript Date object.
 * @returns {DateTime} - A Luxon DateTime object with the timezone set to 'Europe/Sofia', preserving the local time.
 */
const parseDate = (input) => {
    let dateTime;

    if (input instanceof DateTime) {
        // If input is already a Luxon DateTime, we adjust the zone only.
        dateTime = input.setZone('Europe/Sofia');
    } else if (typeof input === 'string' || input instanceof Date) {
        // Create a DateTime from the input assuming local timezone to preserve local time when changing the zone.
        dateTime = DateTime.fromJSDate(new Date(input), { zone: 'local' });
        dateTime = dateTime.setZone('Europe/Sofia');
    } else {
        // Use the current time if no input is given, considered as local time.
        dateTime = DateTime.local().setZone('Europe/Sofia');
    }

    // Set the timezone to 'Europe/Sofia' while keeping the original local time.
    return dateTime.setZone('Europe/Sofia', { keepLocalTime: true });
};
exports.parseDate = parseDate;

// Set timezone to 'Europe/Sofia' without translating time
exports.setTimezone = (input) => {
    let dateTime = parseDate(input);
    dateTime = dateTime.setZone('Europe/Sofia', { keepLocalTime: true });
    return dateTime.toJSDate();
};
exports.setTime = (baseDateTime, timeDateTime) => {
    // Ensure both inputs are DateTime objects
    baseDateTime = parseDate(baseDateTime);
    timeDateTime = parseDate(timeDateTime);

    return baseDateTime.set({
        hour: timeDateTime.hour,
        minute: timeDateTime.minute,
        second: timeDateTime.second,
        millisecond: timeDateTime.millisecond
    });
};

exports.timeToInteger = (hours, minutes) => {
    return hours * 100 + minutes;
}

// Format date to a specified format, defaulting to 'HH:mm'
exports.getTimeFormatted = (input, format = 'HH:mm') => {
    if (!input) return "";
    const dateTime = parseDate(input);
    return dateTime.toFormat(format);
};

// Set time in 'HH:mm' format to a date and return as JS Date in Sofia timezone
exports.setTimeHHmm = (input, timeString) => {
    let dateTime = parseDate(input);
    const [hour, minute] = timeString.split(':').map(Number);
    dateTime = dateTime.set({ hour, minute });
    return dateTime.toJSDate();
};

// Parse 'HH:mm' time string to a JS Date object in Sofia timezone for today
exports.parseTimeHHmm = (timeString) => {
    const dateTime = DateTime.now({ zone: 'Europe/Sofia' });
    const [hour, minute] = timeString.split(':').map(Number);
    return dateTime.set({ hour, minute }).toJSDate();
};

function isTimeBetween(startTime, endTime, checkTime) {
    const start = new Date(0, 0, 0, startTime.getHours(), startTime.getMinutes());
    const end = new Date(0, 0, 0, endTime.getHours(), endTime.getMinutes());
    const check = new Date(0, 0, 0, checkTime.getHours(), checkTime.getMinutes());

    // If the end time is less than the start time, it means the time range spans midnight
    if (end < start) {
        // Check time is between start and midnight or between midnight and end
        return (check >= start && check <= new Date(0, 0, 1, 0, 0, 0)) || (check >= new Date(0, 0, 0, 0, 0, 0) && check <= end);
    } else {
        return check >= start && check <= end;
    }
}
exports.isTimeBetween = isTimeBetween;


// ToDo: update all uses of this function to use the new one

// exports.getTimeFormatted = function (date) {
//     const dateTime = DateTime.fromJSDate(date, { zone: 'Europe/Sofia' });
//     return dateTime.toFormat('HH:mm');
// };

// exports.setTimeHHmm = (date, timeStringOrHours) => {
//     const newDate = new Date(date);

//     if (typeof timeStringOrHours === 'string' && timeStringOrHours.includes(':')) {
//         // If hours is a string in "HH:mm" format
//         const [h, m] = timeStringOrHours.split(':');
//         newDate.setHours(parseInt(h, 10), parseInt(m, 10), 0, 0);
//     } else {
//         // If hours and minutes are provided separately
//         newDate.setHours(parseInt(timeStringOrHours, 10), 0, 0, 0);
//     }

//     return newDate;
// };
// // format date to 'HH:mm' time string required by the time picker
// exports.formatTimeHHmm = function (input) {
//     // Check if the input is a string or a Date object
//     const date = (typeof input === 'string') ? new Date(input) : input;

//     return date.toLocaleTimeString('en-US', {
//         hour12: false,
//         hour: '2-digit',
//         minute: '2-digit',
//         timeZone: 'Europe/Sofia'
//     }).substring(0, 5);
// }


// //parse 'HH:mm' time string to date object
// exports.parseTimeHHmm = (timeString) => {
//     // If timeString is already a date, return it as is
//     if (timeString instanceof Date) {
//         return timeString;
//     }

//     const [hours, minutes] = timeString.split(':');
//     const date = new Date();
//     date.setHours(hours);
//     date.setMinutes(minutes);
//     return date;
// }



exports.getTimeInMinutes = (dateOrTimestamp) => {
    const date = new Date(dateOrTimestamp);
    logger.debug("getTimeInMinutes: date = ", date);
    return date.getHours() * 60 + date.getMinutes();
};

exports.parseBool = function (value) {
    if (value === undefined) {
        return false;
    }
    value = value || "false";
    const truthyValues = ['1', 'y', 'yes', 'true', 't'];
    return truthyValues.includes(String(value).toLowerCase());
}

exports.getStartOfDay = function (date) {
    const result = new Date(date); // create a copy of the input date
    result.setHours(0, 0, 0, 0); // set time to midnight
    return result;
}
exports.getEndOfDay = function (date) {
    const result = new Date(date);
    result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
    return result;
}

exports.getStartOfWeek = function (date) {
    const result = new Date(date); // create a copy of the input date
    // If the day is Sunday (0), we set it to -6, otherwise, subtract the current day of the week from 1 (for Monday)
    let daysToSubtract = result.getDay() === 0 ? 6 : result.getDay() - 1;
    result.setDate(result.getDate() - daysToSubtract);
    result.setHours(0, 0, 0, 0); // set time to midnight
    return result;
}
exports.getEndOfWeek = function (date) {
    const result = new Date(date);
    // If the day is Sunday (0 in `getDay()`), no addition is needed. Otherwise, add 7 minus the current day of the week.
    let daysToAdd = result.getDay() === 0 ? 0 : 7 - result.getDay();
    result.setDate(result.getDate() + daysToAdd);
    result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
    return result;
}
exports.getStartOfMonth = function (date) {
    date = new Date(date);
    return new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);
}
exports.getEndOfMonth = function (date) {
    date = new Date(date);
    const result = new Date(date.getFullYear(), date.getMonth() + 1, 0); // 0th day of the next month
    result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
    return result;
}

exports.excelSerialDateToDate = function (serial) {
    // Set base date as 1899-12-31 (Excel's zero date, ignoring leap year bug)
    let baseDate = new Date(1899, 11, 31);

    // Extract date and time fractions
    let datePart = Math.floor(serial);
    let timePart = serial - datePart;

    // Compute the complete date
    let dateInMs = baseDate.getTime() + datePart * 24 * 60 * 60 * 1000;
    let timeInMs = timePart * 24 * 60 * 60 * 1000;

    // Construct and return the final date
    return new Date(dateInMs + timeInMs);
}
// Function to get a value from the session
exports.getSessionValue = function (session, fieldName, defaultValue = null) {
    return session[fieldName] || defaultValue;
};

// Function to update a value in the session
exports.updateSessionValue = function (session, fieldName, value) {
    session[fieldName] = value;
    session.save(); // Save the session after updating
};


exports.copyToClipboard = function (event, text) {
    let contentToCopy;

    if (event) {
        const spanElement = event.currentTarget;
        contentToCopy = spanElement.textContent;

        // Animation logic
        const originalText = spanElement.textContent;
        const originalColor = spanElement.style.backgroundColor; // Store the original color

        spanElement.textContent = "имейла копиран";
        spanElement.style.backgroundColor = "blue"; // Change color to "info" (blue in this case)

        setTimeout(() => {
            spanElement.textContent = originalText;
            spanElement.style.backgroundColor = originalColor; // Reset the color back to original
        }, 3000); // Reset the text and color back to original after 3 seconds
    } else {
        // If event is null, use the provided text parameter
        if (!text) {
            console.error("No text provided to copy to clipboard");
            return; // Exit the function if no text is provided
        }
        contentToCopy = text;
    }

    // Copy contentToCopy to clipboard
    const tempTextarea = document.createElement('textarea');
    tempTextarea.value = contentToCopy;
    document.body.appendChild(tempTextarea);
    tempTextarea.select();
    document.execCommand('copy');
    document.body.removeChild(tempTextarea);
}




// exports.getUser = async function (req) {
//     // Use req if provided (server-side), otherwise call getSession without args (client-side)
//     const session = req ? await getSession({ req }) : await getSession();
//     return session?.user;
// }

// exports.isUserInRole = function (req, allowedRoles = []) {
//     const user = exports.getUser(req);

//     // Check if the user is authenticated
//     if (!user) {
//         return false;
//     }

//     // If no specific roles are required, return true as the user is authenticated
//     if (allowedRoles.length === 0) {
//         return true;
//     }

//     // Check if the user's role is among the allowed roles
//     // Ensure role exists and is a valid UserRole
//     const userRole = user.role;
//     return allowedRoles.includes(userRole);
// }



// Utility functions for localStorage operations
exports.setLocalStorage = function (key, value) {
    if (typeof window !== 'undefined') {
        window.localStorage.setItem(key, JSON.stringify(value));
    }
};

exports.getLocalStorage = function (key, defaultValue) {
    if (typeof window !== 'undefined') {
        const stored = window.localStorage.getItem(key);
        if (stored) {
            try {
                // Attempt to parse the stored JSON data
                return JSON.parse(stored);
            } catch (error) {
                // Handle parsing error
                console.error(`Error parsing JSON from localStorage for key '${key}':`, error);
                // Return defaultValue or perform other error handling as needed
                return defaultValue;
            }
        }
    }
    return defaultValue;
};

exports.root = function (req) {
    return process.env.NEXT_PUBLIC_PUBLIC_URL;
}

exports.getInitials = function (names) {
    const parts = names.split(' ');
    return parts.map(part => part[0] + ".").join('');
}

// ? import { addMinutes } from 'date-fns';
exports.addMinutes = function (date, minutes) {
    return new Date(date.getTime() + minutes * 60000); // 60000 milliseconds in a minute
}

/**
* Recursively converts all Date objects in an object to ISO strings.
* @param {Object} obj - The object to convert.
* @returns {Object} - The new object with all Date objects converted to ISO strings.
*/
exports.convertDatesToISOStrings = function (obj) {
    if (obj === null || obj === undefined) {
        return obj;
    }

    // if (obj instanceof Date) {
    //     return obj.toISOString();
    // }
    if (obj instanceof Date) {
        // Convert the Date object to a Luxon DateTime in UTC
        const utcDate = DateTime.fromJSDate(obj, { zone: 'utc' });
        return utcDate.toISO(); // Output in UTC as ISO string
    }

    if (Array.isArray(obj)) {
        return obj.map(exports.convertDatesToISOStrings);
    }

    if (typeof obj === 'object') {
        const keys = Object.keys(obj);
        return keys.reduce((acc, key) => {
            acc[key] = exports.convertDatesToISOStrings(obj[key]);
            return acc;
        }, {});
    }

    return obj;
}
function adjustDateForDST(date, timezone) {
    // Convert the date to the specified timezone
    let dateTime = DateTime.fromJSDate(date, { zone: timezone });

    // Check if the original date is in DST
    const isOriginalDST = dateTime.isInDST;

    // Check if the current date in the same timezone is in DST
    const isNowDST = DateTime.now().setZone(timezone).isInDST;

    // Compare and adjust if necessary
    if (isOriginalDST && !isNowDST) {
        // If original date was in DST but now is not, subtract one hour
        dateTime = dateTime.minus({ hours: 1 });
    } else if (!isOriginalDST && isNowDST) {
        // If original date was not in DST but now is, add one hour
        dateTime = dateTime.plus({ hours: 1 });
    }

    // Return the adjusted date as a JavaScript Date
    return dateTime.toJSDate();
}
exports.adjustDateForDST = adjustDateForDST;


exports.base64ToUint8Array = function (base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
        .replace(/-/g, '+')
        .replace(/_/g, '/');
    const rawData = atob(base64);
    const buffer = new Uint8Array(rawData.length);
    for (let i = 0; i < rawData.length; ++i) {
        buffer[i] = rawData.charCodeAt(i);
    }
    return buffer;
}
// exports.getInitials = function (names) {
//     const parts = names.split(' '); // Split the full name into parts
//     if (parts.length === 0) {
//         return '';
//     }
//     // Extract the first two letters of the first name
//     let initials = parts[0].substring(0, 2) + ".";

//     // If there is a last name, add the first letter of the last name
//     if (parts.length > 1) {
//         initials += parts[parts.length - 1][0] + ".";
//     }

//     return initials;
// }