import { TDateCell } from './types';

const getHumanDate = (d: Date) => {
  const year = d.getFullYear();
  const month = d.getMonth() + 1;
  const date = d.getDate();
  const day = d.getDay();
  const hour = d.getHours();
  const minute = d.getMinutes();
  const second = d.getSeconds();
  const ms = d.getMilliseconds();

  return {
    year, y: year, month, M: month, date, d: date, day,
    hour, H: hour, h: hour,
    minute, m: minute,
    second, s: second,
    ms,
  };
};

const getMonthDays = (year: number, month: number) => {
  const first = new Date(year, month - 1, 1);
  const last = new Date(year, month, 0);
  const dates: TDateCell[] = [];

  let day = first.getDay();
  while (day > 0) {
    day -= 1;
    dates.push({ date: new Date(year, month - 1, -day), current: -1 });
  }

  day = last.getDate() - first.getDate();
  for (let i = 0; i <= day; i++) {
    const date = new Date(first);
    date.setDate(i + 1);
    dates.push({ date, current: 0 });
  }

  day = last.getDay();
  let tail: Date = last;
  for (let i = day; i < 6; i++) {
    tail = new Date(year, month, i - day + 1);
    dates.push({ date: tail, current: 1 });
  }
  if(dates.length < 42) {
    day = tail.getDate();
    for (let i = 1; i <= 7; i++) {
      tail = new Date(year, month, day + i);
      dates.push({ date: tail, current: 1 });
    }
  }
  return dates;
};

export const getMonthWeeklyDays = (date: Date = new Date()): TDateCell[][] => {
  if (!(date instanceof Date)) {
    date = new Date();
  }
  const { year, month } = getHumanDate(date);
  const days = getMonthDays(year, month);
  const dayRows: TDateCell[][] = [];
  while (days.length > 0) {
    dayRows.push(days.splice(0, 7));
  }
  return dayRows;
};

export const WEEK_DAYS = ['日', '一', '二', '三', '四', '五', '六'];

export const invokeCallback = <T>(cb: T, ...args: unknown[]): void => {
  typeof cb === 'function' && cb(...args);
};

/**
 * a - b 的月数
 */
export const subDateMonth = (a: Date, b: Date): number => {
  const am = a.getFullYear() * 12 + a.getMonth();
  const bm = b.getFullYear() * 12 + b.getMonth();
  return am - bm;
};

const ONE_DAY = 1000 * 60 * 60 * 24;

/**
 * a - b 的天数
 * @param a
 * @param b
 * @returns
 */
export const subDateDay = (a: Date, b: Date): number => {
  const ad = new Date(a.getFullYear(), a.getMonth(), a.getDate()).getTime();
  const bd = new Date(b.getFullYear(), b.getMonth(), b.getDate()).getTime();
  return (ad - bd) / ONE_DAY;
};

/**
* 比较日期单位
* @param small 相对早的日期
* @param big 相对晚的日期
* @param mode 比较单位
* @param min 不能小于这个值
* @returns
*/
export const compareDate = (small: Date | undefined, big: Date | undefined, mode: 'year' | 'month', min: number): boolean => {
  if (!small || !big) {
    return true;
  }
  if (mode === 'year') {
    return big.getFullYear() - small.getFullYear() > min;
  } else {
    return subDateMonth(big, small) > min;
  }
};


const _parseInt = <T>(str: T, dftVal?: number) => {
  if(!str || typeof str !== 'string') {
    return dftVal;
  }
  const n = parseInt(str);
  if(isNaN(n)) {
    return dftVal;
  }
  return n;
};

export const parseTime = (str?: string): [number, number, number, number] => {
  const [h, m, s, ms] = str?.split(/[\:\.]+/) || [];
  return [_parseInt(h, 0) || 0, _parseInt(m, 0) || 0, _parseInt(s, 0) || 0, _parseInt(ms, 0) || 0];
};

type TDateCounterType = 'd' | 'm' | 'y';


export const dateCounter = (date: Date, type: TDateCounterType): number => {
  switch(type) {
  case 'y': return date.getFullYear();
  case 'm': return date.getFullYear() * 12 + date.getMonth();
  }
  return date.getTime() / ONE_DAY >> 0;
};

export const compareDateSort = (d1: Date, d2: Date, type: TDateCounterType = 'd'): number => {
  const t1 = dateCounter(d1, type), t2 = dateCounter(d2, type);
  return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
};

export const borderDateFactory = (factor: (d1: Date, d2: Date) => Date) => (...ds: Date[]): Date => {
  return ds.length < 2 ? ds[0] || new Date() : ds.reduce((r, v) => factor(r, v));
};
export const getMinDate = borderDateFactory((d1: Date, d2: Date) => compareDateSort(d1, d2) < 0 ? d1 : d2);
export const getMaxDate = borderDateFactory((d1: Date, d2: Date) => compareDateSort(d1, d2) < 0 ? d2 : d1);

/**
 * d 是否在 [left, right] 区间
 * @param date 日期
 * @param left 最小日期
 * @param right 最大日期
 * @returns boolean
 */
export const betweenDate = (date: Date, left: Date, right: Date): boolean => {
  if(left instanceof Date && compareDateSort(left, date, 'd') > 0) {
    return false;
  }
  if(right instanceof Date && compareDateSort(date, right, 'd') > 0) {
    return false;
  }
  return true;
};


export const parseDate = (str?: string): Date | undefined => {
  if(!str || typeof str !== 'string') {
    return;
  }

  const [dateStr = '', timeStr = ''] = str.split(/([ ]|T)+/);
  if(!dateStr) {
    return;
  }
  const [y, m, d] = dateStr.split(/[^\d]+/);
  const year = _parseInt(y), month = _parseInt(m), date = _parseInt(d) || 1;
  if(!year || !month) {
    return;
  }
  const time = parseTime(timeStr);
  return new Date(year, month - 1, date, ...time);
};