export interface RelativeDateOptions {
  to: number;
  from: number;
  output: RelativeDateOptionsOutput;
  i18n: RelativeDateOptionsI18n;
}

export interface RelativeDateOptionsOutput {
  AGO: string;
  IN: string;
}

export interface RelativeDateOptionsI18n {
  MINUTE: string[];
  HOUR: string[];
  DAY: string[];
  AGO: string;
  IN: string;
  NEAR: string;
}

export interface RelativeDateValue {
  value: string;
  diff: number;
}

export declare type RelativeDateFormatFunction = (
  value: string | number | Date,
) => string;

export const relativeDateDefaultFormatFunction: RelativeDateFormatFunction = (
  val: string | number | Date,
): string => new Date(val).toLocaleDateString();

export const relativeDate = (
  params: Partial<RelativeDateOptions> = {},
  format: RelativeDateFormatFunction = relativeDateDefaultFormatFunction,
): RelativeDateValue => {
  const options: RelativeDateOptions = {
    to: Date.now(),
    from: Date.now(),
    output: {
      AGO: 'OPERATOR VALUE TIMING',
      IN: 'OPERATOR VALUE TIMING',
    },
    i18n: {
      MINUTE: ['minute', 'minutes'],
      HOUR: ['heure', 'heures'],
      DAY: ['jour', 'jours'],
      AGO: 'Il y a',
      IN: 'Dans',
      NEAR: `moins d'une`,
    },
    ...params,
  };

  const fromInSeconds = options.from / 1000;
  const toInSeconds = options.to / 1000;
  const diff = Math.round(Math.abs(toInSeconds - fromInSeconds));
  const timeMomentOperator = toInSeconds - fromInSeconds > 0 ? 'IN' : 'AGO';
  let coef = 60;
  let unit = 'MINUTE';

  if (diff >= 604800) {
    return {
      diff,
      value: format(options.to),
    };
  }

  if (diff >= 3600 && diff < 86400) {
    unit = 'HOUR';
    coef = 3600;
  } else if (diff >= 86400 && diff < 604800) {
    unit = 'DAY';
    coef = 86400;
  }

  const value = Math.round(diff / coef);
  const mapObj = {
    OPERATOR: options.i18n[timeMomentOperator],
    VALUE: `${diff < 60 ? options.i18n.NEAR : value}`,
    TIMING:
      options.i18n[unit as keyof RelativeDateOptionsI18n][
        value === 1 || diff < 60 ? 0 : 1
      ],
  };
  const re = new RegExp(Object.keys(mapObj).join('|'), 'gi');

  return {
    diff,
    value: options.output[timeMomentOperator].replace(
      re,
      (matched: string) => mapObj[matched as keyof typeof mapObj],
    ),
  };
};

export const readableDate = (dateAt: string) => {
  try {
    return new Date(dateAt).getTime();
  } catch (e) {
    return null;
  }
};
