import type { Dayjs, OpUnitType } from 'dayjs';

import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';

// ----------------------------------------------------------------------

dayjs.extend(duration);
dayjs.extend(relativeTime);

// ----------------------------------------------------------------------

export type DatePickerFormat = Dayjs | Date | string | number | null | undefined;


export const dateFormat = {
  dateTime: 'DD MMM YYYY h:mm a', // 17 Apr 2022 12:00 am
  date: 'DD MMM YYYY', // 17 Apr 2022
  time: 'h:mm a', // 12:00 am
  split: {
    dateTime: 'DD/MM/YYYY h:mm a', // 17/04/2022 12:00 am
    date: 'DD/MM/YYYY', // 17/04/2022
  },
  paramCase: {
    dateTime: 'DD-MM-YYYY h:mm a', // 17-04-2022 12:00 am
    date: 'DD-MM-YYYY', // 17-04-2022
  },
  isoDate: 'YYYY-MM-DD',
  dateMonthYear: 'MM/YYYY',
};

export function getToday(format?: string) {
  return dayjs(new Date()).startOf('day').format(format);
}

// ----------------------------------------------------------------------

/** output: 17 Apr 2022 12:00 am
 */
export function formatDateTime(date: DatePickerFormat, format?: string) {
  if (!date) {
    return null;
  }

  const isValid = dayjs(date).isValid();

  return isValid ? dayjs(date).format(format ?? dateFormat.dateTime) : false;
}

// ----------------------------------------------------------------------

/** output: 17 Apr 2022
 */
export function formatDate(date: DatePickerFormat, format?: string) {
  if (!date) {
    return null;
  }

  const isValid = dayjs(date).isValid();

  return isValid ? dayjs(date).format(format ?? dateFormat.date) : false;
}

// ----------------------------------------------------------------------

/** output: 12:00 am
 */
export function formatTime(date: DatePickerFormat, format?: string) {
  if (!date) {
    return null;
  }

  const isValid = dayjs(date).isValid();

  return isValid ? dayjs(date).format(format ?? dateFormat.time) : false;
}

// ----------------------------------------------------------------------

/** output: 1713250100
 */
export function getTimestamp(date: DatePickerFormat) {
  if (!date) {
    return null;
  }

  const isValid = dayjs(date).isValid();

  return isValid ? dayjs(date).valueOf() : false;
}

// ----------------------------------------------------------------------

/** output: a few seconds, 2 years
 */
export function getRelativeTime(date: DatePickerFormat) {
  if (!date) {
    return null;
  }

  const isValid = dayjs(date).isValid();

  return isValid ? dayjs(date).toNow(true) : false;
}

// ----------------------------------------------------------------------

/** output: boolean
 */
export function isDateInRange(
  inputDate: DatePickerFormat,
  startDate: DatePickerFormat,
  endDate: DatePickerFormat
) {
  if (!inputDate || !startDate || !endDate) {
    return false;
  }

  const formattedInputDate = getTimestamp(inputDate);
  const formattedStartDate = getTimestamp(startDate);
  const formattedEndDate = getTimestamp(endDate);

  if (formattedInputDate && formattedStartDate && formattedEndDate) {
    return formattedInputDate >= formattedStartDate && formattedInputDate <= formattedEndDate;
  }

  return false;
}

// ----------------------------------------------------------------------

/** output: boolean
 */
export function isDateAfter(startDate: DatePickerFormat, endDate: DatePickerFormat) {
  return dayjs(startDate).isAfter(endDate);
}

// ----------------------------------------------------------------------

/** output: boolean
 */
export function isSameDate(
  startDate: DatePickerFormat,
  endDate: DatePickerFormat,
  units?: OpUnitType
) {
  if (!startDate || !endDate) {
    return false;
  }

  const isValid = dayjs(startDate).isValid() && dayjs(endDate).isValid();

  if (!isValid) {
    return false;
  }

  return dayjs(startDate).isSame(endDate, units ?? 'year');
}

// ----------------------------------------------------------------------

/** output:
 * Same day: 26 Apr 2024
 * Same month: 25 - 26 Apr 2024
 * Same month: 25 - 26 Apr 2024
 * Same year: 25 Apr - 26 May 2024
 */
export function getShortDateRangeLabel(
  startDate: DatePickerFormat,
  endDate: DatePickerFormat,
  initial?: boolean
) {
  const isValid = dayjs(startDate).isValid() && dayjs(endDate).isValid();

  const isAfter = isDateAfter(startDate, endDate);

  if (!isValid || isAfter) {
    false;
  }

  let label = `${formatDate(startDate)} - ${formatDate(endDate)}`;

  if (initial) {
    return label;
  }

  const isSameYear = isSameDate(startDate, endDate, 'year');
  const isSameMonth = isSameDate(startDate, endDate, 'month');
  const isSameDay = isSameDate(startDate, endDate, 'day');

  if (isSameYear && !isSameMonth) {
    label = `${formatDate(startDate, 'DD MMM')} - ${formatDate(endDate)}`;
  } else if (isSameYear && isSameMonth && !isSameDay) {
    label = `${formatDate(startDate, 'DD')} - ${formatDate(endDate)}`;
  } else if (isSameYear && isSameMonth && isSameDay) {
    label = `${formatDate(endDate)}`;
  }

  return label;
}

// ----------------------------------------------------------------------

export type DurationProps = {
  years?: number;
  months?: number;
  days?: number;
  hours?: number;
  minutes?: number;
  seconds?: number;
  milliseconds?: number;
};

/** output: '2024-05-28T05:55:31+00:00'
 */
export function addDuration({
  years = 0,
  months = 0,
  days = 0,
  hours = 0,
  minutes = 0,
  seconds = 0,
  milliseconds = 0,
}: DurationProps) {
  const result = dayjs()
    .add(
      dayjs.duration({
        years,
        months,
        days,
        hours,
        minutes,
        seconds,
        milliseconds,
      })
    )
    .format();

  return result;
}

/** output: '2024-05-28T05:55:31+00:00'
 */
export function subtractDuration({
  years = 0,
  months = 0,
  days = 0,
  hours = 0,
  minutes = 0,
  seconds = 0,
  milliseconds = 0,
}: DurationProps) {
  const result = dayjs()
    .subtract(
      dayjs.duration({
        years,
        months,
        days,
        hours,
        minutes,
        seconds,
        milliseconds,
      })
    )
    .format();

  return result;
}
