import dayjs, { Dayjs, OpUnitType, QUnitType } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import objectSupport from 'dayjs/plugin/objectSupport';

dayjs.extend(objectSupport);
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(isSameOrAfter);

export interface ISelectedMonth {
  year: number;
  month: number;
}

export const defaultDateFormat = 'MM-DD-YYYY';

export const normalizeDate = (date: Date, type: 'start' | 'end'): Date => {
  return type === 'start'
    ? dayjs(date).utc().startOf('day').toDate()
    : dayjs(date).utc().endOf('day').toDate();
};

export const convertAnyToDate = (date: any): Date | null => {
  let result = null;
  if (!date) {
    result = null;
  }
  if (date instanceof Date) {
    result = date;
  }
  if (date.toString().match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/)) {
    result = new Date(date.slice(0, -1));
  }
  return result;
};

export const dateLessThan = (date: Date, compare: number, unit: QUnitType | OpUnitType) => {
  const currentDate = dayjs().utc();
  const nextDate = dayjs(date).utc();
  const difference = currentDate.diff(nextDate, unit);
  return difference < compare;
};

export const convertDayjsToFormat = (date: Dayjs, format = defaultDateFormat): string => {
  return date.format(format);
};

export const convertDateToFormat = (date: Date, format = defaultDateFormat) => {
  return convertDayjsToFormat(dayjs(date).utc(), format);
};

export const calculateTimeZone = (date: Date): Date => {
  if (date instanceof Date) {
    return dayjs(date).utc().subtract(date.getTimezoneOffset(), 'minute').toDate();
  }
  return date;
};

export const excelDateToJSDate = (serial: number) => {
  const utcDays = Math.floor(serial - 25569);
  const utcValue = utcDays * 86400;
  const dateInfo = new Date(utcValue * 1000);
  const fractionalDay = serial - Math.floor(serial) + 0.0000001;
  let totalSeconds = Math.floor(86400 * fractionalDay);
  const seconds = totalSeconds % 60;
  totalSeconds -= seconds;
  const hours = Math.floor(totalSeconds / (60 * 60));
  const minutes = Math.floor(totalSeconds / 60) % 60;
  return new Date(dateInfo.getFullYear(), dateInfo.getMonth(), dateInfo.getDate(), hours, minutes, seconds);
};

export const convertSelectedMonthToDate = (selectedMonth: ISelectedMonth) => {
  return dayjs.utc({ year: selectedMonth.year, month: selectedMonth.month - 1, day: 1 }).toDate();
};

export const getStartOfMonthsBetweenDates = (startDate: Date, endDate: Date): {
  [dateView: string]: { year: number; month: number }
} => {
  let start = dayjs.utc(startDate).startOf('month');
  const end = dayjs.utc(endDate).startOf('month');
  const dates = {};

  while (start.isBefore(end) || start.isSame(end)) {
    dates[start.format('YYYY-MM')] = <ISelectedMonth>{ year: start.year(), month: start.month() + 1 };
    start = start.add(1, 'month');
  }

  return dates;
};

export const getDays = (period: { start: dayjs.Dayjs, end: dayjs.Dayjs }) => {
  const days: string[] = [];
  for (let day = dayjs(period.start); day.isBefore(period.end); day = day.add(1, 'day')) {
    days.push(day.format('YYYY-MM-DD'));
  }
  return days;
};

export const getShiftedMonth = (month: ISelectedMonth, shift: number) => {
  const nextMonth = dayjs().utc().year(month.year).month(month.month - 1).add(shift, 'month');
  return { year: nextMonth.year(), month: nextMonth.month() + 1 };
};

export const isProductActive = (
  startDate: Date | null | undefined,
  deactivationDate: Date | null | undefined,
  checkDate: Date
) => {
  const check = dayjs(checkDate);

  // If startDate exists and is in the future → not active yet
  if (startDate && dayjs(startDate).isAfter(check)) {
    return false;
  }

  // If no deactivation date → active (and already started)
  if (!deactivationDate) {
    return true;
  }

  const deactivation = dayjs(deactivationDate);

  // Active if checkDate is before deactivationDate
  return check.isBefore(deactivation);
};
