// todo wrap store getters into functions
// eslint-disable-next-line no-restricted-imports
import {
  parse,
  isDate,
  parseISO,
  fromUnixTime,
  isPast,
  sub,
  isValid,
  differenceInDays,
  differenceInMonths,
  differenceInYears,
  getDay,
  getMonth,
  getYear,
  addMonths,
  subMonths,
  subHours,
  startOfDay,
  startOfMonth,
  startOfYear,
} from 'date-fns';
// eslint-disable-next-line no-restricted-imports
import { format, utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
import ru from 'date-fns/locale/ru';
import en from 'date-fns/locale/en-US';
import store from '@state/store';
import { DateFormat } from '@constants/utils/format-date';
import { isNumber, isString } from 'lodash';

export { DateFormat };

declare interface Locales {
  [key: string]: Locale;
}

declare type DateWithNull = Date | string | number | null;
declare type DateWithoutNull = Date | string | number;
declare type DateString = Date | string;
declare type DateTimestamp = string | number;

const locales: Locales = {
  ru: ru,
  en: en,
};

const BACKEND_TIMEZONE = 'Europe/Moscow';

function parseFormatToUnicodeMarkers(format: string) {
  return format
    .replace('DD', 'dd')
    .replace('YYYY', 'yyyy')
    .replace('T', `'T'`)
    .replace('Z', 'xxx');
}

export function convertDateFromBackendFormat(
  date: DateWithoutNull,
  dateFormat: string = DateFormat.datetime
): string {
  try {
    return date
      ? format(
          utcToZonedTime(date, store.getters['utils/app/timezone']),
          parseFormatToUnicodeMarkers(dateFormat),
          {
            timeZone: store.getters['utils/app/timezone'],
            locale: locales[store.getters['i18N/locale']] || locales.ru,
          }
        )
      : '';
  } catch (e) {
    console.error(
      `[utils/format-date/convertDateFromBackendFormat] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function convertDateToBackendFormat(
  date: DateWithNull,
  dateFormat: string = DateFormat.datetime
): string {
  try {
    const parsedDate = isDate(date)
      ? date
      : parse(
          <string>date,
          parseFormatToUnicodeMarkers(dateFormat),
          new Date(),
          {
            locale: store.getters['i18N/locale'],
          }
        );
    return format(
      zonedTimeToUtc(<Date>parsedDate, BACKEND_TIMEZONE),
      parseFormatToUnicodeMarkers(DateFormat.backend)
    ).slice(0, -3);
  } catch (e) {
    console.error(
      `[utils/format-date/convertDateToBackendFormat] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function convertDateFormat(
  date: DateWithNull,
  fromFormat: string = DateFormat.date,
  toFormat: string = DateFormat.datetime
): string {
  try {
    const parsedDate = isDate(date)
      ? date
      : parse(
          <string>date,
          parseFormatToUnicodeMarkers(fromFormat),
          new Date()
        );
    return format(<Date>parsedDate, parseFormatToUnicodeMarkers(toFormat), {
      locale: locales[store.getters['i18N/locale']],
    });
  } catch (e) {
    console.error(
      `[utils/format-date/convertDateFormat] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function convertDateFormatWithoutLocale(
  date: DateString,
  toFormat: string = DateFormat.date,
  fromFormat: string | null = null
): string {
  try {
    const parsedDate = isDate(date)
      ? date
      : fromFormat
      ? parse(<string>date, parseFormatToUnicodeMarkers(fromFormat), new Date())
      : parseISO(<string>date);
    return format(parsedDate, parseFormatToUnicodeMarkers(toFormat));
  } catch (e) {
    console.error(
      `[utils/format-date/convertDateFormatWithoutLocale] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function convertDateFromTimestamp(
  date: DateTimestamp,
  toFormat: string = DateFormat.datetime
): string {
  try {
    return format(
      fromUnixTime(<number>date),
      parseFormatToUnicodeMarkers(toFormat)
    );
  } catch (e) {
    console.error(
      `[utils/format-date/convertDateFromTimestamp] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function parseDateFromString(date: string, format: string): Date | null {
  try {
    return parse(date, parseFormatToUnicodeMarkers(format), new Date(), {});
  } catch (e) {
    console.error(
      `[utils/format-date/parseDateFromString] value:'${date}' - ${e.toString()}`
    );
    return null;
  }
}

export function parseDate(date: string | number, format: string): Date | null {
  if (isString(date) && format !== 'timestamp') {
    return parseDateFromString(date, format);
  }
  date = isNumber(date) ? date : parseInt(date);
  return new Date(date * 1000);
}

export function getDifferenceBetweenDates(
  mode: string,
  firstDate: Date | null,
  secondDate: Date | null
): number {
  try {
    if (firstDate && secondDate) {
      switch (mode) {
        case 'days':
          return differenceInDays(firstDate, secondDate);
        case 'months':
          return differenceInMonths(firstDate, secondDate);
        case 'years':
          return differenceInYears(firstDate, secondDate);
        default:
          console.warn(
            `[utils/format-date/getDifferenceBetweenDates] - INCORRECT MODE: ${mode}`
          );
          return 0;
      }
    } else {
      return 0;
    }
  } catch (e) {
    console.error(
      `[utils/format-date/getDifferenceBetweenDates] values:'${firstDate} and ${secondDate}' - ${e.toString()}`
    );
    return 0;
  }
}

/**
 * Expanded function
 */
export function getCurrentValueFromDate(
  mode: string,
  date: Date | null
): number {
  try {
    if (date) {
      switch (mode) {
        case 'day':
          return getDay(date);
        case 'month':
          return getMonth(date);
        case 'year':
          return getYear(date);
        default:
          console.warn(
            `[utils/format-date/getCurrentValueFromDate] - INCORRECT MODE: ${mode}`
          );
          return 0;
      }
    } else {
      return 0;
    }
  } catch (e) {
    console.error(
      `[utils/format-date/getCurrentValueFromDate] value:'${date}' - ${e.toString()}`
    );
    return 0;
  }
}

/**
 * Expanded function
 */
export function doMathWithDate(mode: string, date: Date, amount: number): Date {
  try {
    if (date) {
      switch (mode) {
        case 'addMonths':
          return addMonths(date, amount);
        case 'subMonths':
          return subMonths(date, amount);
        case 'subHours':
          return subHours(date, amount);
        default:
          console.warn(
            `[utils/format-date/doMathWithDate] - INCORRECT MODE: ${mode}`
          );
          return date;
      }
    } else {
      return date;
    }
  } catch (e) {
    console.error(
      `[utils/format-date/doMathWithDate] value:'${date}' - ${e.toString()}`
    );
    return date;
  }
}

/**
 * Expanded function
 */
export function setStartOfDate(mode: string, date: Date): Date {
  try {
    if (date) {
      switch (mode) {
        case 'day':
          return startOfDay(date);
        case 'month':
          return startOfMonth(date);
        case 'subHours':
          return startOfYear(date);
        default:
          console.warn(
            `[utils/format-date/setStartOfDate] - INCORRECT MODE: ${mode}`
          );
          return date;
      }
    } else {
      return date;
    }
  } catch (e) {
    console.error(
      `[utils/format-date/setStartOfDate] value:'${date}' - ${e.toString()}`
    );
    return date;
  }
}

export function isPastDate(date: DateWithoutNull): boolean {
  try {
    const parsedDate = isDate(date) ? date : parseISO(<string>date);
    return isPast(<Date>parsedDate);
  } catch (e) {
    console.error(
      `[utils/format-date/isPastDate] value:'${date}' - ${e.toString()}`
    );
    return false;
  }
}

export function isCorrectDate(date: any): boolean {
  return isDate(date) && isValid(date);
}

// PGS-1628
export function costylForTicketsVisitTime(date: DateString): string {
  try {
    const parsedDate = isDate(date) ? date : parseISO(<string>date);
    return format(sub(<Date>parsedDate, { hours: 3 }), DateFormat.datetime);
  } catch (e) {
    console.error(
      `[utils/format-date/costylForTickedTime] value:'${date}' - ${e.toString()}`
    );
    return '';
  }
}

export function toUtcTimestamp(date: Date) {
  return (date.getTime() + date.getTimezoneOffset() * 60 * 1000) / 1000;
}

export function toTimestamp(date: Date) {
  return date.getTime() / 1000;
}

export function getCurrentDate(): string {
  const date = new Date();
  const yyyy = date.getFullYear();
  const mm = String(date.getMonth() + 1).padStart(2, '0'); // January is 0!
  const dd = String(date.getDate()).padStart(2, '0');

  return yyyy + '/' + mm + '/' + dd;
}

export class DateTimeService {
  static fromTimestamp(timestamp: number, dateFormat = DateFormat.datetime) {
    return format(
      new Date(timestamp),
      parseFormatToUnicodeMarkers(DateFormat.datetime)
    );
  }
}
