import { DatePickerTranslations, Internationalization } from './../../constants/constants';
import moment, { Moment } from 'moment';
import { DateUtils } from '../../utils/date-utils';

export enum CALENDAR_VIEWS {
  DAYS,
  MONTHS,
  YEARS,
  DECADES
}

export enum CALENDAR_NAVIGATION {
  FIRST = "first",
  LAST = "last",
  PREV = "prev",
  NEXT = "next"
}

export enum CALENDAR_GRAIN {
  DAY = "$day",
  WEEK = "$week",
  MONTH = "$month",
  YEAR = "$year"
}

export type IDateDisabled = string[] | (({ date }: { date: Date }) => boolean) | undefined;

export interface IDatePreset {
  preset: Date;
  label: string;
}

export interface IRangePreset {
  preset?: [Date, Date];
  label: string;
  action?: () => null;
  includeCurrentDate?: boolean;
}

export interface IDateConfig {
  date?: [Date, Date] | Date | undefined;

  minDate: Date | undefined;
  maxDate: Date | undefined;

  disabled: IDateDisabled;

  page: CALENDAR_VIEWS;
}
export interface IHeaderConfig {
  firstDisabled?: boolean;
  prevDisabled?: boolean;
  lastDisabled?: boolean;
  nextDisabled?: boolean;

  firstTooltip?: string;
  lastTooltip?: string;
  prevTooltip?: string;
  nextTooltip?: string;
}

export const parseDate = (date: Date | string | undefined, dateUtils: DateUtils): Date => {
  if (isString(date)) {
    let dt = dateUtils.getStartOfMinute(moment());
    if (date.startsWith(CALENDAR_GRAIN.DAY)) {
      return dateUtils.addDays(dt, parseInt(date.replace(CALENDAR_GRAIN.DAY, "") || "0", 10));
    } else if (date.startsWith(CALENDAR_GRAIN.WEEK)) {
      return dateUtils.addWeeks(dt, parseInt(date.replace(CALENDAR_GRAIN.WEEK, "") || "0", 10));
    } else if (date.startsWith(CALENDAR_GRAIN.MONTH)) {
      return dateUtils.addMonths(dt, parseInt(date.replace(CALENDAR_GRAIN.MONTH, "") || "0", 10));
    } else if (date.startsWith(CALENDAR_GRAIN.YEAR)) {
      return dateUtils.addYears(dt, parseInt(date.replace(CALENDAR_GRAIN.YEAR, "") || "0", 10));
    } else {
      return dateUtils.toDate(date);
    }
  } else if (date) {
    return date as Date;
  }
  return null;
};

export const parseRange = (date: string | [string, string] | undefined, dateUtils: DateUtils): [Date, Date] => {
  if (isString(date)) {
    if (date.startsWith(CALENDAR_GRAIN.DAY)) {
      const today = dateUtils.getStartOfDay(moment());
      const diff = dateUtils.addDays(moment(), parseInt(date.replace(CALENDAR_GRAIN.DAY, "") || "0", 10));
      return date.includes("-") ? [diff, today] : [today, diff];
    }
    if (date.startsWith(CALENDAR_GRAIN.WEEK)) {
      const diff = parseInt(date.replace(CALENDAR_GRAIN.WEEK, "") || "0", 10);
      return [dateUtils.addWeeks(dateUtils.getStartOfWeek(moment()), diff), dateUtils.addWeeks(dateUtils.getEndOfWeek(moment()), diff)];
    }
    if (date.startsWith(CALENDAR_GRAIN.MONTH)) {
      const diff = parseInt(date.replace(CALENDAR_GRAIN.MONTH, "") || "0", 10);
      return [dateUtils.addMonths(dateUtils.getStartOfMonth(moment()), diff), dateUtils.addMonths(dateUtils.getEndOfMonth(moment()), diff)];
    }
    if (date.startsWith(CALENDAR_GRAIN.YEAR)) {
      const diff = parseInt(date.replace(CALENDAR_GRAIN.YEAR, "") || "0", 10);
      return [dateUtils.addYears(dateUtils.getStartOfYear(moment()), diff), dateUtils.addYears(dateUtils.getEndOfYear(moment()), diff)];
    }
  } else if (isArray(date)) {
    return [parseDate(date[0], dateUtils), parseDate(date[1], dateUtils)];
  }
  return null;
};

export const getTitle = (date: Date, view: CALENDAR_VIEWS, translations, dateUtils: DateUtils) => {
  if (view === CALENDAR_VIEWS.DAYS) {
    return translations.months[date.getMonth()] + " " + dateUtils.format(date, "yyyy");
  }
  if (view === CALENDAR_VIEWS.MONTHS) {
    return dateUtils.format(date, "yyyy");
  }
  if (view === CALENDAR_VIEWS.YEARS) {
    return dateUtils.format(dateUtils.getStartOfDecade(date), "yyyy") + "-" + dateUtils.format(dateUtils.getEndOfDecade(date), "yyyy");
  }
  return "-";
};

export const changeMonth = (date: Date, view: CALENDAR_VIEWS, grain: string, dateUtils: DateUtils): Date => {
  if (view === CALENDAR_VIEWS.DAYS) {
    switch (grain) {
      case CALENDAR_NAVIGATION.FIRST:
        return dateUtils.subtractMonths(date, 12);
      case CALENDAR_NAVIGATION.LAST:
        return dateUtils.addMonths(date, 12);
      case CALENDAR_NAVIGATION.PREV:
        return dateUtils.subtractMonths(date, 1);
      case CALENDAR_NAVIGATION.NEXT:
        return dateUtils.addMonths(date, 1);
    }
  }
  if (view === CALENDAR_VIEWS.MONTHS) {
    switch (grain) {
      case CALENDAR_NAVIGATION.PREV:
        return dateUtils.subtractYears(date, 1);
      case CALENDAR_NAVIGATION.NEXT:
        return dateUtils.addYears(date, 1);
    }
  }
  if (view === CALENDAR_VIEWS.YEARS) {
    switch (grain) {
      case CALENDAR_NAVIGATION.PREV:
        return dateUtils.subtractYears(date, 10);
      case CALENDAR_NAVIGATION.NEXT:
        return dateUtils.addYears(date, 10);
    }
  }
  return date;
};

export const buildHeaderConfig = (date: Date, view: CALENDAR_VIEWS, config: IDateConfig, dateUtils: DateUtils, translations: DatePickerTranslations = Internationalization.datePickerTranslationsEN) => {
  if (view === CALENDAR_VIEWS.DAYS) {
    return {
      firstDisabled: config.minDate ? isBeforeMin(date, dateUtils.getStartOfYear(config.minDate), -12, dateUtils) : false,
      lastDisabled: config.maxDate ? isAfterMax(date, dateUtils.getEndOfYear(config.maxDate), 12, dateUtils) : false,
      prevDisabled: config.minDate ? isBeforeMin(date, dateUtils.getStartOfMonth(config.minDate), -1, dateUtils) : false,
      nextDisabled: config.maxDate ? isAfterMax(date, dateUtils.getEndOfMonth(config.maxDate), 1, dateUtils) : false,

      firstTooltip: getTitle(dateUtils.subtractMonths(date, 12), view, translations, dateUtils),
      lastTooltip: getTitle(dateUtils.addMonths(date, 12), view, translations, dateUtils),
      prevTooltip: getTitle(dateUtils.subtractMonths(date, 1), view, translations, dateUtils),
      nextTooltip: getTitle(dateUtils.addMonths(date, 1), view, translations, dateUtils)
    };
  }
  if (view === CALENDAR_VIEWS.MONTHS) {
    return {
      prevDisabled: config.minDate ? dateUtils.isBeforeYear(dateUtils.subtractMonths(date, 12), config.minDate) : false,
      nextDisabled: config.maxDate ? dateUtils.isAfterYear(dateUtils.addMonths(date, 12), config.maxDate) : false,
      prevTooltip: getTitle(dateUtils.subtractYears(date, 1), view, translations, dateUtils),
      nextTooltip: getTitle(dateUtils.addYears(date, 1), view, translations, dateUtils)
    };
  }
  if (view === CALENDAR_VIEWS.YEARS) {
    const start = dateUtils.getStartOfDecade(date);
    const end = dateUtils.getEndOfDecade(date);
    return {
      prevDisabled: config.minDate ? dateUtils.isBeforeYear(dateUtils.subtractMonths(date, 120), dateUtils.getStartOfDecade(config.minDate)) : false,
      nextDisabled: config.maxDate ? dateUtils.isAfterYear(dateUtils.addMonths(date, 120), dateUtils.getEndOfDecade(config.maxDate)) : false,
      prevTooltip: dateUtils.format(dateUtils.addYears(start, -10), "yyyy") + "-" + dateUtils.format(dateUtils.addYears(start, -1), "yyyy"),
      nextTooltip: dateUtils.format(dateUtils.addYears(end, 1), "yyyy") + "-" + dateUtils.format(dateUtils.addYears(end, 10), "yyyy")
    };
  }
};

export const isBeforeMin = (date: Date, minDate: Date, n: number = 0, dateUtils: DateUtils) => {
  return dateUtils.isValid(minDate) ? dateUtils.isBefore(dateUtils.addMonths(dateUtils.getStartOfDay(date), n), minDate) : false;
};

export const isAfterMax = (date: Date, maxDate: Date, n: number = 0, dateUtils: DateUtils) => {
  return dateUtils.isValid(maxDate) ? dateUtils.isAfter(dateUtils.addMonths(dateUtils.getStartOfDay(date), n), maxDate) : false;
};

export const isDisabled = (config: IDateConfig, date: Date, dateUtils: DateUtils): boolean => {
  let min: Date | Moment = config.minDate;
  let max: Date | Moment = config.maxDate;

  if (config.page === CALENDAR_VIEWS.DAYS) {
    min = dateUtils.getStartOfDay(min);
    max = dateUtils.getStartOfDay(max);
  }
  if (config.page === CALENDAR_VIEWS.MONTHS) {
    min = dateUtils.getStartOfDay(dateUtils.getStartOfMonth(min));
    max = dateUtils.getStartOfDay(dateUtils.getStartOfMonth(max));
  }
  if (config.page === CALENDAR_VIEWS.YEARS) {
    min = dateUtils.getStartOfDay(dateUtils.getStartOfYear(min));
    max = dateUtils.getStartOfDay(dateUtils.getStartOfYear(max));
  }
  if (dateUtils.isBefore(date, min)) {
    return true;
  }
  if (dateUtils.isAfter(date, max)) {
    return true;
  }
  if (config.page === CALENDAR_VIEWS.DAYS && config.disabled) {
    const { disabled } = config;
    if (isArray(disabled)) {
      return disabled.includes(dateUtils.getStartOfDay(date).toISOString());
    } else if (isFunction(disabled)) {
      return disabled({ date });
    }
  }
  return false;
};

